パケットキャプチャツールのつくり方 〜IPヘッダー解析〜

個人的な考えですが、パケットについて知りたければtcpdumpやWireSharkの出力結果を眺めるよりもパケットキャプチャツールを自分で作った方が良いと思っています。C言語の知識が必要となりますが(Cでなくても良いのですが…)、高機能なものは全然必要なくて、最初はしょぼいもので大丈夫です。単純にパケットの中身を解析して表示させれば良いので、100行くらいで書けます。自分でパケットキャプチャツールを作ることができるようにれば、WireSharkの使い方も変わってくると思います。というか、嫌でもパケットの中身の知識がつくので、ビット単位でパケット構造を理解できるようになるはずです。

スポンサーリンク
スポンサーリンク

対象とするOS

  • Mac OS X、Linux、BSD

対象はUnix系OSですが、Windowsでも基本部分は変わらないと思います。なお、C言語の勉強が目的ではないので、C言語についての解説はしません。

パケット受信処理方法の違い

Mac OS XとBSDは/dev/bpfというデバイスファイルを読むことでパケットを受信することができます。対して、Linuxはsocket(2)を使います。パケットの受信は低レベルな処理なので、どうしてもOSによって差異が生じてしまいます。そこで、OSの差異を吸収する仕組みが考えられました。libpcapです。libpcapは元々tcpdmpのために作られたライブラリです。

libpcapを使う事によりOSに関係なく統一したコードを書くことができるようになります。今回はこのlibpcapを使ってLinuxとMac OS X、BSDで動く共通コードを書くことにします。

libpcapが導入されていない場合は別途インストールする必要があります。ぼくの手元のCentOS 7は未導入でしたので、以下のようにしてインストールをおこないました。

# yum -y install libpcap
# yum -y install libpcap-devel

パケットキャプチャの流れ

簡単に書くと、パケットキャプチャは以下の流れでおこないます。

  1. デバイスを開く
  2. ループでひたすらパケットを受信する
  3. 受信したパケットをIPヘッダーの構造体にマッピングする
  4. IPヘッダーのプロトコルタイプを調べて、TCPならTCPヘッダー、UDPならUDPヘッダーという感じで構造体にマッピングする
  5. 各ヘッダー値を画面に出力する

ポインタが解っていれば問題なく書けると思います。古い本ですが、ぼくはこの本で一発でポインタを理解しました。超オススメの良書です。

パケットを扱うときの注意点

パケットキャプチャのコードを書くにあたって、ひとつだけ注意する点があります。それは、バイトオーダーです。バイトーオーダーは「リトルエンディアン」と「ビッグエンディアン」があります。

バイトオーダーは、データをどのような順でメモリに格納するのか、というものです。たとえば0x12345678という4バイトのデータをメモリに格納する場合を想定すると以下のようになります。

ビッグエンディアン ⇒ 12 34 56 78
リトルエンディアン ⇒ 78 56 34 12

このように、ビッグエンディアンとリトルエンディアンはメモリへのデータ格納順が逆です。

また、ネットワークを流れるデータを「ネットワークバイトオーダー」と呼び、ビッグエンディアンと同じバイトオーダーとなっています。そして、ローカルのバイトオーダーを「ホストバイトオーダー」といいます。IPヘッダーやTCPヘッダーなど、2バイト以上のデータは必ずバイトオーダーの変換が必要なるので、注意してください。

このバイトオーダーの差異を吸収するのがhtons、htonl、htonll、ntohs、ntohl、ntohllというものです。ローカルホストのバイトオーダーをネットワークバイトオーダーへ変換するときはhtonXを使い、ネットワークバイトオーダーをローカルホストのバイトオーダーへ変換するときはntohXを使います。Xの部分は変換するデータのサイズに合わせます。2バイトのデータはshortなので「s」、4バイトのデータはlongなので「l」、8バイトのデータはlong longなので「ll」となります。この後に出てくるサンプルコードを見れば、どういうことか解ると思います。

パケットを受信する

デバイスを開く

pcapでデバイスを指定して開くには、以下のようにします。

今回はサンプルとして、 ICMPパケットのみ受信するようにフィルターの設定をします。ここで設定しているフィルターの文字列は、普段tcpdumpを使用するときに指定するフィルターの文字列と同じです。

ループでひたすらパケットを受信する

libpcapでパケットを受信する方法はいくつかあるのですが、今回は単純なpcap_next()を使用します。

受信したパケットをIPヘッダーの構造体にマッピングする

受信したパケットからIPヘッダーの値へアクセスするためにIPヘッダーの構造体にマッピングします。

各ヘッダー値を画面に出力する

IPヘッダーの構造体にマッピングできたら、あとは値を取得して画面へ表示するだけです。先述したとおり、バイトオーダーに注意して、値のサイズに合わせてntohXを使います。IPヘッダーの場合は基本的に2バイトの値がメインなので、ntohsを使うことになると思います。

サンプルコードのコンパイルと実行

それでは、ここまで書いたものをひとつにまとめます。

上記のファイルをpacket-cap.cという名前で保存しました。コンパイルするには以下のようにします。libpcapとリンクするために”-l pcap”が必要であることに注意してください。

$ cc packet-cap.c -lpcap

コンパイルは一般ユーザーで構いませんが、実行はルート権限でおこないます。手元のMacでは以下のようにして実行しました。

コンパイルしたプログラムを実行後、ICMP通信を受信するとIPヘッダーの値を表示します。

まとめ

今回はIPヘッダーに留めましたが、上位プロトコルの解析をおこなう場合も、やっていることは今回と同じです。

パケット解析のコツは、/usr/include/netinet以下にあるip.hやtcp.hなどを読むことです。これが読めれば、構造体にマッピングして値を表示するだけです。

ネットワークプログラミングに興味があれば、バイブル本を読むことをオススメします。著者、翻訳者共に世界最高レベルのハッカーが書いた名著です。

スポンサーリンク
スポンサーリンク
スポンサーリンク

フォローする

コメント

  1. […] 前回はパケットキャプチャでIPヘッダーの表示を行いました。今回は、UDPヘッダーの解析と表示をおこないます。UDPヘッダーは単純な構造のため、UDPヘッダーの解析ができればTCPやICMPで […]