scapyの勉強をしていた時にpcapファイルに含まれるファイルを全て抽出できないかと思い立ち、とりあえず作ってみました。その際の知識もを含めてメモとして残します。
#インストール
pipを使ってインストールが可能です。
pip3 install scapy
#基本編
下の構文でライブラリを読み込みます
from scapy.all import *
ファイルの読み込みは、rdpcapを使います。
p = rdpcap('test.pcap')
pcapデータを全て見る時は、showメソッドを使います。
p.show()
0000 Ether / IP / UDP / DNS Qry "b'www.qiita.com.'"
0001 Ether / IP / UDP / DNS Ans "13.230.63.181"
パケット1個1個の内容を見る時は、パケット番号を指定してshowメソッドを使います。
p[0].show()
###[ Ethernet ]###
dst = xx:xx:xx:xx:xx:xx
src = xx:xx:xx:xx:xx:xx
type = IPv4
###[ IP ]###
version = 4
ihl = 5
........
###[ UDP ]###
sport = 59228
dport = domain
........
###[ DNS ]###
id = 55997
qr = 0
opcode = QUERY
........
指定したプロトコルがパケットに入っているかを判別します。プロトコルが入っていればtrue
を返します。
'DNS' in p[0]
パケットから指定したプロトコルの内容を取り出します。
p[0]['DNS']
<DNS id=55997 qr=0 opcode=QUERY aa=0 tc=0 rd=1 ra=0 z=0 ad=0 cd=0 rcode=ok qdcount=1 ancount=0 nscount=0 arcount=1 qd=<DNSQR qname='www.qiita.com.' qtype=A qclass=IN |> an=None ns=None ar=<DNSRROPT rrname='.' type=OPT rclass=512 extrcode=0 version=0 z=0 rdlen=None |> |>
パラメータは、下のような構文で取り出せます。
p[0]['DNS'].qr
#応用編
DNS応答から名前解決した結果を取り出します。シノニムも元のFQDNにします。
p = rdpcap('test.pcap')
cnv = lambda x:x if type(x) == str else x.decode()
dnsdata = { cnv(x['DNSRR'][y].rdata):cnv(x['DNSRR'][y].rrname) for x in p if 'DNSRR' in x for y in range(x['DNS'].ancount) }
dns = lambda x:x if not x in dnsdata else dns(dnsdata[x])
print({ r:dns(a) for r,a in dnsdata.items() })
{'13.230.63.181': 'www.qiita.com.', '54.92.35.84': 'www.qiita.com.', '13.113.37.17': 'www.qiita.com.'}
パケットのバイト数を、(送信元のIPアドレス,送信先のIPアドレス)毎に集計します。
p = rdpcap('test.pcap')
t = {}
for y in [ ((x['IP'].src,x['IP'].dst),len(x)) for x in p if 'IP' in x ]:
t[y[0]] = y[1] if not y[0] in t else t[y[0]] + y[1]
print(t)
{('192.168.99.99', '54.92.35.84'): 10984, ('54.92.35.84', '192.168.99.99'): 130283}
TCPのペイロードを、(送信元のIPアドレス,送信元のポート番号,送信先のIPアドレス,送信先のポート番号)毎にファイルとして取り出します。1つのTCPセッションで複数のファイルを送っていた場合は、1つのファイルに全てつながって入りますので、binwalkなどでファイル分割をしてください。
p = rdpcap('test.pcap')
cnv = lambda x:x if type(x) == str else x.decode()
dnsdata = { cnv(x['DNSRR'][y].rdata):cnv(x['DNSRR'][y].rrname) for x in p if 'DNSRR' in x for y in range(x['DNS'].ancount) }
dns = lambda x:x if not x in dnsdata else dns(dnsdata[x])
t = {}
for y in [ ((x['IP'].src,x['TCP'].sport,x['IP'].dst,x['TCP'].dport),[i,x['Raw'].load]) for i,x in enumerate(p) if ('TCP' in x) and ('Raw' in x) ]:
t[y[0]] = y[1] if not y[0] in t else [t[y[0]][0],t[y[0]][1] + y[1][1]]
with open('summary.txt','w') as f1:
for k,v in t.items():
n = str(v[0])+'.bin'
f1.write(n+' -- '+dns(k[0])+':'+str(k[1])+' -> '+dns(k[2])+':'+str(k[3])+'\n')
with open(n,'wb') as f2:
f2.write(v[1])
#さいごに
本当にとりあえず作ったものなので、使い方によっては望みのデータが取り出せない場合があるかもしれません。