またしても、彼はwindowsでwinpcapをpythonから叩く道へ引き返す。

winpcapyを使ってパケットキャプチャをしてみるサンプルコード

あらすじ

( ゚д゚)/ 3行で分かるlibpcapのpythonラッパーたち

茶番

あーしちゃん「あーしwindowsなんだけど」

そこでおじさんは考えた。ctypesしよう、と。

今日紹介するのは winpcapy だよ。
コイツはpythonから超簡単にwinpcap経由でデバイスを叩けるナイスなラッパーさ。
インストール? C:\\python27\\Lib\\site-packages\\ に置くだけだよ。

やっぱりさぁ、windowsでpythonでwinpcap使いたいとか言い始めるとさ、やれコンパイルしろとか色々うるさいわけじゃん。
俺はもっと環境負担の少ない、手軽な感じで救われたいんだよ。
Visual Studio Expressをダウンロードしてくるところから始まるpythonとか、手足を縛られてwindowsの前に転がされた状態からLinux入れてスタートとか、やっぱりしんどいんだよ。
でもwinpcapyはヤバイ。すべてをぶっ飛ばしてLet’s パケットキャプチャ and パケットジェネレータなわけよ。
使い方をいちいち説明する気はないぜ。サンプルコードを載せて、どういう動きをするかだけ書く。
それで十分なのさ。
本家のサンプルコードでも良いんだが、そこはそれ。

オレオレサンプルコード

ちょいと擬似サーバでも立てて、主に実験用途でしか役に立たない感じのエコーサーバを作ってみる。

基本機能

  • 利用可能なインタフェースの一覧を取得する(-d)
  • 待ち受けインタフェースとアドレスを指定して、ARP/pingに応答するサーバとして動作する(-i XXX -s Y.Y.Y.Y -m ZZ:ZZ:ZZ:ZZ:ZZ:ZZ)

ここまで出来ることが確認出来れば、パケットの中身は加工次第でどうにでもなる。

以下コード片。

  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
# -*- coding: utf-8 -*-

from ctypes import *
from winpcapy import *
import dpkt
import socket
from optparse import OptionParser
version = u'%prog 1.1'

def pkt_send(dev, buf):
    pcap_sendpacket(dev,cast(buf,POINTER(c_ubyte)),len(buf))
    return

def _packet_decode(pkt_no, header, pkt_data, dev, opts):
    raw_data = string_at(pkt_data, header.contents.len)
    try:
        packet = dpkt.ethernet.Ethernet(raw_data)
    except:
        print("Decode fail")
        return
    reply = False
    eth_src = ''.join([chr(int(i,16)) for i in opts.server_mac.split(':')])
    eth_dst = packet.src
    eth_type = packet.type
    while 1:
        if type(packet.data) == dpkt.arp.ARP:
            packet = packet.data
            print '[ARP]',socket.inet_ntoa(packet.spa),"search for",socket.inet_ntoa(packet.tpa)
            if socket.inet_ntoa(packet.tpa) == opts.server_ip:
                eth = dpkt.ethernet.Ethernet(src=eth_src, dst=eth_dst, type=eth_type)
                arp = dpkt.arp.ARP(op = dpkt.arp.ARP_OP_REPLY)
                arp.sha = eth_src
                arp.spa = packet.tpa
                arp.tha = packet.sha
                arp.tpa = packet.spa
                eth.data = arp
                buf = str(eth)
                pkt_send(dev, buf)
                break
        elif type(packet.data) == dpkt.ip.IP:
            packet = packet.data
            if socket.inet_ntoa(packet.dst) == opts.server_ip:
                reply = True
        elif type(packet.data) == dpkt.icmp.ICMP:
            if reply and packet.data.type == dpkt.icmp.ICMP_ECHO:
                eth = dpkt.ethernet.Ethernet(src=eth_src, dst=eth_dst, type=eth_type)
                ip = dpkt.ip.IP(src=packet.dst, dst=packet.src, p=packet.p, ttl=packet.ttl)
                icmp = dpkt.icmp.ICMP(type=dpkt.icmp.ICMP_ECHOREPLY)
                icmp.data = packet.data.data
                ip.data = icmp
                ip.len = len(str(ip))
                ip.sum = 0
                eth.data = ip
                buf = str(eth)
                pkt_send(dev, buf)
                reply = False
            break
        else:
            break

