-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpacket.tex
251 lines (202 loc) · 9.26 KB
/
packet.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
% #+TITLE: Go言語でPacket解析
% #+AUTHOR: @emaxser
% #+EMAIL: emaxser@bonprosoft.com
\chapter{Go言語でPacket解析}
\section{はじめに}
はじめまして、コンピュータクラブWeb班班長の @emaxser です。
ここ5年、6年くらいパケット眺めてたら研究室でもパケットを眺めることになりました。
ということで、パケットを眺める人を増やすためになぜかGoogleのGithubリポジトリに存在するパケット解析ライブラリであるgopacketを用いてパケットを触る方法を書いていきたいと思います。
gopacket はGo言語のライブラリですのでGo言語のインストールを行って下さい。そして、以下のコマンドを実行して下さい。
% #+BEGIN_SRC text
\begin{lstlisting}
$ go get github.com/google/gopacket
\end{lstlisting}
% #+END_SRC
これで準備は完了です。では楽しくパケットを触っていきましょう。
\section{簡単なパケットキャプチャ}
とりあえずキャプチャしたパケットの情報がとりあえず見ることが出来るパケットキャプチャを作成してみましょう。
まずソースコードを見たほうが早いと思うので簡単に見てみましょう。
% #+BEGIN_SRC go
\begin{lstlisting}
package main
import (
"flag"
"fmt"
"github.com/google/gopacket/dumpcommand"
"github.com/google/gopacket/pcap"
)
var iface = flag.String("i", "eth0", "Interface to read packets from")
func main() {
flag.Parse()
inactive, err := pcap.NewInactiveHandle(*iface)
if err != nil {
fmt.Println(err)
return
}
defer inactive.CleanUp()
err = inactive.SetPromisc(true)
if err != nil {
fmt.Println(err)
return
}
handle, err := inactive.Activate()
if err != nil {
fmt.Println(err)
return
}
defer handle.Close()
dumpcommand.Run(handle)
}
\end{lstlisting}
% #+END_SRC
まず、コマンドライン引数のフラグをパースして初期設定を読み込みます。このプログラムではインターフェース名をコマンドライン引数として渡すのでその解析を行ってくれます。
そしてそこから最後の行までインターフェースからパケットキャプチャする前準備のおまじないです。設定やらを行っています。今回はプロミスキャス・モードという自分宛以外のパケットもキャプチャ出来るようにだけ設定を行っています。
そして、dumpcommandのRunという関数にパケットキャプチャするhundleを渡してパケットの情報を取り出して貰っています。
dumpcommand.Run関数はこのパケットソースから情報を表示してくれる関数になっています。dumpcommand.Runという関数は引数にPacketDataSourceというインターフェースを満たした型を要求します。PacketDataSourceというインターフェースにはReadPacketData()という関数が含まれている事が条件となっています。
ReadPacketData()は多くの場合、次のパケットを返す関数です。
今回、この次のパケットというのがインターフェースを流れてきた次のパケットという意味なので、これでパケットキャプチャを行えているということになります。
\section{解析情報の抽出}
では、上記のパケットキャプチャから自分が欲しい情報だけを取り出して見ましょう。
まずはソースコードです。
% #+BEGIN_SRC go
\begin{lstlisting}
package main
import (
"flag"
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
)
var iface = flag.String("i", "eth0", "Interface to read packets from")
func capRun(src gopacket.PacketDataSource) {
var eth layers.Ethernet
var ip4 layers.IPv4
var ip6 layers.IPv6
var tcp layers.TCP
var udp layers.UDP
var payload gopacket.Payload
parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip4, &ip6, &tcp, &udp, &payload)
decodedLayers := make([]gopacket.LayerType, 0, 10)
for {
data, _, err := src.ReadPacketData()
if err != nil {
fmt.Println("Error reading packet data: ", err)
continue
}
fmt.Println("Decoding packet")
err = parser.DecodeLayers(data, &decodedLayers)
for _, typ := range decodedLayers {
fmt.Println(" layer type", typ)
switch typ {
case layers.LayerTypeEthernet:
fmt.Println(" Eth ", eth.SrcMAC, eth.DstMAC)
case layers.LayerTypeIPv4:
fmt.Println(" IP4 ", ip4.SrcIP, ip4.DstIP)
case layers.LayerTypeIPv6:
fmt.Println(" IP6 ", ip6.SrcIP, ip6.DstIP)
case layers.LayerTypeTCP:
fmt.Println(" TCP ", tcp.SrcPort, tcp.DstPort)
case layers.LayerTypeUDP:
fmt.Println(" UDP ", udp.SrcPort, udp.DstPort)
}
}
}
}
func main() {
flag.Parse()
inactive, err := pcap.NewInactiveHandle(*iface)
if err != nil {
fmt.Println(err)
return
}
defer inactive.CleanUp()
err = inactive.SetPromisc(true)
if err != nil {
fmt.Println(err)
return
}
handle, err := inactive.Activate()
if err != nil {
fmt.Println(err)
return
}
defer handle.Close()
capRun(handle)
}
\end{lstlisting}
% #+END_SRC
このソースコードは最初のソースコードにcapRunという関数を追加し、dumpcommand.RunではなくcapRunを呼び出すように変更しました。
capRunではPacketDataSourceからReadPacketDataメソッドを用いて順次パケットを処理して行っています。
capRunの中ではまず、それぞれのレイヤーのパーサを作成します。ここで指定されているレイヤーに ReadPacketData から得られるパケットのバイナリデータをパーサがフィットしてくれます。
あとは見たままです。それぞれのレイヤーが存在しているか確認し、存在していればその情報を表示するといったものになっています。
ここまで見たからにはもう、後は欲しいプロトコルのレイヤを追加したり、必要ない情報を表示させないといったことは簡単ですね。
さぁ、どんどん自分の好きなようにパケット解析しましょう。下記のドキュメントを読むと、 gopacket が多くのプロトコルに対応している事が分かると思います。自分の好きなプロトコルの情報をぜひキャプチャして確認してみて下さい。
https://godoc.org/github.com/google/gopacket/layers
* ファイルからのパケット解析
パケット解析はインターフェースから情報を得るだけでなく、保存されたpcapファイルなどから情報を得る事もあります。最後にこの方法を解説します。
まぁ、解析と行っても先ほどのソースコードとほとんど異なる所がないようなソースコードになっています。
% #+BEGIN_SRC go
\begin{lstlisting}
package main
import (
"flag"
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/examples/util"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
)
var filename = flag.String("f", "", "input file")
func capRun(src gopacket.PacketDataSource) {
var eth layers.Ethernet
var ip4 layers.IPv4
var ip6 layers.IPv6
var tcp layers.TCP
var udp layers.UDP
var payload gopacket.Payload
parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip4, &ip6, &tcp, &udp, &payload)
decodedLayers := make([]gopacket.LayerType, 0, 10)
for {
data, _, err := src.ReadPacketData()
if err != nil {
fmt.Println("Error reading packet data: ", err)
return
}
fmt.Println("Decoding packet")
err = parser.DecodeLayers(data, &decodedLayers)
for _, typ := range decodedLayers {
fmt.Println(" layer type", typ)
switch typ {
case layers.LayerTypeEthernet:
fmt.Println(" Eth ", eth.SrcMAC, eth.DstMAC)
case layers.LayerTypeIPv4:
fmt.Println(" IP4 ", ip4.SrcIP, ip4.DstIP)
case layers.LayerTypeIPv6:
fmt.Println(" IP6 ", ip6.SrcIP, ip6.DstIP)
case layers.LayerTypeTCP:
fmt.Println(" TCP ", tcp.SrcPort, tcp.DstPort)
case layers.LayerTypeUDP:
fmt.Println(" UDP ", udp.SrcPort, udp.DstPort)
}
}
}
}
func main() {
defer util.Run()()
pcapfile, err := pcap.OpenOffline(*filename)
if err != nil {
fmt.Println(err)
return
}
defer pcapfile.Close()
capRun(pcapfile)
//dumpcommand.Run(handle)
}
\end{lstlisting}
% #+END_SRC
先ほどとの違いは、main関数とcapRun関数のなかでエラー時に continue ではなく return を行うように変更になっているだけです。main関数の中でインターフェースから読み込むのではなく、 pcap.OpenOffline関数を用いてファイルから読み込んでいます。
また、 pcap.OpenOffline は pcap.Handle といった型を返すのですが、この型は ReadPacketData メソッドが実装されており、 PacketDataSource インターフェースを満たすので、 capRun に渡すただけで、キャプチャとして動作します。
\section{さいごに}
Go言語では gopacket というライブラリを利用するだけで、簡単にパケット解析を行うことが出来ます。
パケット解析は誰でも簡単に楽しく行える趣味なので、この記事を読んでみて簡単かな?と思った人は楽しくパケット解析をしてみましょう。