I was trying to solve a challenge where the "hidden data" were in ICMP echo payloads. I decided to do it in Go but there were some hiccups on the way. Here are my notes in case (most likely) future me or someone else needs to do the same.
Code is in my clone at:
gopacket is the official Go library for packet manipulation. It also supports reading and writing pcap files through
I started following this tutorial from dev dungeon (skipped the capturing part because I have a pcap file in hand). We need to
go get both
go get github.com/google/gopacket/pcap won't work on Windows. I searched around and found an answer on Stack Overflow. I got it to work with some modification.
go get pcap on Windows
- Install go_amd64 (add go binaries to your PATH). I assume you have a Go environment ready to go.
- Install MinGW x64 via Win-Builds like I have written about before.
- Install npcap.
- Download Winpcap developer's pack and extract it to
C:\. So you will have
C:\Windows\System32and copy them somewhere.
MinGW) on both files.
- Generate static library files:
dlltool --as-flags=--64 -m i386:x86-64 -k --output-lib libwpcap.a --input-def wpcap.def
dlltool --as-flags=--64 -m i386:x86-64 -k --output-lib libpacket.a --input-def packet.def
go get github.com/google/gopacket/pcap.
Following the tutorial I started making code snippets to do what I wanted. Most code is based on the tutorial.
Gopacket godoc and source are also your friends:
Opening a pcap File
This one shows how to open a pcap file and print the packets.
But I got the following error:
$ go run go-pcap-test1.go 2017/12/02 15:48:46 bad dump file format exit status 1
Seems like the original file was in
pcapng format which is not supported by
gopacket. Converting the file to
pcap worked. The new file is named
Reading everything in the pcap file is good but not what we want. We want to set a filter and only read certain packets. This can be done with
handle.SetBPFFilter(filter) in which
filter is a string containing a filter in BPF syntax. We just pass the filter
This code only reads packets of type
gopacket is based on layers. You can get each layer from raw packet data (either from the pcap file or just bytes). Layers are in
github.com/google/gopacket/layers. We are interested in IPv4 pings so I used
ipLayer := packet.Layer(layers.LayerTypeIPv4).
ipLayer is a
*layers.IPv4 (don't worry about it being a pointer) and we can print it with
fmt.Printf("%+v", ipLayer) to get:
Remember those are printed in decimal (bytes are just uint8 in go) and not hex. Personally I prefer printing in hex because it's easier for me to read ASCII-Hex.
At this point you would think we could just do
ipLayer.Payload and read it but we get:
ipLayer.Payload undefined (type gopacket.Layer has no field or method Payload)
But if we print the type with
%T we get
*layers.IPv4 and when we print it with
%+v we can see the
What we have is an interface and the compiler does not know it's going to be populated by
*layers.IPv4 at runtime. We need to cast the packet to
*layers.IPv4 manually. Then we can access
Which results in
For more info see section Pointers to Known Layers in gopacket docs.
So we mostly got everything, the payload is some headers and then base64 encoded data. We could just discard the first 8 (header) + 9 (
$$START$$) and grab what we want. But let's do things properly.
Creating an ICMP Message in Go
We can create an
icmp message from the IPv4 layer payload.
First we need
go get golang.org/x/net/icmp and then:
ProtocolIPv6ICMP are defined in
golang.org/x/net/internal/iana. It's an internal package and we cannot use it directly. Instead I have copied the constants directly in my code.
The result is *icmp.Message:
We are interested in
Body of type MessageBody which is again an interface. If we print the value and type we get:
Getting ICMP Payload
But again we need to cast it to
*icmp.Echo before we can get the
Data field which contains the payload.
Now we have the payload:
This is base64 encoded and we can decode it after removing
Ham shank rump, nulla non alcatra ut deserunt minim boudi
The rest is easy. Complete code for this section is in