def dev_discovery():
    alldevs = POINTER(pcap_if_t)()
    errbuf  = create_string_buffer(PCAP_ERRBUF_SIZE)
    if (pcap_findalldevs(byref(alldevs), errbuf) == -1):
        print ("Error in pcap_findalldevs: %s\n" % errbuf.value)
        return

    dev_count = 0
    try:
        dev = alldevs.contents
    except:
        print ("Error in pcap_findalldevs: %s" % errbuf.value)
        print ("Maybe you need admin privilege?\n")
        return

    while dev:
        dev_count = dev_count+1
        print("%d. %s" % (dev_count, dev.name))
        if (dev.description):
            print (" (%s)\n" % (dev.description))
        else:
            print (" (No description available)\n")
        if dev.next:
            dev = dev.next.contents
        else:
            dev = False

    if (dev_count==0):
        print ("\nNo interfaces found! Make sure WinPcap is installed.\n")
        return

def echo_server(opts):
    alldevs = POINTER(pcap_if_t)()
    errbuf  = create_string_buffer(PCAP_ERRBUF_SIZE)
    if (pcap_findalldevs(byref(alldevs), errbuf) == -1):
        print ("Error in pcap_findalldevs: %s\n" % errbuf.value)
        return

    dev_count = 0
    try:
        dev = alldevs.contents
    except:
        print ("Error in pcap_findalldevs: %s" % errbuf.value)
        print ("Maybe you need admin privilege?\n")
        return

    try:
        dev = alldevs
        for dev_count in range(0,opts.interface-1):
            dev = dev.contents.next
    except:
        print "Invalid interface number."
        pcap_freealldevs(alldevs)
        return

    dev = dev.contents
    adhandle = pcap_open(dev.name,65536,PCAP_OPENFLAG_PROMISCUOUS,1,None,errbuf)
    pcap_setmintocopy(adhandle,60)
    if (adhandle == None):
        print("\nUnable to open the adapter. %s is not supported by Pcap-WinPcap\n" % dev.name)
        pcap_freealldevs(alldevs)
        return

    print("\nlistening on %s...\n" % (dev.description))
    pcap_freealldevs(alldevs)

    pkt_no = 0
    pkt_hdr = POINTER(pcap_pkthdr)()
    pkt_data = POINTER(c_ubyte)()
    try:
        while 1:
            nx = pcap_next_ex(adhandle,pkt_hdr,pkt_data)
            if nx != 0:
                pkt_no += 1
                _packet_decode(pkt_no,pkt_hdr,pkt_data,adhandle,opts)
    except:
        print "exit."
    pcap_close(adhandle)

def main():
    p = OptionParser(version=version)
    p.add_option('-d', '--discovery', action='store_true', help="Discovery device.")
    p.add_option('-i', '--interface', action='store', type='int', help="Interface number.")
    p.add_option('-s', '--server-ip', action='store', help="Virtual IP")
    p.add_option('-m', '--server-mac', action='store', help="Virtual MAC")
    opts, args = p.parse_args()

    if opts.discovery:
        dev_discovery()
        return

    elif opts.server_ip is None or opts.server_mac is None:
        print "Server ip and mac is requied."
    elif opts.interface < 1:
        print "Select interface number."
    else:
        echo_server(opts)
        return

    p.print_version()
    p.print_help()
    return

if __name__ == '__main__':
    main()

長い。
あと、整形とかif文で例外吐きそうになるとかそんな気はするけど。

試しにキャプチャして、動作を確認するとこんな感じ。

デバイス操作のひな形さえ作ってしまえば、存外何にでも使えると思うのだがどうだろう。

Hugo で構築されています。
テーマ StackJimmy によって設計されています。