我使用的环境为:Windows10、python3.6、scapy 2.4.0
一、基本知识
Sniff方法定义:
sniff(filter="",iface="any", prn=function, count=N)
filter的规则使用 Berkeley Packet Filter (BPF)语法
iface用来指定要在哪个网络接口上进行抓包(通常不指定即所有网络接口)
prn指定回调函数,每当一个符合filter的报文被探测到时,就会执行回调函数,通常使用lambda表达式来写回调函数
count指定最多嗅探多少个报文(是指符合filter条件的报文,而非所有报文)
filter写法举例:
抓取源地址为 www.baidu.com 的报文:
>>> sniff(filter="ip src www.baidu.com", iface=ifs, prn=lambda x:x.summary(), count=3)
Ether / IP / TCP 14.215.177.39:https > 192.168.2.204:6593 A / Padding
Ether / IP / TCP 14.215.177.39:https > 192.168.2.204:6593 A / Padding
Ether / IP / TCP 14.215.177.39:https > 192.168.2.204:6593 A / Raw
Out[15]: 3 UDP:0 ICMP:0 Other:0>
抓取目的地址网段为192.168.2.204/24的报文,没有设置count,所以会一直输出:
>>> sniff(filter="dst net 192.168.2.204", iface=ifs, prn=lambda x:x.summary())
Ether / IP / TCP 180.97.162.191:8202 > 192.168.2.204:4963 PA / Raw
Ether / IP / TCP 180.97.162.191:8202 > 192.168.2.204:4963 PA / Raw
Ether / IP / TCP 180.97.162.191:8200 > 192.168.2.204:4967 PA / Raw / Padding
Ether / IP / TCP 180.97.162.191:8202 > 192.168.2.204:4963 PA / Raw
。。。
抓取非ICMP的报文:
>>> sniff(filter="not icmp", iface=ifs, prn=lambda x:x.summary(), count=3)
Ether / IP / TCP 192.168.2.204:4963 > 180.97.162.191:8202 A
Ether / IP / TCP 180.97.162.191:8202 > 192.168.2.204:4963 PA / Raw
Ether / IP / TCP 192.168.2.204:4962 > 180.97.162.191:8202 A
Out[9]: 3 UDP:0 ICMP:0 Other:0>
prn函数举例:
将抓取到的报文的summary打印出来:
>>> sniff(filter="", iface=ifs, prn=lambda x:x.summary(), count=3)
Ether / IP / TCP 180.97.162.191:8202 > 192.168.2.204:4963 PA / Raw
Ether / IP / TCP 192.168.2.204:4963 > 180.97.162.191:8202 A
Ether / IP / TCP 180.97.162.191:8202 > 192.168.2.204:4963 PA / Raw
3 UDP:0 ICMP:0 Other:0>
将所有IP报文的源地址打印出来:
>>> sniff(filter="", iface=ifs, prn=lambda x:x[IP].src, count=3)
180.97.162.191
192.168.2.204
14.215.177.39
3 UDP:0 ICMP:0 Other:0>
也可使用回调函数:
def packet_callback(packet):
print packet.show()
sniff(prn=packet_callback, count=10)
二、代码示例
import os
from scapy.all import sniff,wrpcap,Raw,IP,TCP
def get_pcap(ifs,ip=None,size=100):
''' 获取指定 ifs(网卡), 指定数量size 的数据包;
如果有指定ip,则这里只接收tcp,80端口,指定ip的包 '''
filter = ""
if ip:
filter += "ip src %s and tcp and tcp port 80"%ip
dpkt = sniff(iface=ifs,filter=filter,count=size)
else:
dpkt = sniff(iface=ifs,count=size)
# wrpcap("pc1.pcap",dpkt) # 保存数据包到文件
return dpkt
def get_ip_pcap(ifs,sender,size=100):
''' 获取指定 ifs(网卡), 指定发送方 sender(域名或ip) 的数据包
size:(一次获取数据包的数量) '''
if 'www.' in sender:
v = os.popen('ping %s'%sender).read()
ip = v.split()[8]
print("准备接收IP为 %s 的数据包..."%ip)
else:
ip = sender
print("准备接收IP为 %s 的数据包..."%ip)
count = 0
while count<10:
d = get_pcap(ifs,ip=sender,size=size)
for i in d:
try:
if i[IP].src==ip: # 发送方的IP为:ip 接收方的IP:i[IP].dst==ip
print(i[Raw].load)
except:
pass
count+=1
def main():
ifs = 'Realtek PCIe GBE Family Controller' # 网卡
ip = "116.4.8.127" # ip地址,也可写域名,如:www.baidu.com
get_ip_pcap(ifs,ip,size=1) # 一次接收一个包
if __name__ =='__main__':
main()
保存文件为:grab_unpack.py
然后打开网站页面,是一个动态刷新的图,效果如下:
执行:python grab_unpack.py
输出结果:
其中最后面以 zx 为键的 json 数据就是我想要的数据,而这是ajax请求过来的数据,使用一般的爬虫是难以爬取到的