ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

GreatFET One实战:从USB抓包到HID模拟与安全研究

GreatFET One实战:从USB抓包到HID模拟与安全研究 如果你手头有一块GreatFET One又对USB协议层那些事感兴趣那你大概率会和我一样在插上板子的第一周就把官方文档来回翻了好几遍。这块板子看起来不炫但它是少数能把USB抓包、设备模拟、协议调试甚至安全研究里的USB攻击测试都串在一起的开发平台。我最初只是拿它当“高级逻辑分析仪”用结果后来越玩越深把USB协议从枚举到事务传输捋了个遍也顺手折腾了不少和安全研究相关的实验。这篇文章不是官方教程的复述而是我个人折腾GreatFET的一手记录。我会先讲清楚为什么选它然后从环境搭建、USB抓包、HID模拟、中间人代理到非USB玩法逐个展开。适合那些有一定嵌入式或协议基础、想真正搞懂USB设备通信或者在做USB安全测试的朋友参考。我尽量把坑点写明白让你少走弯路。1. 为什么是GreatFET而不是随便买块开发板1.1 USB安全研究的门槛在哪做USB协议研究你面对的第一个问题是普通电脑根本看不到USB总线上实际传输的信号。你用鼠标在屏幕上滑动主机和鼠标之间交换了什么数据包Windows、Linux都不会把这些信息直接暴露给你。想要分析要么在USB协议栈里加钩子要么在物理链路上接入一个抓包器。前者需要改驱动或内核后者则需要趁手的硬件。第二个问题是你想模拟一个USB设备时普通MCU要你自己写完整的USB设备栈。STM32那种带USB外设的芯片还行但处理低速、全速、高速的差异处理各种描述符请求和class协议工作量不小。如果只是为了验证一个想法比如“模拟键盘发送按键”“模拟U盘让主机加载镜像”用现成工具能省下大量时间。第三个问题是很多USB安全问题出在协议实现层面需要你在枚举阶段、控制传输阶段插入自己的逻辑。这要求工具不仅能“看到包”还能“假装成设备”去响应主机请求。逻辑分析仪看不到设备内部的处理逻辑普通开发板又难以灵活模拟各种设备类型。GreatFET刚好把这几个能力结合在了一起。1.2 GreatFET One 的核心能力一览GreatFET One是一块基于NXP LPC4330双核Cortex-M4/M0处理器的开发板板上还带了一片FPGA用于处理高速采样和时序密集型任务。它最大的特色不是某一项性能突出而是接口丰富得过分随手列一下功能说明USB Device板子作为USB外设连接电脑通过USB枚举与上位机通信USB Sniffing配合逻辑分析能力可以被动采集USB D/D-信号并还原为包USB Emulation在Python层面模拟USB键盘、鼠标、U盘等设备GPIO十几路可编程IO支持输入输出、中断UART / SPI / I2C常用串行协议调试接口ADC / DAC模拟量采集和输出Logic Analyzer多通道数字采样可用于UART/I2C/SPI/USB协议解码这相当于把“USB协议分析仪 USB设备模拟器 多协议调试器”塞进了一个巴掌大的板子里而且全部有Python API不用写一行C代码就能跑起来。对安全研究和协议调试来说这个“什么都能干一点”的定位反而最实用。1.3 与常见工具的定位差异很多朋友会拿Hak5 Rubber Ducky和Facedancer来和它对比。Rubber Ducky本质是一个预编程的HID键盘模拟器插上就能执行按键脚本但它只能做键盘模拟不能抓包也不能模拟其他USB设备类。Facedancer也就是GoodFET系专注USB设备仿真和中间人抓包能力强但通用IO和协议调试能力很弱。GreatFET更像是两者的超集它保留了Facedancer的USB仿真能力又把逻辑分析、GPIO这些调试功能集成进来。我自己的体会是如果你只想“一键演示BadUSB”Rubber Ducky更省事如果你想搞懂USB协议、想复现设备枚举过程、想跑轻量级fuzzingGreatFET才是能陪你走得更远的工具。它不追求即插即用的傻瓜化而是把底层能力都暴露给你这也是它适合研究者的原因。2. 环境搭建最容易踩的四个坑2.1 固件烧录从零让板子跑起来GreatFET One拿到手第一件事是烧固件。官方仓库里有编译好的固件镜像但在Windows和Linux上烧录方式略有区别。我当时的操作流程是按住板子上的BOOT按钮再插入USB线进入系统固件更新模式。使用dfu-util烧录编译好的greatfet_usb.bin。重新插拔USB线板子会枚举为GreatFET设备。sudo dfu-util -a 0 -d 1d50:6086 -D build/greatfet_usb.bin这里有个坑不同版本的固件可能要求不同的dfu-util参数最好从官方README确认当前命令。我第一次烧录时用了旧的dfu-util结果提示Device has DFU interface, but has DFU functional descriptor换了新版才成功。如果你在Windows上还需要用Zadig把驱动换成WinUSB否则上位机无法识别。2.2 与host通信GreatFET API和Python绑定固件跑起来之后需要安装Python库pip install greatfet greatfet status如果一切正常你会看到固件版本、序列号等信息。greatfet status是我最常用的自检命令每次连不上板子先跑它能快速确认是驱动问题还是固件问题。Python API的使用方式很简单核心是创建一个GreatFET对象然后调用对应方法。比如读取GPIOimport greatfet gf greatfet.GreatFET() print(gf.supply_voltages())API的名称和参数在不同固件版本里可能会有调整所以遇到报错先查一下greatfet库的__version__和固件版本是否对应。我踩过的第一个坑就是固件是旧版Python库却升到了新版结果调用逻辑分析功能时枚举参数对不上。2.3 驱动/权限问题在Linux下如果greatfet status提示找不到设备多半是权限或udev规则问题。需要添加一个udev规则让普通用户也能访问USB设备SUBSYSTEMusb, ATTR{idVendor}1d50, ATTR{idProduct}6086, MODE0666保存到/etc/udev/rules.d/50-greatfet.rules然后执行sudo udevadm control --reload。Windows下则相对简单Zadig把设备驱动换成WinUSB即可。但要注意如果以前装过其他USB驱动软件设备管理器里可能残留旧驱动导致Zadig识别不到端口。遇到这种情况先把旧驱动卸载干净再重装。2.4 固件版本与Python库版本匹配这个是最隐蔽的坑。GreatFET的固件和上位机库是分开发布的两者有严格的版本对应关系。官方README里一般会写明“Python library x.y.z requires firmware build date 2023xxxx”。如果版本不匹配常见的症状是greatfet status能显示设备但调用某个API时报AttributeError抓包时数据不完整或完全是错的模拟USB设备时主机识别不了我后来养成了一个习惯每次更新板子固件顺手把pip install --upgrade greatfet也跑一遍。两个保持在较新的版本问题会少很多。3. 第一个实验把GreatFET变成USB逻辑分析仪3.1 USB协议抓包的基本原理USB 2.0的数据线是D和D-两根差分线。低速设备1.5Mbps在D-上拉全速设备12Mbps在D上拉高速设备480Mbps初始也是全速握手再切换。想抓这些信号最直接的办法是把逻辑分析仪探头接到D、D-和GND上以足够高的采样率记录电平变化然后在软件里解码出差分信号对应的USB包。GreatFET的逻辑分析能力在这里就派上用场了。它不需要像USB分析仪那样内置协议引擎而是把原始采样数据传回电脑由软件比如sigrok或Wireshark做协议解码。这样做的优点是灵活你还能看到包与包之间的电平细节缺点是数据量很大采样时间太长容易内存爆炸。所以抓USB时一般用触发条件先过滤掉无关数据。3.2 实操抓取一个USB鼠标的通信我实际的抓法是这样的拆开一个USB鼠标外壳把D、D-、GND三根线引出来分别接到GreatFET的三个GPIO输入上。然后配置逻辑分析仪采样率为24MSps触发条件设置为捕获D第一次拉高的上升沿也就是USB设备插入的时刻。import greatfet from greatfet import GreatFET from greatfet.logic import LogicAnalyzer gf GreatFET() la LogicAnalyzer(gf, sample_rate24e6, num_samples24000) la.trigger(channel0, edgerising) data la.capture()这里的关键是采样率不要低于USB数据传输速率的10倍。12Mbps的全速USB最少也得给到24MSps否则恢复出来的波形很难看解码误码率也会高。抓完的数据可以导出为VCD或者直接交给sigrok解码。我一般先导出成二进制流再把解码结果保存为pcap方便后续用Wireshark看。3.3 用Wireshark分析USB流量Wireshark本身支持USB协议分析但前提是你得把抓到的原始信号转成它认识的格式。我的做法是用pyusb和sigrok-cli做转换链路也可以直接在GreatFET的Python API里调用现成的解码函数。大致流程用sigrok-cli -i capture.sr -a usb_parsing解码USB包导出为pcap文件Wireshark打开pcap选择usb过滤规则Wireshark里能看到非常清晰的枚举过程主机发GET_DESCRIPTOR请求设备响应设备描述符然后是SET_ADDRESS再请求配置描述符。之后鼠标移动事件会表现为URB_INTERRUPT in里面是一段HID报告。3.4 抓包结果解读从枚举到HID报告第一次抓到完整鼠标流量时你会发现自己对USB协议的理解瞬间立体了。举个例子枚举阶段的一小段交互主机发送GET_DESCRIPTOR请求设备描述符bmRequestType0x80, bRequest0x06设备返回18字节设备描述符包含idVendor、idProduct、bcdUSB等信息主机发送SET_ADDRESS把设备地址从0改成1之后所有通信都使用新的设备地址鼠标运动时中断端点上会周期性出现4字节HID报告。对于标准三键鼠标报告格式通常是第0字节是按键掩码、第1-2字节是X/Y位移、第3字节是滚轮。有了这些信息你再看“BadUSB为什么防不住”就很容易理解主机对USB键盘的信任是建立在设备描述符上的而描述符完全由设备自己声明。如果你模拟一个PID/VID跟普通键盘一样的设备主机根本没有办法从物理上区分它是不是真正的键盘。4. BadUSB实验从HID模拟到实战防御4.1 什么是BadUSB/HID键盘模拟BadUSB这个概念最早来自2014年的安全会议核心思想是USB设备可以通过固件把自己枚举成键盘然后向主机发送预先编排好的按键指令。因为主机只认“这是一个标准HID键盘”所以这些按键会被当成用户输入来执行。在安全测试里这类攻击通常用来验证终端的USB外设管控是否有效、杀毒软件能不能拦截基于键盘输入的投放链。我需要说清楚一点这篇文章提到的攻击实验只允许在你自己控制的电脑、虚拟机或获得书面授权的测试环境里做。未经授权去动别人的机器是违法行为。4.2 用GreatFET实现HID键盘模拟我在GreatFET上做HID模拟用的是官方Python接口。思路很简单把GreatFET配置为一个USB HID设备然后通过发送HID报告来模拟按键。import greatfet from greatfet.usb import USBDevice gf greatfet.GreatFET() dev USBDevice(gf) # 将设备配置为HID键盘类 dev.set_descriptor(device_class0x03, subclass0x01, protocol0x01) # 发送一次按键: 按下a键然后释放 hid_report [0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00] dev.control_transfer(0x21, 0x09, 0x0200, 0, hid_report) time.sleep(0.05) dev.control_transfer(0x21, 0x09, 0x0200, 0, [0]*8)这里的0x04是USB HID键盘字母“a”的键码。实际测试中你可以先发一个记事本打开命令再输入一段文字观察是否被执行。第一次实验我建议用虚拟机这样可以随时快照回滚避免把宿主机弄崩。4.3 扩展模拟鼠标、组合按键和Ducky脚本键盘模拟只是起点。模拟鼠标也很简单把HID报告格式从键盘改成鼠标然后发送位移和按钮数据。组合按键则稍微复杂一点需要同时设置修饰键和主键比如CTRLESC打开开始菜单# 按下 Win 键 R hid_report [0x08, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00] # RCTRL dev.write_interrupt(0x01, hid_report) hid_report [0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00] # R dev.write_interrupt(0x01, hid_report) time.sleep(0.1) # 释放全部按键 dev.write_interrupt(0x01, [0]*8)Hak5的Ducky脚本用文本描述按键序列如果你想让GreatFET执行Ducky脚本可以自己写一个简单的解析器把STRING,DELAY,GUI r这些指令翻译成HID报告序列。这个工作量不大但能省掉很多手指重复劳动。4.4 如何检测与防御BadUSB研究攻击手段最终还是要落到防御上。我自己在测试环境里用过几种检测思路看枚举过程用Wireshark抓USB流量如果看到一个设备枚举为键盘但它的PID/VID不是常见键盘厂商就要警惕。看HID报告频率正常键盘的输入事件是稀疏的BadUSB在短时间内会发送大量按下/释放操作特征非常明显。使用USB白名单在可控网关上配置设备黑/白名单只允许已登记的PID/VID设备使用。使用USB隔离器或过滤驱动在系统层面对HID设备的输入速率做限制或者拦截可疑组合键。防御没有银弹但把“枚举特征 HID报告频率 设备白名单”三层叠加起来至少能挡住绝大多数无脑BadUSB。5. 进阶USB中间人、Fuzzing和更多玩法5.1 用GreatFET做USB中间人代理USB中间人的原理是在物理上把目标设备插入GreatFET的USB Host口GreatFET再通过自己的Device口连接电脑。上位机软件会把主机发出的所有USB请求转发给目标设备再把设备响应返回给主机。这样做的好处是你可以在转发过程中修改请求和响应实现“中间人篡改”。我当时做的一个实验是中间人拦截了一个USB键盘的HID报告把原有的字母键替换成另一个键。具体做法是在转发HID报告之前检查报告字节如果是目标按键码就改写成新的键码。这个实验能直观展示“协议层面修改数据的可能性”同时也说明了为什么USB设备的端到端信任在安全领域如此重要。实现中间人需要把GreatFET的Host和Device两个角色同时用起来逻辑比单纯模拟HID要复杂。建议先用官方仓库的facedancer示例跑通基础转发再逐步加入篡改逻辑。5.2 轻量级USB fuzzing发送畸形描述符和控制请求USB fuzzing是安全研究里很重要的一个方向目标是检测主机端USB协议栈对异常描述的容忍度。GreatFET可以扮演一个“恶意设备”向主机发送各种畸形请求超长字符串描述符不合法的配置描述符长度突然重置总线在枚举过程中随机延迟响应我的做法是写一个Python脚本循环生成不同组合的描述符每次控制传输结束后观察主机是否蓝屏、卡死、或产生异常日志。在Windows虚拟机里跑这个实验特别有效因为Windows对USB描述符的解析比较严格稍微出格的数据就可能触发错误路径。for length in range(0, 64): desc bytes([length, 0x03] [ord(A)]*length) try: dev.control_transfer(0x80, 0x06, 0x0340, 0, length) except Exception as e: print(fError with length {length}: {e})这个实验强烈建议放在虚拟机里做而且先快照。一旦把宿主机的USB协议栈搞挂恢复起来很麻烦。5.3 脱离USBSPI、I2C、UART、ADC和GPIO扩展GreatFET的“More”不止在USB领域。它的GPIO和串行接口扩展能力让它可以顺手充当一个多协议调试器。我常用它来读取SPI Flash这在进行固件逆向时特别方便。接线就很直接把SPI Flash的CS、CLK、MOSI、MISO分别连到GreatFET的对应引脚上然后用Python API拉取Flash内容。from greatfet import GreatFET from greatfet.peripherals.spi import SPIBus gf GreatFET() spi SPIBus(gf, controller0, cs0) # 读取JEDEC ID jedec_id spi.read(3) print(Flash ID:, jedec_id.hex())网上很多硬件逆向教程会教你先拆芯片再编程器离线读但GreatFET能直接在板上接探针读取省去了拆焊的风险。类似地你也可以用它做I2C传感器读取、UART日志监控、ADC电压测量等于在了一个微型示波器/逻辑分析仪/编程器。5.4 综合实例用GreatFET模拟U盘模拟U盘这个实验挺有意思它把USB Mass Storage类协议和实际场景结合起来。GreatFET在USB Device模式下可以把自己模拟成一个只有几MB空间的U盘并在内存里动态生成FAT文件系统镜像。主机插上后会看到一个普通的可移动磁盘里面可以写出文件也可以读取预设内容。我在实际测试中用它做了“自动执行payload”的理论验证插入设备后让U盘枚举为CD-ROM和键盘的组合。这类设备在物理世界中经常被用于社会工程学演练但在GreatFET上你可以先用PythonAPI逐步构造这类复合设备观察主机的枚举行为。整个过程都是在受控环境中完成的重点是观察协议交互而不是实际攻击。6. 实际操作中的经验与后续扩展6.1 电源与电平问题GreatFET板载的USB口供电能力有限。当你通过Host口连接外部设备时如果那个设备功耗较大比如USB无线网卡、移动硬盘可能会出现供电不足导致设备反复枚举。我遇到过一次连接一个USB蓝牙适配器时每次枚举到一半就掉线后来发现是电流不够。解决办法是用带外部供电的USB Hub给设备供电同时把GreatFET的Host口改成不供电的模式。电平方面GreatFET的IO是3.3V逻辑电平和5V TTL设备直接相连有可能烧板子。和5V设备通信时需要加电平转换模块或者确认对方IO口能容忍3.3V输入。6.2 协议分析时的时序设置用逻辑分析仪抓USB信号时采样率决定了你能看到多细节的信号完整性。24MSps能看清全速USB的大部分位流但如果遇到信号质量差、边沿缓的情况建议切换到40MSps以上。另一个关键是触发通道USB插入瞬间D从低到高的上升沿是一个天然触发点能让抓包窗口对准枚举过程不会抓到一大堆空闲总线噪声。6.3 踩坑记录USB枚举失败排查有一次我模拟HID键盘主机一直不识别设备管理器里显示“未知设备”。排查过程很有意思先检查了D/D-的接线确认没有接反。再用逻辑分析仪抓枚举过程发现主机发了很多GET_DESCRIPTOR但设备响应不正确。最后定位到问题我配置描述符里的bMaxPacketSize0写错为64但实际端点端点0的最大包长只有16导致主机解析失败。USB枚举失败的常见原因无非这么几类描述符错误、地址设置失败、端点配置不正确、电源问题。通过抓包逐个排除比瞎子摸象高效得多。这也是我为什么强调“先把抓包跑通再研究攻击”的原因。6.4 后续还可以怎么玩GreatFET的能力远不止我上面写的这些。你还可以用它的ADC做简单的信号测量后续配合Python做数据分析。把逻辑分析仪和GPIO联动实现自动化协议验证。结合scapy或pyserial做更复杂的协议fuzzing框架。利用它的USB Host能力做“USB防火墙”原型过滤特定PID/VID设备。我个人最推荐的路线是先照着抓包实验把USB协议读明白然后对枚举流程做一次完整的手工复现接着再去碰模拟设备和中间人。基础打牢了后续研究攻击手法或者做协议开发都会顺手很多。折腾这么一圈之后最大的体会是USB并没有想象中那么神秘但也不是看几篇博客就能掌握的。真正让你理解协议的是你亲手抓到一堆数据包、亲手改坏一个描述符、又亲手把它修好的过程。GreatFET恰好给了你一个足够低的入口和足够深的扩展空间剩下的就看你愿不愿意花时间钻进去了。
返回列表