プログラミング

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

このページはこんな方におすすめです
  • 自分でパケットキャプチャツールを作ってみたい
  • パケットの構造に興味がある
  • ネットワークの知識を深めたい

「パケットキャプチャツールをつくる」と聞くと難しそうな印象を持ってしまうかもしれませんが、そんな事はありません。

高機能なものは全然必要なくて最初は単純なもので大丈夫です。

受信したパケットの中身を解析して表示させれば良いので、100行くらいで書けます。

自分でパケットキャプチャツールを作ることができるようになればWireSharkの使い方も変わってくると思います。

パケットについて知識が深まる事でビット単位でパケット構造を理解できるようになり、トラブルシューティングでも役に立ちます。

対象とするOS

  • Mac OS X
  • Linux
  • BSD

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

パケットキャプチャツールのつくり方

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

macOSと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. 各ヘッダー値を画面に出力する

ポインタが解っていれば問題なく書けると思います。古い本ですが、ぼくは「Cプログラミング専門課程」を読んで一発でポインタを理解しました。超オススメの良書です。

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

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

バイトオーダーは、データをどのような順でメモリに格納するのか、というものです。たとえば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を使うことになると思います。

サンプルコードと実行例

サンプルコード

サンプルコードはGitHubからも落とせます。

≫サンプルコード

サンプルコードはGitHubからダウンロードできます。

https://raw.githubusercontent.com/shj/netwiz.jp/master/sample/packet_capture_icmp.c

コンパイルするには以下のようにします。libpcapとリンクするために”-l pcap”が必要であることに注意してください。

$ cc packet_capture_icmp.c -lpcap

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

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

まとめ

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

ネットワークプログラミングに興味があれば「UNIXネットワークプログラミング〈Vol.1〉ネットワークAPI・ソケットとXTI」を読むことをオススメします。著者、翻訳者共に世界最高レベルのハッカーが書いた名著です。

COMMENT

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)