MacBook ProのAirMac Extremeをモニターモードで動作させたい
WireSharkはsudoつけて起動するとモニターモードでキャプチャできるのだけど (*1)、WireShark以外に自作ツールでモニターモードにしたいとか、そういうこともあるよねー。で、どうやったらモニターモードにできるか調べてみた。
結論から言ってしまえば、低レベルには/dev/bpf* をopenしてioctlで制御できるのだけど、もう少し高レベルなAPIとしてlibpcapの関数を利用できる。
以下のようなコードで、libpcapでen0をモニターモードにできる。肝はpcap_set_rfmonでこれによりpcap_activate呼び出し時にモニターモードで動作する。(なお、ビルドするときはgcc -lpcap -o target src.c ってやって動的リンクしてやらないとリンク時にエラーになるから注意)
#include <pcap/pcap.h> #include <stdio.h> int main(int argc, char **argv) { char errbuf[256]; pcap_t *p = pcap_create("en0", errbuf); if (p == NULL) { fprintf(stderr, errbuf); return 1; } // activate時にモニターモードで動作させる if (pcap_set_rfmon(p, 1)) return 1; // activate時にプロミスキャスモードを有効化する if (pcap_set_promisc(p, 1)) return 1; // パケットをキャプチャするときのバッファーの大きさ (?) if (pcap_set_snaplen(p, 2048)) return 1; // タイムアウト (ms) if (pcap_set_timeout(p, 1000)) return 1; // activateする。 if (pcap_activate(p)) return 1; return 0; }
また、適当にコールバックを書いてやることで、パケットをキャプチャしつつキャプチャした時点のタイムスタンプ (というよりは経過した時間) や、生の802.11フレームを出力するのも簡単にできる。
パケットキャプチャを開始するには、pcap_loop関数を使い、これに以下のようにコールバックを渡すことで上記のことを実現できる。
#include <pcap/pcap.h> #include <stdio.h> #include <stdlib.h> struct radiotap_header { unsigned char version; unsigned char padding; unsigned short len; unsigned int present; } __attribute__((__packed__)); struct ieee80211_header { unsigned short frame_control; unsigned short duration_id; unsigned char addr1[6]; unsigned char addr2[6]; unsigned char addr3[6]; unsigned short seq_ctrl; unsigned char addr4[6]; } __attribute__((__packed__)); void callback(u_char *ignore, struct pcap_pkthdr *header, const u_char *raw) { struct radiotap_header *rt; struct ieee80211_header *dot11; unsigned int offset; unsigned char *payload; rt = (struct radiotap_header*) raw; dot11 = (ieee80211_header*) (raw + rt.len); offset = rt.len + sizeof(ieee80211_header); if (header->len == header->caplen && header->caplen > offset + 4) { payload = malloc(header->len - offset - 4); memcpy(payload, raw + offset, header->len - offset - 4); // snip // ここにやりたいことをかく。たとえば、addr1〜3とペイロードを表示など。 free(payload); } else { // snip // この分岐に至るのはieee802.11 のヘッダーの後ろにペイロードがないケース } } int main(int argc, char **argv) { // snip // 10回キャプチャする pcap_loop(p, 10, callback, NULL); return 0; }
ioctlで頑張るよりもこのようにlibpcapを使った方が簡単にモニターモードでスニフするコードを書けるのだが、ぼくらのような高レベルな生き物は、より高レベルな言語で書きたくなる訳で、Cythonを使ってPythonから呼び出せるようなPythonモジュールを書いている。飽きて放擲するかもしれないけど、飽きるまではたまに機能を追加したり修正したりするよ。
https://github.com/0gawa/pylibpcap3


















