弁財天

ゴフマン「専門家を信じるのではなく、自分自身で考えて判断せよ」

Cygwinにiplogを移植

Win10の環境が手に入ったので、Cygwinに iplog-2.2.3を 移植してみてWin10の環境の理解を深めてみたw

つかWindowsサーバーにもiplogを導入し、PCがどこにアクセスし、誰がアクセスしてくるのかをモニターする。そして民主主義を目指すというのが本当の目的ww

Windows版のlibpcapが必要。WinPcapに互換性のあるlibpcap.aがあるのでこれを使ってみる。

WinPcapのディレクトリを見るとx64ディレクトリ下に libwpcap.a がない。 Cygwin64のバグでWindowsのDLLを呼び出しすロードモジュールを正しく作成できないみたいだ。
Problems using a DLL with Cygwin 64 bit

$ ./iplog
      6 [main] iplog (7132) C:\cygwin64\usr\local\sbin\iplog.exe: *** fatal error - cygheap base mismatch detected - 0x180324400/0xF54400.
This problem is probably due to using incompatible versions of the cygwin DLL.
Search for cygwin1.dll using the Windows Start->Find/Search facility
and delete all but the most recent version.  The most recent version *should*
reside in x:\cygwin\bin, where 'x' is the drive on which you have
installed the cygwin distribution.  Rebooting is also suggested if you
are unable to find another cygwin DLL.
$
こんなエラーになる。

C/C++言語でのプログラミングについてのHOWTO/FAQ集

この"--kill-at"がない場合,DLLを使用するプログラムとインポートライブラリの静的リンクまでは行えるが,DLLとの動的リンクの際に失敗する(プログラムが起動中に異常終了し,エラーメッセージが出ないこともあるので注意が必要).
これと似たことが起きた。 エラーなしでロードモジュールの作成はできるのだが、 実行すると何もエラーメッセージなしでプログラムが終了してしまう現象。

32ビットのCygwinなら何も問題なく32ビットのDLLとリンクしてロードモジュールを実行できる。 ふむ。WpdPack/Lib/x64/wpcap.libにlibwpcap.aが付属してなかったのはCygwin64環境ではDLL問題があって実行できないからだ。

なのでCygwin64環境は捨てw。
Cygwin32に切り替えるときは いろいろトラブルので注意。 Cygwinのサービスの残骸が残って消せないときは

C:\>sc delete apache 
C:\>sc delete sshd 
C:\>sc delete cygserver 
[SC] DeleteService SUCCESS
みたいにscコマンドで削除してしまう。

パッケージを消すつもりでトップのディレクトリCygwin64を_Cygwin64とリネームしても、リネームしたディレクトリ下のプログラムを拾ってくる仕様ってどうなのよ?なので完全な消去が必要w

teratermのcyglaunch.exeが動かない。
Cygwinを64→32に切り替えてcyglaunchから起動できないと悩んでたら、 C:\Program Files (x86)\teraterm\cygterm+-i686\cygterm.exe をひとつ上のディレクトリにコピーする仕様みたいだw なんだかなー。

32ビットのlibpcap.aの作成はdlltoolと.defファイルで作成する。

dlltool --kill-at --def wpcap.def --dllname wpcap.dll --output-lib libpcap.a
wpcap.defはnmで作成してもいいし、ソースの中のWpcap_no_extensions.defを使ってもいい。

もっとすげーやり方。
Re: Net::Pcap installation help (and not with the library)
MinGWのpexports

--WinPcap

  1. Make sure WinPcap is installed - if not, install it
  2. Unzip WinPcap Developers Pack http://www.winpcap.org/devel.htm in same parent directory Net::Pcap was unzipped in, e.g.,:
      tmp\Net-Pcap-0.16\..
      tmp\WpdPack\..
    
  3. IF on x64 platform, check on the existence of the x64 libraries:
      dir WpdPack\lib\x64\*.a
    
  4. They may not exist. If not, built them:
      pexports \Windows\system32\wpcap.dll > wpcap.def
      dlltool --as-flags=--64 -m i386:x86-64 -k --output-lib libwpcap.a --input-def wpcap.def
    
      pexports \Windows\system32\Packet.dll > Packet.def
      dlltool --as-flags=--64 -m i386:x86-64 -k --output-lib libpacket.a --input-def Packet.def
    
      move libwpcap.a WpdPack\lib\x64
      move libpacket.a WpdPack\lib\x64
    
