ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

嵌入式黑盒协议逆向:从物理层到单片机插桩的完整实战指南

嵌入式黑盒协议逆向:从物理层到单片机插桩的完整实战指南 在嵌入式调试生涯里有一个工作永远不缺挑战性你拿到一块完全不透明的板卡没有原理图、没有源码、没有文档唯一拥有的是两台设备之间那根可疑的通信线以及一个倔强的念头——我要知道它们之间在说什么。这不是影视剧里的黑客桥段而是嵌入式黑盒逆向的日常。黑盒通信协议逆向核心思路是从现象倒推本质顺着物理层信号一路摸到协议层语义再回到单片机层面用插桩手段验证猜测。这条链路里物理层盲猜光耦反相和单片机插桩是三个最容易卡住新手的节点也是真正拉开经验差距的地方。这篇文章不谈高深理论就按照我自己实测走过的完整路径把这套流程一步步拆开讲清楚。特别适合接手遗留项目、做设备互联、或者单纯想搞明白这破盒子到底怎么通信的嵌入式工程师。1. 拿到陌生板卡先别急着抓波形——物理层判断是逆向的第一道关口很多人做协议逆向有一个通病上来就上逻辑分析仪开抓抓到一堆波形又开始瞎猜。这是本末倒置。物理层的判断决定了你后续所有工作的方向先把这个摸清楚后面会省掉大量无用功。1.1 先从板卡外观读出接口类型把板卡翻过来先别上电。用放大镜和万用表把通信链路附近的器件认一遍这一步能过滤掉一半的错误猜测。我的习惯是先找这几类标志性器件光耦常见封装有DIP-4、DIP-8、SOP-4等丝印以6N137、PC817、TLP521、HCPL-0630等开头。看到光耦意味着这条链路大概率存在电气隔离你必须在后续测量中考虑隔离前后电平可能完全不同这个因素。RS485/RS422收发器丝印常见MAX485、SP3485、SN65HVD72等。这意味着总线上是差分信号A/B两线之间才有正确电平单端对地测量会得到完全错误的结果。CAN收发器TJA1050、MCP2551、SN65HVD230。同样是差分信号但和RS485的电平定义、物理特性、通信速率范围都不同。电平转换芯片如TXS0108、SN74LVC4245。说明两侧电压域不同可能是3.3V和5V之间的转换也可能是1.8V到3.3V的转换。这块判断为什么重要举个例子我遇到过一块板子通信线连接的是MAX485结果同事拿逻辑分析仪直接对A线对地测量抓波形怎么抓都是乱七八糟的毛刺。差分信号必须测A-B之间的差值单端测量等于是在看半个信号能看明白才怪。1.2 光耦反相问题一道大部分人都栽过的坎光耦在通信链路里的作用就是电气隔离电平转换但恰恰是这个隔离器件最容易在逆向过程中制造认知混乱。核心原因在于光耦的输出逻辑和输入逻辑不一定同相。具体要看输出端电路结构。以最常见的PC817低速光耦为例输入状态输出状态输入端有电流LED亮输出三极管导通输出端拉低输入端无电流LED灭输出三极管截止输出端由上拉电阻拉高也就是说PC817的输出和输入在逻辑上是反相的。输入为高电平时输出反而是低电平。而6N137这类高速光耦内部集成了反相器和开漏输出同样是反相逻辑。如果你不知道这一点会在解码时得到完全反过来的位序该是0的地方读到1该是1的地方读到0整帧数据怎么解都是错的。第一次踩这个坑时我花了整整一个下午去怀疑逻辑分析仪坏了。当时抓到的波形明明很规整帧头帧尾也看得像那么回事但解出来的十六进制数据就是不对CRC校验怎么都不过。后来用万用表实测光耦输入侧和输出侧的静态电平才意识到输入高电平对应的输出其实是低电平。1.3 静态电平测量法三分钟确定通信速率范围前面器件识别做得差不多后下一步就是上电测静态电平。把万用表拨到直流电压档先测通信线对地的静态电压值。这一步可以帮你快速锁定接口类型3.3V或5V左右的单端静态高电平大概率是UART、I2C、SPI、单总线这类常规数字接口。静态为0V工作时才出现电平变化可能是漏极开路输出需要外接上拉电阻或者通信是事件驱动型的不是持续有数据。差分电压约1.5V-2.5V左右多半是RS485或者CAN。用万用表量A-B之间的电压RS485空闲时A比B高约0.2V-0.5V取决于偏置电阻CAN空闲时CANH和CANL都约为2.5V差值接近0。拿到静态电平之后速率判断的经验法则普通MCU的UART常见波特率是9600、19200、115200CAN常见速率是125Kbps、250Kbps、500Kbps、1MbpsI2C则是100Kbps、400Kbps、1Mbps。先用这些候选值作为后续波形解调的起点不要一上来就猜极端速率。这块物理层盲猜的工作本质上是建立预期。带着预期去抓波形比盲目抓取再强行解释要高效得多。有了这个基础下一步才轮到逻辑分析仪上场。2. 波形捕获与波特率定位用逻辑分析仪把0和1变成能看懂的数据物理层判断完成后就可以上逻辑分析仪了。这个环节的目标是把物理层的电平变化转换成时间轴上的逻辑序列再进一步识别出波特率、帧格式和数据字节。2.1 接线与探头一个GND和一个通道就够了但两个细节必须注意很多人以为接逻辑分析仪就是把探头夹到信号线上就行。实际上有两个细节不处理好后面解码必然出幺蛾子。第一个细节逻辑分析仪的GND必须和目标板卡共地。这一点怎么强调都不过分。通道探头夹住信号线GND夹子夹住板卡的GND网络这是测量完整信号的前提。如果不共地逻辑分析仪看到的就不是信号的真实电平而是信号和地之间的浮动电位差屏幕上出现的会是莫名其妙的毛刺和漂移。第二个细节如果信号线上有光耦探头应该接哪一侧我的习惯是优先接光耦的输出侧也就是进入MCU的那一侧。为什么因为输出侧的逻辑电平通常是MCU可以直接处理的TTL电平3.3V或5V更重要的是如果光耦的输入侧来自外部接口比如长距离RS485总线那个地方的共模干扰、浪涌都可能对逻辑分析仪造成损伤。接输出侧既安全又便于后续直接在MCU侧做验证。2.2 采样率不嫌高用过采样思路避免假波形逻辑分析仪的采样率设置直接决定了你能不能看到真实的波形细节。一般原则是采样率至少是目标波特率的10倍以上我通常直接拉到20倍以上。举个例子如果你猜这条链路是115200bps的UART一个bit宽度大约是8.68微秒。10倍采样率意味着每个bit能采到10个点勉强可以判断电平20倍采样率的话每个bit就有约20个点波形还原得更准确毛刺和边沿抖动也能看清楚。但这里有一个反向问题如果你完全不知道该链路的速率怎么定采样率我的做法是从板卡上的晶振值反推。MCU旁边那颗8MHz、12MHz、16MHz的晶振都有暗示作用。比如晶振是8MHzCPU主频可能是72MHzSTM32F103常用配置那么UART波特率通常从115200、57600、38400里出CAN速率从500Kbps、250Kbps里出。带着这个预期去设置逻辑分析仪初始采样率大多数情况下能直接抓到看得懂的数据。2.3 波特率破译实操先量最小脉宽再查候选表如果波形抓到了但还不知道波特率有一个百试百灵的物理方法测量最小脉宽。所谓最小脉宽就是波形中最窄的单个高低电平持续宽度。在UART协议里这个最小脉宽通常对应一个bit的宽度。具体操作步骤在逻辑分析仪软件里把光标放到波形上量出最窄脉冲的宽度T单位微秒或纳秒。波特率约等于1/T。比如量到最小脉宽约为8.68微秒波特率就是1/0.00000868 ≈ 115200。实际测量时会有一点偏差如果是晶振分频出来的波特率偏差通常在1%以内不会影响判断。这个方法的原理很简单任何通信协议在底层都是比特流数据传输时它不可能没有bit级别的跳变。只要信号里有数据在流动就一定能量到最基本的bit宽度。量出来之后再去对照常见波特率表基本就能锁定。2.4 可疑波形辨析不是所有毛刺都是数据实际操作中波形不可能像教科书那样干净。我总结过几类常见的假波形根源供参考上拉电阻缺失导致的悬浮电平信号线上没有确定的上拉电阻或者探头接触不良波形会呈现出接近中间电压的漂浮状态。此时在信号线上加一个10K欧姆上拉电阻到VCC通常能改善。长线反射造成的振铃毛刺连接线过长超过20厘米或者用了劣质杜邦线信号边沿会产生振铃。尽量用短的屏蔽线或双绞线连接探头尽量靠近目标芯片引脚。共地不良导致的噪声前面已经说了GND夹子必须夹稳。尤其在板卡有大电流负载的情况下地线上的电压波动会叠加到信号上这时建议用一根粗短的线将逻辑分析仪的GND和板卡的地直接连接在一起。波形干净之后用逻辑分析仪自带的UART解码器跑一遍把原始bit流转成十六进制字节接下来进入最烧脑的环节协议帧结构猜解。3. 帧结构盲猜从字节流到通信协议的语法复原拿到一坨看似无序的十六进制数据之后真正考验功力的部分才刚开始。黑盒逆向协议的核心工作是从这些字节里找出帧头、帧尾、长度、地址、命令、数据、校验这些结构性元素还原出一套能解释所有观察到的通信过程的语法。3.1 帧头帧尾的识别方法违反随机性的地方就是边界任何有意义的通信协议数据都是以帧为单位组织的。帧的边界通常带有明显的非随机特征。如果你已经从UART里解出了一长串字节流我的建议是先把数据分成多段对比观察每一段的起始位置和结束位置。举一个真实案例。我逆向过一个工业仪表的通信协议数据流长这样AA 55 01 03 02 10 00 1F 34 55 AA AA 55 01 03 03 20 00 2D 44 55 AA AA 55 01 03 04 30 00 3B 54 55 AA一眼就能看出每一帧以AA 55开头以55 AA结尾这大概率就是帧头帧尾。而01、03这个位置的值在连续三帧里保持不变可能是设备地址和功能码。02、03、04这一列在递增可能是某种序号或寄存器地址。后面10 00 / 20 00 / 30 00的变化则像是数据字段。最后两位的1F 34、2D 44、3B 54看起来随数据变化极可能是校验字段。帧边界识别的核心逻辑是在随机性数据流中反复出现的固定序列就是边界的有力候选。这个原理适用于任何协议不管是UART、SPI还是CAN。3.2 关键字段猜解通过主动改变变量来锁定字段含义被动观察数据流只能帮你找出格式真正确定每个字段的含义需要主动出击。方法是操作这台设备的不同功能按键、遥控、上位机指令观察数据流里哪些字节跟着变化、变化规律是什么。拿上面那个例子来说如果我按下设备的加按键第4、5字节03 02、03 03、03 04这列跟着变化那这列很可能就是寄存器地址或者操作类型码。如果第6、7字节10 00、20 00、30 00在显示数值变化时跟着变那么它就是数据内容。这个过程叫变量隔离——通过单独改变一个输入变量观察输出的哪个字节受到影响。当年逆向一个PT2262遥控编码时就是这么做的。PT2262是51单片机时代常用的遥控编码芯片它发出的每一帧数据由同步码、地址码、数据码组成。我对遥控器上不同的按键做对比发现按A键和按B键时数据流的最后几位变化有规律逐步确认了该芯片使用三态编码来表示0、1和悬空状态从而把整个编码格式还原了出来。在这个过程中一个实用的技巧是做一张输入-输出对照表。左侧记录你做的物理操作按按键、发送指令右侧记录捕获到的数据帧。对照表列得多了帧结构自然浮现出来。3.3 校验字段识别CRC还是校验和用排除法说话校验字段是协议逆向中比较难啃的骨头。好在常用方案就那么几种用排除法可以逐步收敛。第一步判断校验长度1个字节大概率是简单校验和或者异或校验2个字节大概率是CRC16Modbus CRC、CCITT等4个字节可能有安全设计也可能是CRC32。第二步用已知工具跑一遍。常见的CRC计算工具如RevEng软件、在线计算器都支持输入数据校验值自动反推CRC参数。我的操作方式是取几组已知的数据帧把帧头到数据段的内容作为输入把最后两个字节作为目标输出让工具去反推CRC的宽度、多项式、初始值、输入输出反转配置。如果几组数据能套用同一套CRC参数那基本就破译成功了。第三步用改变一个字节观察校验变化的方法验证如果改动数据段的一个字节校验字段的两个字节都发生变化而改动帧头帧尾对校验没有影响说明校验计算的覆盖范围是从帧头之后到数据段末尾。这个覆盖范围的确定对后续自行构造帧非常重要。3.4 特殊协议类型IIC、SPI、单总线与CAN的辨识特征不是所有黑盒逆向都发生在UART链路上。I2C、SPI、CAN等总线的协议结构各有明显特征识别出来之后能大大降低猜解难度I2CIIC通信协议有SCL和SDA两根线。SCL是时钟线持续输出周期性脉冲SDA是数据线在SCL高电平期间保持稳定在SCL低电平期间变化。起始条件是SCL高电平时SDA产生下降沿停止条件是SCL高电平时SDA产生上升沿。抓到这两根线的逻辑组合再按地址字节7位从机地址读写位识别第一字节就能展开通信内容。SPI串行外设接口通常有SCK、MOSI、MISO、CS四根线。CS在通信期间拉低SCK持续输出时钟MOSI/MISO在时钟边沿采样数据。SPI的特征是通信期间CS保持低电平通信结束拉高这个片选行为在波形上非常明显。CAN总线只有CANH和CANL两根差分线波形看起来像是隐性电平2.5V附近显性电平约1.5V压差的复合。CAN的帧起始是显性电平接着是仲裁字段、控制字段、数据字段、CRC和ACK槽。CANoscope这类工具可以自动解析CAN帧但解析时需要注意先搞清楚是标准帧还是扩展帧。曼彻斯特编码每一bit中间都有跳变看起来像连续方波。这种编码常见于RFID和以太网。如果观察到波形里每个bit区间中间必有跳变那就要按特定解码规则还原原始数据不能按常规电平直接映射0和1。识别出协议类型之后接下来的工作就清晰了如果是标准协议如Modbus RTU、I2C、SPI、CAN直接参照公开文档就能理解语义如果是私有协议就要靠字段猜解和经验积累了。4. 单片机插桩验证把猜疑链终止在真实反应面前前三个阶段做完你应该已经有一套我认为协议是这样的完整假设。但假设停留在纸面上始终是不够的真正的检验手段是在单片机上插桩用自己的代码模拟这套协议看看设备是否按预期响应。这一步是整个黑盒逆向流程的闭环。4.1 为什么要插桩而不是继续在外部抓波形外部抓波形有一个本质局限你只能看到通信线缆上流过的内容看不到MCU内部对内容的处理过程。很多模糊问题是无法靠外部测量解决的比如设备收到帧后是否做了应答应答是直接回复还是经过内部状态机转换后再回复同一个命令在不同状态下行为是否不同插桩的意义在于把观察点从通信线上移到MCU内部。你可以用代码在MCU里打印关键变量的值、记录接收中断触发的时刻、甚至伪造一个从机设备来测试主机的行为。在逆向的验证阶段插桩是最高效的手段。4.2 插桩方案选择有条件接调试器没条件就做一个软件逻辑分析仪插桩的物理基础是在目标板卡上拥有可执行代码的环境。最常见的方案是目标MCU有SWD/JTAG调试接口直接用调试器读写MCU的寄存器、内存和外设。比如一块STM32板卡你完全可以用ST-Link连接SWD接口在System View或调试器里直接看USART_DR寄存器的变化甚至可以在SysTick中断里做定时捕获。但如果目标MCU没有引出调试接口这在黑盒逆向中非常常见很多工业板卡根本不引出调试引脚就需要换思路用单片机自己采集并转发数据。我做过一个纯山寨逻辑分析仪的插桩方案具体做法是这样的找一块多余的STM32最小系统板或者51单片机最小系统板频率要尽量高。把目标板卡通信线上的一路信号接到这块辅助板的GPIO输入引脚上。在辅助板上写一段捕获程序用外部中断定时器记录每一个电平跳变的时刻和方向把时间戳序列缓存到内存。利用辅助板的串口把采集到的数据发给电脑端的串口助手或Python脚本用脚本还原时序。这种做法本质上就是用辅助单片机去偷听信号然后把偷听到的内容转发出来分析。实测下来用72MHz主频的STM32F103捕获115200波特率的UART信号误差极小完全可行。甚至对于500Kbps的CAN信号用定时器输入捕获模式也能抓得比较准确。4.3 验证协议假设构造合法帧并对设备做行为测试当插桩环境就绪后验证协议假设的方法就非常直接了用宿主设备电脑或辅助单片机按你猜出的协议格式主动发送帧观察设备是否产生预期响应。拿我逆向的那个工业仪表举例。我猜它的协议是AA 55 地址 功能码 寄存器高字节 寄存器低字节 数据高字节 数据低字节 校验 55 AA且校验是Modbus CRC16。为了验证这个假设我用电脑串口按这个格式发送AA 55 01 03 00 01 00 0A 校验 55 AA意思大概是读取设备地址01的寄存器00 01的值。设备如果按这个协议通讯应该会回一帧数据。如果回帧内容符合我预期的响应格式比如包含地址、功能码、数据长度、数据内容和校验那我的协议假设基本成立。如果设备没回或回的内容完全对不上就需要回头修正假设。4.4 插桩时最容易翻车的三个细节插桩验证阶段有几个细节处理不好轻则数据乱码重则损坏目标板卡一定要特别谨慎第一共地问题又来了。辅助板和目标板必须共地但如果是隔离通信比如经过光耦隔离的总线辅助板的GND应该接在隔离侧还是非隔离侧需要想清楚。我的习惯是接在MCU那一侧也就是逻辑信号直接进入MCU引脚的那一侧这样捕获到的电平更贴近MCU实际看到的内容。第二万用表先确认电平再决定要不要加转换电路。如果目标板通信逻辑是5V而辅助板是3.3V供电直接接上去可能烧毁引脚。在不确认的情况下最简单的保护办法是串一个1K欧姆电阻做限流或者用二极管钳位电路把电平限制在3.3V。第三测试指令要从小白鼠开始别一上来就发破坏性命令。如果你从数据流里猜出了写入寄存器、复位、擦除参数这类功能码不要在验证阶段轻易尝试。先构造只读型指令测试协议的正确性确认协议完全跑通后再考虑要不要冒险测试写指令。协议逆向的目标是搞懂机制不是把设备搞坏。5. 实战复盘一个完整的光耦反相协议逆向案例为了把前面几章的内容串起来我用一个实际遇到的案例做完整复盘。这是很多读者可能会经历的典型场景一台旧设备一条三线串口一个不透明的MCU。5.1 现场情况与初始判断朋友的车间里有一台老设备MCU是STM32F103通信引脚通过PC817光耦引出到DB9接口。没有原理图没有源代码固件是加密的。需求很简单搞懂上位机下发的命令是什么意思以便迁移到新平台。接手后我没有直接上逻辑分析仪而是先做了物理层判断。DB9接口引脚测量结果2脚对5脚电压为0V3脚对5脚电压为0V7脚对5脚电压为5V。这说明DB9里的UART没有直接输出5V信号更像是RS232电平或者是悬空状态。顺藤摸瓜查到PC817输入端是DB9引过来的信号输出端接到STM32的PA10USART1_RX。用万用表测PC817输出侧静态电平发现STM32侧的PA10在没有通信时是3.3V高电平这个状态是对的——光耦输出端截止时由上拉电阻拉到VCC。5.2 抓波与反相修正的过程逻辑分析仪接在PC817输出侧与GND之间抓了一轮上位机发起通信的数据。第一次解出来的字节流乱七八糟完全看不出来帧结构。排查到最后发现我把光耦输出侧的波形直接按高1低0来解码了。由于PC817反相实际波形里的高电平对应的是无信号空闲态低电平反而是有效信号数据位。看清楚这一点后我在逻辑分析仪软件里对波形做了反相处理再重新解码数据立刻变成了可理解的帧AA 55 01 06 00 64 00 00 01 21 CB 55 AA开头AA 55和结尾55 AA是帧头帧尾01是设备地址06是功能码从数据位来看这是一条写入单个寄存器命令。00 64是寄存器地址十进制10000 00是要写入的值后面的01 21 CB是校验字段。5.3 校验反推与最终验证我拿了几组不同数据的帧做CRC校验反推。把AA 55之前的帧头去掉从01开始到00 00作为数据段后面的01 21 CB作为校验值用RevEng工具反推。结果是Modbus CRC16而且计算范围是从设备地址字段开始到数据字段结束帧头帧尾不参与校验。为了验证这个结论我用电脑串口发送自己构造的帧AA 55 01 06 00 64 FF FF 校验 55 AA意思是将寄存器100的值改为0xFFFF。结果是设备正常响应了一帧AA 55 01 06 00 64 FF FF 校验 55 AA的回显帧和新参数下设备的行为完全吻合。至此协议逆向闭环完成整套语义被彻底还原。这个案例能成功关键不在于用了多先进的设备而在于严格走完了物理层判断→波形捕获→帧结构猜解→插桩验证这条完整链路。每一个环节的经验——光耦反相、最小脉宽测量、字段变量隔离、CRC反推、辅助单片机做软件逻辑分析仪——都在里面派上了用场。做黑盒协议逆向最大的错觉是我应该一步到位抓到全部真相。实际上它更像拼图每一块证据单独看都不显眼但它们合在一起时协议的全貌就自然浮现出来了。遇到解不动的数据流时我最常做的事情反而是把逻辑分析仪拔掉坐回去把前面的波形重新翻一遍往往能发现当初忽略的细节。这种逆向思维的耐性可能比任何工具都重要。
返回列表