
在嵌入式开发和无线安全研究领域Wi-Fi握手包的捕获与分析是一个经典且重要的课题。它不仅是理解802.11协议安全机制的关键也是进行合法安全评估如渗透测试授权的必备技能。传统的抓包方法往往依赖于电脑和特定的无线网卡流程复杂且不便携。本文将分享一个结合硬件与软件的创新实践使用BW16 Wi-Fi/蓝牙双模模组基于RTL8720DN搭建一个便携式握手包捕获器并为其开发一个简洁的网页UI进行控制与状态监控。本项目非常适合对嵌入式网络、单片机开发以及Web前后端交互感兴趣的开发者。通过本文你将掌握从固件开发、网络协议解析到简易Web服务器搭建的全流程。我们将从核心概念讲起逐步完成硬件选型、固件烧录、功能实现和网页UI设计最终得到一个可通过手机或电脑浏览器控制的独立抓包设备。1. 背景与核心概念解析在深入项目之前有必要厘清几个核心概念这有助于理解我们到底在做什么以及为什么这么做。1.1 什么是Wi-Fi握手包Wi-Fi握手包特指在客户端如手机、电脑尝试连接一个受WPA/WPA2-PSK预共享密钥保护的无线接入点AP即路由器时双方进行四次交互所产生的一组数据包。这四次交互被称为“四次握手”4-Way Handshake。握手的目的并非传输密码而是为了验证双方是否拥有相同的预共享密钥即Wi-Fi密码并基于此生成用于加密后续通信数据的临时密钥PTK/GTK。四次握手过程简述消息1 (M1): AP向客户端发送一个随机数ANonce。消息2 (M2): 客户端用自己的随机数SNonce和已知的密码PMK结合ANonce计算出一个PTK并将SNonce发送给AP。消息3 (M3): AP用收到的SNonce和自己的ANonce、密码PMK计算PTK。如果计算出的PTK与客户端计算的一致通过验证MIC码则说明密码正确。AP发送GTK给客户端。消息4 (M4): 客户端确认收到GTK。抓取的意义在这四次交互中M1和M2包含了ANonce和SNonce并且是以明文形式在网络中广播的。如果我们抓取到了这完整的四个包再结合目标Wi-Fi的SSID服务集标识符和一份密码字典就可以在离线环境下进行暴力破解或字典攻击尝试还原出密码。请注意此技术仅可用于对自己拥有完全所有权的网络进行安全测试或是在获得明确书面授权的渗透测试中使用未经授权攻击他人网络是违法行为。1.2 为什么选择BW16模组BW16是一款基于RTL8720DN芯片的Wi-Fi/蓝牙双模模组。选择它作为本项目的硬件核心主要基于以下几点考虑成本与集成度作为模组它集成了芯片、射频电路和天线价格相对低廉开发者无需复杂的射频设计即可使用。监控模式支持RTL8720DN芯片的驱动支持将Wi-Fi接口设置为“监控模式”Monitor Mode。在此模式下网卡可以捕获空气中所有802.11帧包括目标AP与任意客户端之间的握手包而不必连接到任何网络这是抓包的前提。单片机特性它本身就是一个微控制器MCU可以运行用户编写的固件程序实现逻辑控制、数据包处理和网络通信等功能。开发友好有基于Arduino、FreeRTOS等的SDK支持降低了开发门槛。1.3 网页UI控制的意义传统的抓包工具如airodump-ng需要在命令行操作。本项目旨在提供一个更直观、易用的交互方式便携独立设备将BW16配置为AP模式并运行一个微型Web服务器。用户只需用手机/电脑连接该设备创建的Wi-Fi热点打开浏览器即可访问控制页面。图形化操作通过网页上的按钮、表单实现“开始扫描”、“选择目标”、“开始抓包”、“下载数据包文件”等操作。状态实时反馈在网页上动态显示扫描到的AP列表、客户端列表、已捕获的握手包数量等信息。2. 环境准备与硬件搭建2.1 所需硬件清单BW16模组开发板推荐使用搭载BW16模组的开发板如Ai-Thinker BW16 Kit它通常自带USB转串口芯片方便供电和调试。USB数据线用于连接开发板和电脑进行供电、程序烧录和串口日志打印。电脑用于编写和编译固件以及后续的网页访问测试。2.2 软件开发环境搭建BW16有多种开发方式本项目以Arduino框架为例因其生态丰富易于上手。安装Arduino IDE从官网下载并安装最新版Arduino IDE。添加开发板支持打开Arduino IDE进入文件 - 首选项在“附加开发板管理器网址”中添加https://github.com/ambiot/ambd_arduino/raw/master/Arduino_package/package_realtek.com_ameba_index.json打开工具 - 开发板 - 开发板管理器搜索“Realtek Ameba”找到并安装Realtek Ameba Boards (32-bit Arm Cortex-M33)。选择开发板与端口安装完成后在工具 - 开发板中选择BW16 (RTL8720DN)。将BW16开发板通过USB连接电脑在工具 - 端口中选择对应的串口如COM3或/dev/ttyUSB0。2.3 项目依赖库安装我们需要安装以下库来支持Wi-Fi监控模式和Web服务器功能。在Arduino IDE的“库管理器”中搜索并安装WiFiArduino核心库通常已内置。WebServer用于创建HTTP服务器如ESP8266WebServer或WebServer根据Ameba平台可能名称不同需确认兼容性。Ameba平台可能使用内置的WiFiWebServer示例。用于数据包处理的库原生的WiFi库可能不支持监控模式。我们需要使用Realtek SDK中提供的底层API。关键点这通常不通过库管理器安装而是需要引用Ameba SDK中的特定头文件和示例。最直接的方法是找到Arduino安装目录下的hardware/realtek/ameba/...路径参考其中关于Promiscuous混杂模式的示例代码。3. 核心原理与功能拆解整个系统的工作流程可以分为几个核心模块。3.1 系统工作流程初始化设备启动后初始化串口用于调试输出并将Wi-Fi设置为AP模式创建一个供控制端连接的热点如SSID:BW16-Capturer, 密码:12345678。启动Web服务器在AP网络内启动一个HTTP服务器例如监听IP192.168.4.1端口80并绑定处理不同URL请求的函数。网页交互用户设备连接BW16-Capturer热点。浏览器访问http://192.168.4.1加载控制页面。用户在页面点击“扫描”浏览器发送AJAX请求到/scan。后端处理Web服务器收到/scan请求调用底层SDK函数将Wi-Fi网卡切换到扫描模式收集周围AP信息SSID, BSSID, 信道信号强度等然后以JSON格式返回给网页。用户选择目标AP和信道点击“开始抓包”发送请求到/start_capture。抓包引擎后端收到指令调用SDK函数将网卡设置为监控模式并锁定到指定信道。注册一个数据包接收回调函数。此后所有经过该信道的802.11数据帧都会被此函数捕获。在回调函数中解析每个帧的头部。判断其是否为“EAPOL-Key”帧即握手包并检查其是否为完整的四次握手序列。将符合条件的握手包通常需要M1, M2, M3, M4保存到内存缓冲区或SPIFFS文件系统中。状态反馈与下载网页通过定时轮询如请求/status获取已抓到的握手包数量、目标AP信息等。抓包完成后用户可点击“下载”请求/download服务器将保存的握手包文件通常是.pcap或.cap格式发送给浏览器。3.2 关键技术点监控模式与包过滤这是项目的核心难点。在Arduino环境下我们需要使用Realtek SDK的非标准API。关键函数/概念基于Ameba SDK示例wifi_set_channel(int channel): 设置Wi-Fi工作的信道。wifi_promiscuous_enable(...)或wifi_set_promiscuous(...): 启用网卡的混杂/监控模式。wifi_set_promiscuous_rx_cb(promiscuous_callback_t cb): 设置监控模式下的数据包接收回调函数。所有捕获的帧都会传递给这个回调函数。回调函数结构回调函数会接收到数据包的缓冲区指针、长度以及一个包含RSSI等信息的wifi_promiscuous_pkt_t结构体。开发者需要在此函数中解析802.11帧头。802.11帧基础解析 在回调函数中我们需要检查帧类型。握手包是“管理帧”中的“认证帧”子类型并且承载的是EAPOL-Key协议。// 伪代码展示回调函数中的判断逻辑 void promiscuous_rx_cb(uint8_t *buf, uint16_t len, wifi_promiscuous_pkt_t *pkt) { // 1. 检查长度是否足够包含802.11帧头 if (len sizeof(wifi_ieee80211_mac_hdr_t)) return; wifi_ieee80211_mac_hdr_t *hdr (wifi_ieee80211_mac_hdr_t*)buf; // 2. 获取帧控制字段 uint16_t frameControl hdr-frame_ctrl; // 3. 判断是否为数据帧握手包在WPA2中通常以数据帧形式承载EAPOL协议 // 更准确的判断是帧类型为“数据帧”(0x02)并且子类型为“数据”(0x00) // 同时需要检查其承载的LLC/SNAP封装看上层协议是否为0x888E (EAPOL) uint8_t frameType (frameControl 0x0C) 2; // 提取类型位 uint8_t frameSubType (frameControl 0xF0) 4; // 提取子类型位 if (frameType 0x02 frameSubType 0x00) { // 可能是QoS数据帧需要进一步解析 // 跳过802.11 MAC头、可能的QoS控制字段找到LLC/SNAP头 uint8_t *llc buf sizeof(wifi_ieee80211_mac_hdr_t) 2; // 2 for QoS control if (llc[0] 0xAA llc[1] 0xAA llc[2] 0x03 llc[3] 0x00 llc[4] 0x00 llc[5] 0x00 llc[6] 0x88 llc[7] 0x8E) { // 确认是EAPOL-Key帧 // 4. 解析EAPOL-Key报文提取密钥信息、MIC、Nonce等 // 5. 判断是四次握手中的哪一条消息通过Key Descriptor Version, Key Type, Key Info字段 // 6. 将完整的帧或关键信息存储起来 save_handshake_packet(buf, len, target_bssid); } } }注意实际的帧结构解析更为复杂需要参考802.11和EAPOL协议标准。Realtek SDK的示例代码PromiscuousMode是极好的起点。4. 完整实战固件开发与网页UI实现4.1 项目文件结构规划在Arduino IDE中创建一个新项目我们可能需要以下主要文件capture_portal.ino主程序文件包含setup()和loop()。capture_engine.h/cpp抓包引擎核心包含监控模式设置、回调函数、包解析逻辑。web_interface.h/cppWeb服务器相关定义路由和处理函数。data文件夹通过SPIFFS上传存放网页前端文件HTML, CSS, JS。4.2 主程序框架 (capture_portal.ino)#include WiFi.h #include WebServer.h // 根据实际平台调整 #include SPIFFS.h #include capture_engine.h #include web_interface.h WebServer server(80); // 在端口80创建Web服务器对象 CaptureEngine capturer; // 抓包引擎实例 void setup() { Serial.begin(115200); delay(1000); // 初始化文件系统用于存储网页和抓包文件 if (!SPIFFS.begin(true)) { Serial.println(SPIFFS Mount Failed); return; } // 1. 启动AP模式 WiFi.mode(WIFI_AP); bool apResult WiFi.softAP(BW16-Capturer, 12345678); if (!apResult) { Serial.println(AP启动失败); while(1); } Serial.print(AP IP地址: ); Serial.println(WiFi.softAPIP()); // 通常是 192.168.4.1 // 2. 初始化抓包引擎 capturer.init(); // 3. 初始化Web服务器设置路由 setupWebServerRoutes(server, capturer); // 4. 启动Web服务器 server.begin(); Serial.println(HTTP服务器已启动); } void loop() { server.handleClient(); // 处理客户端请求 capturer.loop(); // 抓包引擎的后台处理如果有 }4.3 抓包引擎核心实现 (capture_engine.cpp 片段)这里展示关键部分的简化代码实际开发需结合SDK示例。// capture_engine.h #ifndef CAPTURE_ENGINE_H #define CAPTURE_ENGINE_H #include Arduino.h class CaptureEngine { public: bool init(); bool startCapture(const String targetBSSID, uint8_t channel); void stopCapture(); String getStatusJSON(); // 返回状态信息的JSON字符串 bool saveToFile(const char* filename); // 保存为pcap文件 private: static void packetHandler(uint8_t *buf, uint16_t len, wifi_promiscuous_pkt_t *pkt); // ... 其他私有成员变量如目标BSSID、信道、握手包计数器、存储缓冲区等 }; #endif // capture_engine.cpp (关键部分) #include capture_engine.h // 全局或静态变量用于在C风格回调函数中访问类成员需要一些技巧如使用单例或传递this指针 CaptureEngine* activeCapturer nullptr; void CaptureEngine::packetHandler(uint8_t *buf, uint16_t len, wifi_promiscuous_pkt_t *pkt) { if (!activeCapturer) return; // 调用成员函数处理包 // activeCapturer-processPacket(buf, len, pkt); } bool CaptureEngine::init() { // 初始化状态变量 // ... activeCapturer this; // 设置全局指针 return true; } bool CaptureEngine::startCapture(const String targetBSSID, uint8_t channel) { Serial.printf(开始抓包目标: %s, 信道: %d\n, targetBSSID.c_str(), channel); // 1. 设置信道 wifi_set_channel(channel); // 2. 设置抓包回调函数 wifi_set_promiscuous_rx_cb(packetHandler); // 3. 启用监控模式 wifi_set_promiscuous(1); // 参数1为启用 // 4. 重置计数器等状态 // ... return true; } void CaptureEngine::stopCapture() { wifi_set_promiscuous(0); // 关闭监控模式 Serial.println(抓包已停止); }4.4 Web服务器与API接口 (web_interface.cpp)// web_interface.cpp #include web_interface.h #include SPIFFS.h void setupWebServerRoutes(WebServer* server, CaptureEngine* capturer) { // 1. 静态文件服务提供前端页面 server-serveStatic(/, SPIFFS, /index.html); server-serveStatic(/style.css, SPIFFS, /style.css); server-serveStatic(/script.js, SPIFFS, /script.js); // 2. API 接口 // 扫描附近AP server-on(/api/scan, HTTP_GET, [capturer]() { // 调用WiFi.scanNetworks或底层扫描函数 // 将结果格式化为JSON数组[{ssid:..., bssid:..., channel:1, rssi:-60}, ...] String json capturer-scanNetworksJSON(); // 假设有此方法 server-send(200, application/json, json); }); // 开始抓包 server-on(/api/start_capture, HTTP_POST, [capturer]() { String bssid server-arg(bssid); int channel server-arg(channel).toInt(); if (bssid.length() 0 || channel 0) { server-send(400, text/plain, 参数错误); return; } bool success capturer-startCapture(bssid, channel); if (success) { server-send(200, text/plain, 抓包已启动); } else { server-send(500, text/plain, 启动失败); } }); // 获取抓包状态 server-on(/api/status, HTTP_GET, [capturer]() { String status capturer-getStatusJSON(); server-send(200, application/json, status); }); // 下载抓包文件 server-on(/api/download, HTTP_GET, [capturer]() { // 设置响应头触发浏览器下载 server-sendHeader(Content-Type, application/octet-stream); server-sendHeader(Content-Disposition, attachment; filenamehandshake.cap); // 从文件系统读取.cap文件并发送 File file SPIFFS.open(/handshake.cap, r); if (file) { server-streamFile(file, application/octet-stream); file.close(); } else { server-send(404, text/plain, 文件未找到); } }); }4.5 网页前端示例 (index.html 和 script.js)这是一个极简的UI用于演示交互逻辑。!DOCTYPE html html head titleBW16 握手包捕获器/title link relstylesheet hrefstyle.css meta charsetutf-8 /head body h1Wi-Fi握手包捕获控制台/h1 div button onclickscanNetworks()扫描网络/button div idapList/div /div div h3抓包控制/h3 input typetext idtargetBSSID placeholder目标BSSID (如: AA:BB:CC:DD:EE:FF) input typenumber idtargetChannel placeholder信道 min1 max14 button onclickstartCapture()开始抓包/button button onclickstopCapture()停止抓包/button /div div h3状态/h3 p已捕获握手包: span idhandshakeCount0/span/p button onclickdownloadCapture()下载.cap文件/button /div script srcscript.js/script /body /html// script.js let refreshInterval; function scanNetworks() { fetch(/api/scan) .then(r r.json()) .then(data { let html ul; data.forEach(ap { html li${ap.ssid} (${ap.bssid}) - 信道${ap.channel} - 信号${ap.rssi}dBm button onclicksetTarget(${ap.bssid}, ${ap.channel})选择/button/li; }); html /ul; document.getElementById(apList).innerHTML html; }); } function setTarget(bssid, channel) { document.getElementById(targetBSSID).value bssid; document.getElementById(targetChannel).value channel; } function startCapture() { const bssid document.getElementById(targetBSSID).value; const channel document.getElementById(targetChannel).value; const formData new FormData(); formData.append(bssid, bssid); formData.append(channel, channel); fetch(/api/start_capture, { method: POST, body: new URLSearchParams(formData) }) .then(r r.text()) .then(msg alert(msg)); // 开始轮询状态 if (refreshInterval) clearInterval(refreshInterval); refreshInterval setInterval(updateStatus, 2000); } function updateStatus() { fetch(/api/status) .then(r r.json()) .then(data { document.getElementById(handshakeCount).textContent data.count || 0; }); } function stopCapture() { // 需要实现 /api/stop_capture 接口 fetch(/api/stop_capture, { method: POST }) .then(r r.text()) .then(msg { alert(msg); if (refreshInterval) clearInterval(refreshInterval); }); } function downloadCapture() { window.location.href /api/download; }4.6 固件烧录与测试编译上传在Arduino IDE中点击“上传”按钮将完整代码编译并烧录到BW16开发板。上传网页文件使用Arduino IDE的“ESP32 Sketch Data Upload”工具或类似的SPIFFS上传工具将index.html,style.css,script.js上传到开发板的SPIFFS文件系统。连接测试打开串口监视器波特率115200查看启动日志。用手机或电脑搜索Wi-Fi热点BW16-Capturer密码12345678并连接。打开浏览器访问http://192.168.4.1。点击“扫描网络”应该能看到周围的Wi-Fi列表。选择一个目标网络务必是你自己有权限测试的网络点击“选择”自动填充BSSID和信道。点击“开始抓包”。此时需要有一个客户端另一部手机去连接目标Wi-Fi。如果密码正确BW16将捕获到四次握手包。状态栏的握手包计数会增加。点击“下载.cap文件”浏览器会下载一个包含握手包的数据文件。5. 常见问题与排查思路在开发和使用过程中你可能会遇到以下问题问题现象可能原因排查与解决思路编译错误找不到wifi_set_promiscuous等函数1. 开发板支持包未正确安装。2. 头文件路径或版本问题。1. 确认Arduino IDE中已正确选择BW16 (RTL8720DN)开发板。2. 检查#include是否正确引用了Ameba SDK的头文件如#include WiFi.h可能不够需要#include wifi_promiscuous.h等请参考SDK示例。3. 在Ameba的SDK路径中查找相关示例代码如PromiscuousMode对照其头文件引用。能扫描到AP但抓不到任何握手包1. 信道设置错误。2. 监控模式未成功启用。3. 回调函数注册失败或解析逻辑有误。4. 目标AP与客户端不在同一信道通信现代路由器有频宽和信道绑定。1. 确认抓包时设置的信道与目标AP的信道一致。使用扫描结果中的信道。2. 在串口日志中打印回调函数是否被触发以及接收到的帧长度、类型等信息进行调试。3. 确保有客户端正在尝试连接或已经连接目标AP。4. 尝试抓取开放网络无密码的关联包验证基础抓包功能是否正常。抓到了握手包但用Aircrack-ng等工具无法破解1. 抓取的握手包不完整缺少四次中的某一次。2. 抓包文件格式不正确。3. 密码字典不够强大。1. 检查代码中判断完整四次握手的逻辑。需要捕获到包含相同ANonce和SNonce的完整四组EAPOL-Key帧。2. 确保保存文件时按照.pcap或.cap格式写入全局文件头和每个数据包的头时间戳、捕获长度、原始长度。可以先用Wireshark打开查看是否能正确识别。3. 使用一个简单的密码如纯数字进行测试排除字典问题。网页可以打开但点击按钮无反应API请求失败1. 前端JS代码错误控制台报错。2. 后端API路由未正确定义或处理函数崩溃。3. CORS问题如果前后端分离部署时可能出现。1. 按F12打开浏览器开发者工具查看“网络(Network)”选项卡观察请求是否发出状态码是什么如404, 500。2. 查看Arduino串口日志后端是否收到请求是否有异常打印。3. 在本项目中前后端同源一般无CORS问题。检查API路径是否与前端fetch的URL完全一致。设备运行一段时间后死机或重启1. 内存泄漏频繁动态分配未释放。2. 看门狗超时长时间阻塞操作。3. 缓冲区溢出。1. 避免在抓包回调函数或Web请求处理函数中使用String类进行大量拼接优先使用字符数组和snprintf。2. 在loop()中定期调用延时或喂狗函数。3. 确保抓包缓冲区有大小限制旧数据及时覆盖或保存到文件系统。6. 最佳实践与工程建议将一个小项目打磨得更加稳定、可用需要考虑以下工程化细节内存管理BW16内存有限避免在全局或回调函数中定义大缓冲区。使用预分配的静态或全局缓冲区。抓包数据可以考虑循环缓冲区固定大小新数据覆盖旧数据直到识别到完整的握手包再将其转存到另一个区域或文件。使用SPIFFS存储抓包文件时注意文件大小定期清理旧文件。文件格式为了兼容Aircrack-ng、Hashcat等主流破解工具应将抓取的握手包保存为标准的.pcap格式或Aircrack-ng专用的.cap。这意味着你需要在文件开头写入pcap global header并为每个捕获到的数据包写入pcap packet header时间戳、捕获长度、原始长度然后才是原始的802.11帧数据包括Radiotap或Prism头部如果SDK提供的话。Realtek SDK捕获的包可能自带某种头部信息需要研究其结构。Web服务器优化使用异步Web服务器如果平台支持如AsyncTCP和ESPAsyncWebServer库的移植版避免在处理耗时请求如文件下载时阻塞其他请求。网页前端使用轮询Polling获取状态虽然简单但可以考虑升级为WebSocket以实现服务器向客户端的主动推送体验更佳。用户体验在网页上显示实时的信道信号强度图表。增加“去重”功能避免重复记录同一对AP和客户端的多次握手。提供“自动停止”选项例如在捕获到N组完整的握手包后自动停止。安全与合法性强调必须在代码和UI中体现在Web界面的显著位置添加免责声明“本工具仅用于授权下的安全测试与教育学习。未经授权攻击他人网络是非法行为。”固件中可以加入一个“授权模式”例如需要输入一个特定的管理密码才能启用抓包功能防止设备被他人滥用。扩展性思考蓝牙协同利用BW16的蓝牙功能开发手机App通过蓝牙配置抓包参数摆脱对Web热点的依赖。离线字典破解集成一个简单的密码字典如常见弱口令在设备端进行初步的匹配尝试并将结果反馈到UI。云端同步将抓到的握手包文件通过Wi-Fi上传到指定的服务器进行集中分析和破解。通过这个项目你不仅实现了一个硬件抓包工具更串联起了嵌入式开发、无线通信协议、网络编程和Web前后端等多个技术栈。从理解握手包原理到操控硬件进入监控模式再到设计一个完整的用户交互流程每一步都是对开发者能力的扎实锻炼。记住技术是双刃剑始终将它用于建设性的、合法的领域才能走得更远。