すっげーw。

#!/usr/bin/sh

echo "LIBRARY wpcap.dll" >wpcap_lib.def
echo "EXPORTS" >> wpcap_lib.def
nm /WpdPack/Lib/wpcap.lib| sed -n -e"s/.* T _//p"|grep -v "\.text">>wpcap_lib.def
dlltool --kill-at --def wpcap_lib.def --dllname wpcap.dll --output-lib libpcap.a
cp libpcap.a /WpdPack/Lib/.
まぁこんなファイルをねつ造すればいいのだな。
wpcap.def
LIBRARY wpcap.dll
DESCRIPTION 'Win32 Packet Capture Library'
EXPORTS
bpf_dump
bpf_filter
bpf_image
bpf_validate
endservent
eproto_db DATA
getservent
install_bpf_program
pcap_activate
pcap_breakloop
pcap_close
pcap_compile
pcap_compile_nopcap
pcap_create
pcap_createsrcstr
pcap_datalink
pcap_datalink_name_to_val
pcap_datalink_val_to_description
pcap_datalink_val_to_name
pcap_dispatch
pcap_dump
pcap_dump_close
pcap_dump_file
pcap_dump_flush
pcap_dump_ftell
pcap_dump_open
pcap_file
pcap_fileno
pcap_findalldevs
pcap_findalldevs_ex
pcap_free_datalinks
pcap_freealldevs
pcap_freecode
pcap_get_airpcap_handle
pcap_geterr
pcap_getevent
pcap_getnonblock
pcap_hopen_offline
pcap_is_swapped
pcap_lib_version
pcap_list_datalinks
pcap_live_dump
pcap_live_dump_ended
pcap_lookupdev
pcap_lookupnet
pcap_loop
pcap_major_version
pcap_minor_version
pcap_next
pcap_next_etherent
pcap_next_ex
pcap_offline_filter
pcap_offline_read
pcap_open
pcap_open_dead
pcap_open_live
pcap_open_offline
pcap_parsesrcstr
pcap_perror
pcap_read
pcap_remoteact_accept
pcap_remoteact_cleanup
pcap_remoteact_close
pcap_remoteact_list
pcap_sendpacket
pcap_sendqueue_alloc
pcap_sendqueue_destroy
pcap_sendqueue_queue
pcap_sendqueue_transmit
pcap_set_buffer_size
pcap_set_datalink
pcap_set_promisc
pcap_set_snaplen
pcap_set_timeout
pcap_setbuff
pcap_setdirection
pcap_setfilter
pcap_setmintocopy
pcap_setmode
pcap_setnonblock
pcap_setsampling
pcap_setuserbuffer
pcap_snapshot
pcap_stats
pcap_stats_ex
pcap_strerror
wsockinit

Windows 7 network adapter device name using winpcap
Linux環境だとeth0とかens0とかになるネットワークインターフェイスのデバイス名はWin環境では getmac /fo csv /v で調べることができるとか。

C:\Users\hoge>getmac /fo csv /v
"接続名","アダプター","物理アドレス","トランスポート名"
"Wi-Fi","Realtek RTL8723BE Wireless LAN 802.11n PCI-E NIC","xx-xx-xx-xx-xx-xx","メディアが切断されています"
"eth0","Realtek PCIe GBE Family Controller","80-FA-xx-xx-xx-xx","\Device\Tcpip_{00DE027D-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"

