这里有个问题,如果使用Anaconda目录的pip安装则可能失败,目前原因未明,但官方的python3对应的pip3及python2对应的pip均无此问题。 Windows 端安装 pypcap
根据pypcap官方说明:
WinPcap has compatibility issues with Windows 10, therefore it's recommended to use Npcap (Nmap's packet sniffing library for Windows, based on the WinPcap/Libpcap libraries, but with improved speed, portability, security, and efficiency). Please enable WinPcap API-compatible mode during the library installation.
这里提到winpcap与win10间存在兼容性问题,具体什么问题我也没搞清楚,之前使用wireshark抓包一直用的winpcap也没问题。不过我估计和后面要用到的npcap sdk有关吧。既如此,就需要在安装pypcap前安装好Npcap,并下载好Npcap SDK。
下载文件 ·pypcap 源码 · Npcap · Npcap SDK 安装 · 安装Npcap
安装下载后的Npcap安装包,如果电脑带有无线网卡,记得勾选“support raw 802.11 traffic(and monitor mode) for wireless adapters”。需要注意的是,如果电脑已经安装过winpcap软件,在安装Npcap时会弹窗提示卸载Winpcap,此时需要关闭wireshark或是其它相关的软件 · 安装pypcap
1. 将Npcap SDK文件夹和pypcap源码文件夹放在一个目录下;
2. 将Npcap SDK文件夹名称修改为wpdpack;
3. 进入pypcap源码目录,执行python setup.py install即可完成安装。
在第三步需要注意的是,如果Python版本为3.7.2(其它大于3.7的版本没试过)有可能编译失败,因为有个头文件pystate.h在高版本会有更新,导致结构体_ts PyThreadState中的某些参数不识别,从而提示错误pcap.c(22849): error C2039: 'exc_value': is not a member of '_ts'等。之后我将版本换至3.6.6后便正常编译了。
安装完成后,可以进入python执行import pcap查看是否已经可以正常导入。 简单使用
import pcap
# list all of the Internet devices
devs = pcap.findalldevs()
print(*devs, sep='\n')
pc = pcap.pcap(devs[3], promisc=True, immediate=True, timeout_ms=50)
# fiter http pcakets
pc.setfilter('tcp port 80')
for ptime, pdata in pc:
print(ptime, pdata)
接下来简单解释下几个主要函数: findalldevs
findalldevs可以列出当前操作系统的所有网络接口,但是windows和Linux的输出风格不大一样,下面来看看。
Linux版输出简单明了,若我猜的不错,输出的首个接口便是电脑的有线接口(本人台式机,Ubuntu系统),至少在我这是适用的。
python
Python 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pcap
>>> pcap.findalldevs()
['enp2s0', 'any', 'lo', 'nflog', 'nfqueue', 'usbmon1', 'usbmon2']
>>>
pcap.pcap
pc = pcap.pcap(devs[3], promisc=True, immediate=True, timeout_ms=50)
以上代码定义了一个pcap对象,首个参数devs[3]对应接口名,promisc为真代表打开混杂模式,immediate代表立即模式,启用将不缓存数据包,timeout_ms代表接收数据包的超时时间。 setfilter
setfilter用来设置数据包过滤器,比如只想抓http的包,那就通过setfilter(tcp port 80)实现,更加详细的过滤规则请自行谷歌。 抓包
for ptime, pdata in pc:
print(ptime, pdata)
pcap.pcap对象pc是个动态数据,通常结合for循环或是while循环不断读取数据包,数据包会返回时间戳及报文数据。
上面这个小例子就是简单的说明pcap常用库函数的使用方法.具体的数据包的存储及解析需要由解析库dpkt来完成.下面是一个更加详细的抓包工具实例,可以完成数据包的抓取、解析及存储。 简易抓包工具
#!/usr/bin/env python3
# -*- encoding:utf-8 -*-
import pcap
import dpkt
import getopt
import sys
import datetime
import time
import os
import platform
if 'Windows' in platform.platform():
import winreg as wr
IF_REG = r'SYSTEM\CurrentControlSet\Control\Network\{4d36e972-e325-11ce-bfc1-08002be10318}'
def getInterfaceByName(name):
'''Get guid of interface from regedit of windows system
Args:
name: interface name
Returns:
An valid guid value or None.
Example:
getInterfaceByName('eth0')
'''
reg = wr.ConnectRegistry(None, wr.HKEY_LOCAL_MACHINE)
reg_key = wr.OpenKey(reg, IF_REG)
for i in range(wr.QueryInfoKey(reg_key)[0]):
subkey_name = wr.EnumKey(reg_key, i)
try:
reg_subkey = wr.OpenKey(reg_key, subkey_name + r'\Connection')
Name = wr.QueryValueEx(reg_subkey, 'Name')[0]
wr.CloseKey(reg_subkey)
if Name == name:
return r'\Device\NPF_' + subkey_name
except FileNotFoundError as e:
pass
return None
def mac_addr(mac):
return '%02x:%02x:%02x:%02x:%02x:%02x'%tuple(mac)
def ip_addr(ip):
return '%d.%d.%d.%d'%tuple(ip)
def captureData(iface):
pkt = pcap.pcap(iface, promisc=True, immediate=True, timeout_ms=50)
# filter method
filters = {
'DNS': 'udp port 53',
'HTTP': 'tcp port 80'
}
# pkt.setfilter(filters['HTTP'])
pcap_filepath = 'pkts/pkts_{}.pcap'.format(time.strftime("%Y%m%d-%H%M%S",
time.localtime()))
pcap_file = open(pcap_filepath, 'wb')
writer = dpkt.pcap.Writer(pcap_file)
print('Start capture...')
try:
pkts_count = 0
for ptime, pdata in pkt:
writer.writepkt(pdata, ptime)
# anlysisData(pdata)
printRawPkt(ptime, pdata)
pkts_count += 1
except KeyboardInterrupt as e:
writer.close()
pcap_file.close()
if not pkts_count:
os.remove(pcap_filepath)
print('%d packets received'%(pkts_count))
def printRawPkt(time, data):
eth = dpkt.ethernet.Ethernet(data)
print('Timestamp: ', str(datetime.datetime.utcfromtimestamp(time)))
print('Ethernet Frame: ', mac_addr(eth.src), mac_addr(eth.dst))
if not isinstance(eth.data, dpkt.ip.IP):
print('')
return
ip = eth.data
# get fragments info
do_not_fragment = bool(ip.off & dpkt.ip.IP_DF)
more_fragments = bool(ip.off & dpkt.ip.IP_MF)
fragment_offset = ip.off & dpkt.ip.IP_OFFMASK
print('IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)\n' % (
ip_addr(ip.src), ip_addr(ip.dst), ip.len, ip.ttl,
do_not_fragment, more_fragments, fragment_offset))
def anlysisData(data):
packet = dpkt.ethernet.Ethernet(data)
if isinstance(packet.data, dpkt.ip.IP):
ip = ip_addr(packet.data.dst)
if packet.data.data.dport == 80 or packet.data.data.sport == 80:
try:
print(packet.data.data.data.decode('utf-8', errors='ignore'))
except UnicodeDecodeError as uderr:
print(uderr.__str__())
def main():
if 'Windows' in platform.platform():
iface = getInterfaceByName('Router')
else:
iface = 'enp2s0'
captureData(iface)
if __name__ == "__main__":
main()
简要说明 ·获取接口
getInterfaceByName根据接口名称,通过查找注册表信息获取pcap所需的接口设备信息,适用于Windows系统.至于Linux系统,直接通过ifconfig获取即可,至于自动获取功能,目前还没写,以后再说吧。 · 数据包存储
为了将数据包存储到.pcap文件(此类文件可以使用wireshark打开)中,可以通过dpkt.pcap.Writer对象使用writepkt函数不断写入文件。
pcap_file = open(pcap_filepath, 'wb')
writer = dpkt.pcap.Writer(pcap_file)
for ptime, pdata in pkt:
writer.writepkt(pdata, ptime)