☰
pyshark调用tshark实现WEP/WPA无线流量解密实战指南
2026/10/22 11:44:37 网站建设 项目流程

抓包的时候最烦的就是遇到加密的 WiFi 流量——明明 HTTP 请求就在眼前,打开却是满屏的 802.11 协议乱码,DNS、TCP 全被藏在密文后面。以前我处理这种问题都是打开 Wireshark 图形界面,点鼠标配置解密密钥,可一旦要批量分析几十个 pcap,或者把解密流程写进自动化脚本,手点就不现实了。后来我发现 pyshark 可以直接对接 tshark 的 802.11 解密能力,用几行 Python 代码就能把 WEP、WPA-PWD、WPA-PSK 三种常见场景全部搞定,从此再也不用靠手工点来点去。

这篇文章把我自己踩过的坑、实测过的参数组合全写出来,完整覆盖 WEP、WPA-PWD、WPA-PSK 的解密实操,给到可以直接跑通的代码,顺便再把背后的原理讲清楚。适合正在做无线网络安全分析、协议研究,或者已经在用 Wireshark 做数据包分析但想转向 Python 自动化的朋友。提前说明,所有解密演示必须用于你拥有合法授权的网络环境,比如自己的 AP、实验室内网或者明确授权的渗透测试和 CTF 题目。

1. 解密前的底层逻辑:WEP/WPA 加密与 pyshark 的解密入口

1.1 为什么加密流量打开全是乱码

802.11 加密的目的很简单:不让第三方直接看到数据内容。WEP 时代用的是 RC4 流加密,密钥要么是 40 位要么是 104 位,解密时只要拿到正确的密钥,原始明文就能恢复。WPA/WPA2 就复杂一些,TKIP 和 CCMP(AES)都不再用静态密钥直接加密数据帧,而是通过四次握手协商出成对的临时密钥(PTK),再用 PTK 对数据帧做加密。

这意味着解密 WPA 加密的数据包,不是仅仅知道一个密码字符串就能解决的。抓包里必须有完整的四次握手过程,或者你手上有预共享密钥(PSK),再加上准确的 SSID,才能推导出成对主密钥(PMK),进而恢复出 PTK。这也是为什么 WPA 解密比 WEP 挑数据包完整性——抓包阶段丢了握手帧,后面拿着真密码也白搭。

1.2 pyshark 在解密链中的角色

pyshark 并不是重新实现了一套解密算法,它是 tshark 的 Python 封装。真正的帧解密、协议解析、PMK 推导全部由 tshark 在底层完成,pyshark 负责拼装命令行参数、执行命令、解析输出。理解这个角色非常重要,因为你会发现,pyshark 能解密的范围和 tshark 一模一样,很多诡异的问题其实是你传参的方式不对,而不是 pyshark 本身不支持。

举个例子,Wireshark 图形界面里有一个“802.11 解密”设置面板,你可以在里面填 WEP 密钥、WPA 密码、PSK 等。这些配置最终都会映射到 tshark 的命令行参数上,pyshark 则通过decryption_key和decryption_key_type这两个参数帮你把这些配置传进去。

1.3 成功解密的三个必要条件

  • 正确的密钥材料:WEP 的真实密钥字节,或者 WPA 的密码短语 + SSID,又或者是 64 位十六进制的 PMK/PSK。
  • 完整的数据包捕获:WPA 需要捕获到四次握手帧,WEP 则需要捕获到足够的 ARP 注入帧或其他交互帧,否则即使密钥正确,也无法还原出足够多的明文。
  • 正确的协议配置:确保 tshark 的 802.11 解密开关被打开,这对应 Wireshark 里的“Enable decryption”选项。

这三个条件缺一不可,后面所有实操都是围绕它们展开的。

2. 环境准备:装好 pyshark 和 tshark,把解密开关点亮

2.1 安装步骤与版本要求

pyshark 本身用 pip 装就行:

pip install pyshark

但真正干活的 tshark 需要单独装。Wireshark 安装包里会带上 tshark,Linux 下可以直接用系统包管理器安装。装完以后确认一下 tshark 版本:

tshark --version

如果提示找不到命令,多半是 tshark 没有加入 PATH。在 Windows 上常见的情况是 Wireshark 装好了,但 tshark 不在系统路径里,需要手动把C:\Program Files\Wireshark加进 PATH,或者在 pyshark 构造捕获对象时用tshark_path参数明确指定:

cap = pyshark.FileCapture('capture.pcapng', tshark_path='C:/Program Files/Wireshark/tshark.exe')

pyshark 版本尽量选新一点的,0.4 以上对解密参数的支持才比较稳定。如果你现在还停留在 0.3.x,建议直接升级,否则decryption_key_type这个参数很容易出现“传了等于没传”的诡异现象。

2.2 准备一份可复现的测试文件

实战中你可以用自己的无线网卡抓包获取实验素材。把网卡切入监听模式,在目标网络附近抓一小段时间,确保捕获到了客户端关联和四次握手过程:

sudo airmon-ng start wlan0 sudo airodump-ng -c 6 --bssid 00:11:22:33:44:55 -w lab_capture wlan0mon

然后让某个设备重新连接一次该 WiFi,这样握手的四个帧就能被完整抓下来。比如退出重连手机热点,或者直接把路由器重启一下。

如果你只是想做本地测试,也可以直接用 Wireshark 自带的示例抓包文件,或者自己搭一个无线热点,用安装了抓包工具的客户端连接一次。关键是数据包质量,不是数据量大小。

2.3 验证 pyshark 能否正常调用 tshark

先跑一段最简单的读取脚本,确认整个链路是通的:

import pyshark cap = pyshark.FileCapture('lab_capture.pcapng') count = 0 for pkt in cap: count += 1 print(f'total packets: {count}')

能打出包总数,说明 pyshark 到 tshark 的基本链路没问题。接下来就可以进入正题,逐一开始 WEP、WPA-PWD、WPA-PSK 的解密实操。

3. WEP 解密实操:从 pcap 到明文只需两行参数

3.1 WEP 密钥的类型:40 位与 104 位

WEP 密钥按位长分两种:40 位(5 字节)和 104 位(13 字节)。很多老设备也支持 128 位 WEP,但本质上就是 104 位密钥。这里的“位”指的是密钥实际长度,而不是你在路由器管理界面输入的 ASCII 字符串长度。

在 Wireshark/tshark 的体系里,WEP 密钥可以用 ASCII 字符串表示,也可以用十六进制字符串表示。ASCII 模式下 40 位密钥是 5 个字符,104 位密钥是 13 个字符;十六进制模式下则分别是 10 个字符和 26 个字符。实际抓包分析时,路由器里配置的如果是短密码,比如12345,那就是 40 位 WEP;如果是 13 位字符串,则是 104 位 WEP。

3.2 实操:FileCapture 解密 WEP pcap

解密 WEP 数据包时,在 pyshark 里只需要传入密钥和类型:

import pyshark cap = pyshark.FileCapture( 'wep_lab.pcap', decryption_key='1234567890', # 10 位十六进制,对应 40 位 WEP decryption_key_type='wep' ) for pkt in cap: if hasattr(pkt, 'http'): print('HTTP found:', pkt.http.request_uri) elif hasattr(pkt, 'dns'): print('DNS query:', pkt.dns.qry_name)

如果你拿到的是 ASCII 类型的密钥,比如路由器管理页面里显示的是helloworld(10 个字符),你就得先确认它到底是 ASCII 还是十六进制显示。不少二手路由器把 WEP 密钥显示成十六进制,少数老设备会显示 ASCII,混着用最容易出错。

我的建议是统一转成十六进制传给 pyshark,避免字符编码带来的歧义。转换方法也很简单,Python 里:

key_hex = 'password'.encode('utf-8').hex()

但要注意,WEP 密钥的 ASCII 表示通常限定为 5 或 13 个字节,如果你的密钥长度不在这个范围,路由器在配置时就会拒绝。

3.3 怎么确认真的解密成功了

解密是否成功,最直观的验证方式是看数据包还能不能解析出上层协议。加密状态下,全部流量都蜷缩在 802.11 Data 帧里,看不到 HTTP、DNS、TCP 等协议头;解密成功后,数据链路层之上会出现 LLC、IP、TCP,乃至完整的应用层数据。

还有一个辅助判断方法:对比解密前后相同 pcap 的包数。解密开启后,tshark 会把一个加密数据帧还原成包含明文净荷的帧结构,有时会出现“从一个帧里拆出更多解析内容”的情况,所以能看到的有效协议层数量明显变多,这可以作为参考信号。

提示:WEP 破解在现实里已经几乎被淘汰了,但 WEP 解密脚本在分析老旧嵌入式设备、工控系统或 CTF 题目时依然很常用。它的价值更多体现在“理解老协议的解密链路”上。

4. WPA-PWD 解密实操:密码短语 + SSID 的完整配置

4.1 什么是 WPA-PWD

WPA-PWD 这个名字在不同工具里叫法不完全一样,在 Wireshark/tshark 的 802.11 密钥 UAT 表里,它表示“WPA Passphrase 方式”。这种方式的输入不是现成的 PMK,而是两样东西:密码短语(passphrase)和 SSID。

tshark 拿到这两样东西后,会按照 802.11i 标准里的 PBKDF2-SHA1 算法做 4096 次迭代,生成一个 256 位的 PMK。整个过程可以理解为“密码 + SSID 共同作为种子,算出一个用于后续握手验证的密钥材料”。所以 WPA-PWD 方式下,SSID 写错了,哪怕密码完全正确,最后得到的 PMK 也是错的。

4.2 实操:decryption_key 里直接写 password:ssid

pyshark 里 WPA-PWD 的密钥格式比较特殊,要把密码和 SSID 拼在一起,中间用冒号分隔,传给decryption_key,类型指定为wpa-pwd:

import pyshark cap = pyshark.FileCapture( 'wpa2_lab.pcapng', decryption_key='mysecret123:test_wifi', # 格式是 密码:SSID decryption_key_type='wpa-pwd' ) for pkt in cap: if hasattr(pkt, 'http'): print('HTTP request:', pkt.http.request_uri) if hasattr(pkt, 'tcp'): pass

这里有一个非常考验人的细节:pyshark 官方文档写的是decryption_key和decryption_key_type,但底层的 tshark 实际生成的是类似uat:80211_keys:wpa-pwd,mysecret123:test_wifi的参数。如果你自己在 custom_parameters 里手动写,也必须是这个结构,拼错一个逗号位置,解密就会静默失败。

4.3 实操心得:握手完整性、大小写和 SSID 编码

这一节值得单独写,因为 WPA-PWD 解密失败的案例里,十有八九都栽在下面几个地方。

握手不完整是头号问题。WPA 四次握手的四个帧不一定要 100% 全抓到,但至少要抓到 key 交换的关键部分。最稳妥的做法是抓包的时候慢慢点客户端重连,或者用专门工具反复触发关联流程,确保 pcap 里的握手帧数量足够。

SSID 必须精确匹配。有人会问,密码明明是对的,为什么还是解不开?答案往往是 SSID 大小写不一致,比如实际网络叫Test_WiFi,你在代码里写成了test_wifi,PMK 计算结果就完全不同。SSID 是二进制安全的字符串,但不代表它对大小写不敏感,至少在 PMK 推导这一步,字节差异会直接导致结果不同。

密码本身只能取 ASCII 范围内的字符吗?也不是绝对,但是很多路由器配置界面只接受可打印 ASCII。如果密码里有中文或者其他非 ASCII 字符,你需要考虑编码问题,抓包侧和计算 PMK 侧必须用同一个编码方式。我自己遇到过一次密码末尾带不可见空格的情况,排查了很久才发现是复制粘贴过程引入了额外字符。

再有一个细节:pyshark 的decryption_key_type参数名在不同版本里可能有细微差异,0.4 之后基本都稳定为decryption_key_type,但如果你用的是笔记本里随手装的老版本,建议先升级再排查问题。

5. WPA-PSK 解密实操:用 64 位十六进制密钥直接解

5.1 PSK 和 PMK 的关系,别再混着叫

WPA-PSK 这个词在日常口语里经常指“WPA 预共享密钥”,也就是你在手机里输入的密码短语。但在 Wireshark 的 80211_keys UAT 表里,wpa-psk类型要求的值不是密码短语,而是 64 位十六进制的 PMK。

可以这样理解:wpa-pwd是让 tshark 现算 PMK,输入材料是密码和 SSID;wpa-psk是直接喂给它算好的 PMK,省略了 PBKDF2 计算环节。二者本质上是同一个密钥材料的不同表达形式,网络上有些人把wpa-psk和密码短语混为一谈,到了传参这一步就会翻车。

5.2 实操:把密码短语转成 PSK 再喂给 pyshark

如果你手头只有密码短语和 SSID,可以先用工具生成对应的 PSK。Linux 下最简单的是wpa_passphrase:

wpa_passphrase test_wifi mysecret123

输出里那个psk=后面的 64 位十六进制字符串,就是 PMK,也就是 tshark 需要的 wpa-psk 值。

不想依赖系统工具,也可以用 Python 算:

import hashlib def passphrase_to_psk(passphrase: str, ssid: str) -> str: pmk = hashlib.pbkdf2_hmac( 'sha1', passphrase.encode('utf-8'), ssid.encode('utf-8'), 4096, 32 ) return pmk.hex()

算好之后,在 pyshark 里直接使用:

import pyshark psk_hex = '9f9f...(64 位十六进制)' cap = pyshark.FileCapture( 'wpa2_lab.pcapng', decryption_key=psk_hex, decryption_key_type='wpa-psk' ) for pkt in cap: if hasattr(pkt, 'dns'): print('DNS:', pkt.dns.qry_name)

特别注意:传给decryption_key的字符串必须正好是 64 个十六进制字符,任何多余的空格、换行符、前导0x都会让解析失败。有些工具会输出带0x前缀的格式,记得剥掉。

5.3 WPA-PWD 和 WPA-PSK 到底怎么选

场景推荐方式原因
手头只有 WiFi 密码wpa-pwd直接传 password:ssid,让 tshark 推导 PMK
手头已经有 PMK/PSKwpa-psk不依赖 SSID 二次计算,适合自动化批处理
多个 AP 同密码但 SSID 不同wpa-pwd每次指定不同 SSID,分别解密
大量 pcap 批量分析wpa-psk预计算好 PSK,参数固定,出错概率小
只想验证某个握手包密钥wpa-psk离线分析时避免 PBKDF2 重复计算开销

我自己做批量分析时,更倾向于 wpa-psk。原因很简单:PMK 算一次是固定的,后续所有 pcap 用同一个 64 位十六进制值,省掉了每次都拼接密码和 SSID 的麻烦,也少了一层“SSID 大小写写错”的风险。

6. 实时解密与高级参数:LiveCapture 和 custom_parameters

6.1 LiveCapture 实时抓包解密

除了离线解析 pcap 文件,pyshark 也支持实时抓包并同时解密。比如你已经知道某个热点的密码,想实时看看当前连接的设备都在访问哪些域名,可以这样:

import pyshark cap = pyshark.LiveCapture( interface='wlan0mon', decryption_key='mysecret123:test_wifi', decryption_key_type='wpa-pwd' ) for pkt in cap.sniff_continuously(): if hasattr(pkt, 'http'): print('HTTP:', pkt.http.request_uri)

这里有几个前提必须讲清楚。第一,网卡必须支持并开启监听模式,否则网卡驱动会把 802.11 帧转换成伪以太网帧,很多加密信息在这一步就丢了。第二,实时解密要求抓包进程和握手发生过程同步,如果程序启动时客户端已经完成关联,后续数据包仍然解不开。第三,Linux 下监听模式抓包需要 root 权限,pyshark 报权限错误的话,检查一下是不是没有用 sudo。

实时解密更适合做无线网络故障定位,比如现场排查某个终端上网慢,或者统计一个 AP 下的真实流量分布。它的性能开销比离线解析大,流量高的时候容易丢包,所以生产环境最好还是抓完包再离线解密。

6.2 用 custom_parameters 手动控制 tshark

说完 LiveCapture,再补一个 pyshark 里容易被忽略但极其实用的功能:custom_parameters。它可以让你直接向 tshark 命令行传递任意参数,也就是说,你能做出比 pyshark 默认参数更复杂的解密配置。

举个例子,如果 pcap 里有多个 WEP 密钥,直接通过decryption_key只能填一个,但用 custom_parameters 可以把 80211_keys UAT 表里的多行密钥全部写进去:

import pyshark cap = pyshark.FileCapture( 'multi_key.pcap', custom_parameters=[ '-o', 'wlan.enable_decryption:true', '-o', 'uat:80211_keys:wep,AAAA:1111', '-o', 'uat:80211_keys:wep,BBBB:2222', ] )

注意,custom_parameters 接收的是一个列表,列表的每个元素是按照命令行顺序排列的参数名和参数值。所以'-o'和'uat:80211_keys:...'必须成对出现。这个设计有点反直觉,但它确实能让你绕过 pyshark 默认封装,直接用 tshark 的完整能力。

6.3 监控模式下的抓包小提示

如果你准备自己抓取解密素材,监听模式是绕不开的环节。Linux 下用 airmon-ng 切监听模式之后,接口名通常会变成带mon后缀的名字,比如wlan0mon。抓包的时候要注意,监听模式下网卡不参与正常的网络通信,也就是说你用同一块网卡一边抓包一边上网是行不通的。

另外,在 802.11 信道上,很多网卡默认只抓管理帧和数据帧的控制部分,数据帧的完整内容可能需要额外设置射频监控模式(monitor flag)才能全部收进来。不同无线网卡芯片支持度差异很大,有些网卡(比如部分高通芯片)能很完整地抓全数据帧,有些网卡则只能收到零散碎片。抓包质量不好,后面解密再正确也没用。

7. 常见问题与排查技巧实录

7.1 高频问题速查表

现象可能原因排查方向
解密后仍然只有 802.11,没有上层协议密钥错误、SSID 错误、抓包没有握手帧先用 tshark 命令行加相同参数测试
WPA 解密能看到部分 ICMP,但看不到 HTTP抓包过程中只捕获了部分会话数据检查抓包是否跨完整连接,重新抓包
pyshark 报错 tshark 命令执行失败tshark 路径不对、参数格式错误打印实际执行的 tshark 命令,逐步拆分
decryption_key_type 好像没生效pyshark 版本过旧或参数名拼写错误升级 pyshark,检查源代码签名
可以解析 DNS,但应用层乱码解密正常,但抓包存在分片丢失检查 pcap 是否存在大量 Fragmented 帧
wpa-pwd 始终解不开 WPA3 网络WPA3 的握手机制与 WPA2 不同确认网络是 WPA2-Personal,不是 WPA3-SAE

7.2 三步定位法

解密失败时不要盲目改参数,我一般按照下面三步来排。

第一步,用 tshark 命令行直测,绕过 pyshark。举一个 WPA-PWD 的例子:

tshark -r wpa2_lab.pcapng -o "wlan.enable_decryption:true" -o "uat:80211_keys:wpa-pwd,mysecret123:test_wifi" -Y "http || dns"

如果 tshark 能正常解析出 HTTP 和 DNS,说明 pcap 文件和解密密钥都没问题,问题出在 pyshark 传参环节。如果 tshark 也解析不出来,那基本可以断定是 pcap 缺握手帧或密钥本身有误。

第二步,检查握手帧是否存在。用显示过滤器筛选 EAPOL:

tshark -r wpa2_lab.pcapng -Y "eapol"

如果 eapol 一个都看不到,说明抓包根本没有覆盖四次握手,这时候任何解密操作都是徒劳,重新抓包才是出路。

第三步,打印 pyshark 实际生成的 tshark 命令。在 pyshark 里可以临时 hack 一下,把命令打出来:

cap = pyshark.FileCapture('wpa2_lab.pcapng', decryption_key='mysecret123:test_wifi', decryption_key_type='wpa-pwd') print(cap._get_tshark_command()) # 查看实际命令行

对比打印出的命令和第 7.2 节第一步里的手工命令,找出参数差异,往往一眼就能看出问题所在。

7.3 几个小技巧

批量解密一堆 pcap 时,建议把它封装成一个函数,把密钥、SSID、文件路径作为参数传进去:

def decrypt_pcap(filepath, password=None, ssid=None, psk=None): if psk: key = psk key_type = 'wpa-psk' else: key = f'{password}:{ssid}' key_type = 'wpa-pwd' cap = pyshark.FileCapture( filepath, decryption_key=key, decryption_key_type=key_type, display_filter='http || dns' ) result = [] for pkt in cap: result.append(pkt) cap.close() return result

另外,解密时建议加上display_filter只筛选你关注的协议。环境复杂的时候,一个 pcap 里可能有几千个包,不做过滤的话,pyshark 逐个包解析的上层协议会拖慢速度,而且内存占用会明显上升。用keep_packets=False也可以减少内存压力,适合只需要逐包流式处理的场景。

我自己的习惯是,正式跑批量任务前,先抽出一个小 pcap 验证密钥和参数,再放到完整文件上执行。不要一上来就对几百兆的文件跑解密,一旦参数错误,纯浪费时间。

最后再分享一个个人体会:解密类操作的成功率,很大程度上取决于抓包阶段的质量,而不是解密阶段的算法。很多朋友拿了一个缺握手的 pcap 反复调 pyshark,折腾一晚上也没结果,其实就是一开始数据就没抓全。所以现在我看到类似问题,第一反应永远是先确认抓包素材的完整性,再看密钥参数。先把这一步养成本能,你会发现 WEP、WPA-PWD、WPA-PSK 这些解密操作,本质上都只是“把正确的凭证喂给会算的家伙”而已。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询