C:\Users\hoge>
eth0とか名前を変えちゃえばいいのかとか思ってたけど違うw
\Device\Tcpip_{00DE027D-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
みたいなのがそれらしい。br />

Obtaining the device list
WinPcapにデバイスリストを取得するサンプルがある。

gcc -I/wpdpack/include -L/wpdpack/lib -lpcap pcap_filter.c -o pf
だとエラー。
pcap_filter.c:(.text+0x15c): `pcap_open_live' に対する定義されていない参照です
gcc -I/wpdpack/include -L/wpdpack -L/wpdpack/lib pcap_filter.c -o pf -lpcap
これだとpf.exeが作製される。-lpcap引数の場所に意味があるみたいだけど、よくわからんな。
$ gcc -I /WpdPack/Include/ -L /WpdPack/Lib devlist.c -o devlist -lpcap
$ ./devlist.exe 1. rpcap://\Device\NPF_{00DE027D-XXXX-XXXX-XXXX-XXXXXXXXXXXX} (Network adapter 'Realtek PCIe GBE Family Controller' on … $
どうもpcap_open_live()に渡すデバイス名は
\Device\Tcpip_{00DE027D-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
ではなく
rpcap://\Device\NPF_{00DE027D-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
みたいだ。

configureはこんな。

./configure --host=linux --with-libpcap=/wpdpack --with-libpcap-includes=/wpdpack/include --with-libpcap-libs=/wpdpack/lib

iplog.cをコンパイルするのにincludeファイルが足りない。 なのでLinuxからテケトーにパクってくる。

/usr/include/netinet/if_ether.h
/usr/include/linux/if_ether.h
/usr/include/linux/types.h
/usr/include/linux/posix_types.h
/usr/include/linux/stddef.h
/usr/include/asm/posix_types.h
/usr/include/asm/posix_types_64.h
/usr/include/asm/posix_types_32.h
/usr/include/asm-generic/posix_types.h
/usr/include/asm-generic/bitsperlong.h
/usr/include/asm/bitsperlong.h
/usr/include/net/ethernet.h

src/iplog_pcap.c で interfacesの名前の文字列を比較して openするのだけど 文字列が一致しないから動かない。 なので/etc/iplog.confには

# Listen on eth0 and eth1
#interface eth0,eth1
interface {00DE027D-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
な定義をしてだな。 pcap_open_live()の直前でデバイス名の先頭に"rpcap://\\Device\\NPF_"を付けるように src/iplog_pcap.cにパッチw
static int open_pcap_device(struct pcap_data *pdata) {
        u_char fstring[1024], *temp, errbuf[PCAP_ERRBUF_SIZE];
        struct bpf_program filt;
        u_int i = 0;

        u_char devname[60];
        memset(devname, 0, sizeof(devname));
        strcpy(devname, "rpcap://\\Device\\NPF_");
        strcat(devname, pdata->name);
        pdata->pd =
                //pcap_open_live(pdata->name, SNAPLEN, opt_enabled(PROMISC), 0, errbuf);
                pcap_open_live(devname, SNAPLEN, opt_enabled(PROMISC), 0, errbuf);

linux/if.h:#define	IFNAMSIZ	16
16文字はeth0とかなら足りるが
rpcap://\Device\NPF_{00DE027D-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
123456789012345678901234567890123456789012345678901234567890
56文字を格納するには足りない。
iplog_pcap.h
struct pcap_data {
    struct pcap_data *next;
    struct pcap_data *prev;
    int dl;
    pcap_t *pd;
    ipaddr_t *addr;
    size_t num_addr;
    //u_char name[IFNAMSIZ];
    u_char name[60];
};

Signal 11(SIGSEGV)で落ちるときは -gオプションをつけてコンパイル、iplog実行時に-o オプションを付けてマルチスレッドを禁止してデバッグ。 gdb src/iplogで起動。 run -oで実行、落ちたら bt コマンドで落ちた個所を特定する。

(gdb) run -o
Starting program: /home/hoge/src/iplog-2.2.3/src/iplog -o
[New Thread 628.0x1d44]
[New Thread 628.0x534]
[New Thread 628.0x17e8]
[New Thread 628.0x928]
[New Thread 628.0x20]
[New Thread 628.0x1a18]
[New Thread 628.0x1b78]
[New Thread 628.0x1b18]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 628.0x1b18]
0x0040a7d8 in read_packets (data=0x200442d0) at iplog.c:328
328                     FD_SET(fd, &rfds);
(gdb) bt
#0  0x0040a7d8 in read_packets (data=0x200442d0) at iplog.c:328
#1  0x61112ead in pthread::thread_init_wrapper(void*) ()
   from /usr/bin/cygwin1.dll
#2  0x61094c91 in pthread_wrapper@4 () from /usr/bin/cygwin1.dll
#3  0x00000000 in ?? ()
(gdb)
iplog.cの328行目でSIGSEGVで落ちる。
/*
#ifdef THREADED_LIBPCAP_IS_BROKEN
                FD_SET(fd, &rfds);
                select(fd + 1, &rfds, NULL, NULL, &tv);
#endif
*/
へんなコードをコメントアウト。

$ uname -a
CYGWIN_NT-10.0-WOW HOGE 2.4.1(0.293/5/3) 2016-01-24 11:24 i686 Cygwin
$
な環境。 これでiplogのCygwin32ビット環境への移植完了。実行可能になった。

パッチiplog-2.2.3-cygwin32.diff

win10pcap.orgでpcap_open_live()に渡すデバイス名の頭に"rpcap://\\Device\\NPF_"を付けるとエラーになるぞw バイナリ互換とは言えないだろ。

Apr  6 09:08:34 IP packet with invalid checksum to hoge1 (192.168.x.x) from hoge2 (192.168.x.y)
うーむ。TCPヘッダーの解読ができない。

tcpdumpのサンプル www.tcpdump.org/sniffex.c を同じlibpcap.a/wpcap.dllとコンパイル・リンクしたものはUDP/TCPの識別ができる。 src/iplog_input.c の識別ロジックがダサいのか。

www.tcpdump.org/sniffex.c
sniffexでUDPとICMPが引っかからないと思ったらコードのswitch文でreturnしてるし。orz.

code_snippet/ether_bridge/headers.h

/* UDP header */
struct sniff_udp {
    unsigned short int uh_sport;
    unsigned short int uh_dport;
    unsigned short int uh_len;
    unsigned short int uh_check;
}; /* total udp header length: 8 bytes (=64 bits) */
いいものみっけ。

code_snippet/ether_bridge/headers.c

    const struct sniff_udp *udp;            /* The TCP header */
…
    /* define/compute udp header offset */
    udp = (struct sniff_udp*)(packet + SIZE_ETHERNET + size_ip);

$ diff sniffex.c o/sniffex.c
270,277d269
< /* UDP header */
< struct sniff_udp {
<     unsigned short int uh_sport;
<     unsigned short int uh_dport;
<     unsigned short int uh_len;
<     unsigned short int uh_check;
< }; /* total udp header length: 8 bytes (=64 bits) */
<
435,436d426
<       const struct sniff_udp *udp;
<
442,444d431
<       char proto[10];
<       char src[20];
<       char dst[20];
446c433
<       /* printf("\nPacket number %05d:\n", count); */
---
>       printf("\nPacket number %d:\n", count);
460c447
<       /* print source and destination IP addresses
---
>       /* print source and destination IP addresses */
463d449
<       */
468,469c454
<                       //printf("   Protocol: TCP\n");
<                       strcpy(proto,"TCP");
---
>                       printf("   Protocol: TCP\n");
472,477c457
<                       //printf("   Protocol: UDP\n");
<                       strcpy(proto,"UDP");
<       udp = (struct sniff_udp*)(packet + SIZE_ETHERNET + size_ip);
<       sprintf(src, "%s:%d", inet_ntoa(ip->ip_src), ntohs(udp->uh_sport));
<       sprintf(dst, "%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->uh_dport));
<       printf("%5d %20s -> %-20s %s\n", count, src, dst, proto);
---
>                       printf("   Protocol: UDP\n");
480,485c460
<                       //printf("   Protocol: ICMP\n");
<                       strcpy(proto,"ICMP");
<       udp = (struct sniff_udp*)(packet + SIZE_ETHERNET + size_ip);
<       sprintf(src, "%s:%d", inet_ntoa(ip->ip_src), -1);
<       sprintf(dst, "%s:%d", inet_ntoa(ip->ip_dst), -1);
<       printf("%5d %20s -> %-20s %s\n", count, src, dst, proto);
---
>                       printf("   Protocol: ICMP\n");
488,492c463
<                       //printf("   Protocol: IP\n");
<                       strcpy(proto,"IP");
<       sprintf(src, "%s:%d", inet_ntoa(ip->ip_src), -1);
<       sprintf(dst, "%s:%d", inet_ntoa(ip->ip_dst), -1);
<       printf("%5d %20s -> %-20s %s\n", count, src, dst, proto);
---
>                       printf("   Protocol: IP\n");
495,499c466
<                       //printf("   Protocol: unknown\n");
<                       sprintf(proto,"%d", ip->ip_p);
<       sprintf(src, "%s:%d", inet_ntoa(ip->ip_src), -1);
<       sprintf(dst, "%s:%d", inet_ntoa(ip->ip_dst), -1);
<       printf("%5d %20s -> %-20s %s\n", count, src, dst, proto);
---
>                       printf("   Protocol: unknown\n");
515,519d481
<       //1001 192.168.24.38:22 -> 192.168.24.38:53793 TCP
<       sprintf(src, "%s:%d", inet_ntoa(ip->ip_src), ntohs(tcp->th_sport));
<       sprintf(dst, "%s:%d", inet_ntoa(ip->ip_dst), ntohs(tcp->th_dport));
<       printf("%5d %20s -> %-20s %s\n", count, src, dst, proto);
<       /*
522d483
<       */
532a494
>        */
537d498
<        */
553c514
<       int num_packets = 10000;                        /* number of packets to capture */
---
>       int num_packets = 10;                   /* number of packets to capture */
$

$ cat cc.sh
#!/bin/sh

for s in sniffex sniffex_ip
do
cc -g -O -I ../WpdPack/Include -c -o ${s}.o ${s}.c
cc -g -O -I ../WpdPack/Include -o ${s}.exe ${s}.o -L ../WpdPack/Lib/x64 -lwpcap
done
$
$ ./sniffex "\\Device\\NPF_{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
sniffex - Sniffer example using libpcap
Copyright (c) 2005 The Tcpdump Group
THERE IS ABSOLUTELY NO WARRANTY FOR THIS PROGRAM.

Device: \Device\NPF_{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
Number of packets: 10
Filter expression: ip
    1     192.168.99.38:22 -> 192.168.99.93:52040  TCP
    2  192.168.99.93:52040 -> 192.168.99.38:22     TCP
    3 153.156.202.233:4500 -> 192.168.99.38:4500   UDP
    4   192.168.99.38:4500 -> 153.156.202.233:4500 UDP
    5   192.168.99.38:4500 -> 153.156.202.233:4500 UDP
    6     192.168.99.38:22 -> 192.168.99.93:52040  TCP
    7     192.168.99.38:22 -> 192.168.99.93:52040  TCP
    8  192.168.99.93:52040 -> 192.168.99.38:22     TCP
    9   192.168.99.38:4500 -> 153.156.202.233:4500 UDP
   10     192.168.99.38:22 -> 192.168.99.93:52040  TCP

Capture complete.
おぉぉ。おもろいなー。これ。自作パケットキャプチャ。

$ ps|grep sniff
     5620    3080    5620       5520  pty0      197108 16:32:12 /home/hoge/src/sniffex/sniffex
$ taskkill /pid 5520 /f
SUCCESS: The process with PID 5520 has been terminated.
$
Ctrl+Cでは止まらない。taskkillコマンドを使う。

iplogもsniffexもpcapを使った実装で、iplogが動かなくて、sniffexが動く理由がわからないけど、sniffexのコードをiplogに移植すれば動くよーになるはず。 やらないけどw。

投稿されたコメント:

コメント
コメントは無効になっています。