拓冰建站拓冰建站
首页 / 资讯中心 / 正文

嵌入式黑盒协议逆向实战:UART波形分析、光耦反相与单片机插桩全流程

1. 项目概述与场景定位1.1 这个指南到底解决什么问题嵌入式黑盒通信协议逆向说白了就是你手里有个未知设备不知道它的通信协议是什么但你必须跟它对话或者要把它的通信逻辑摸清楚。这在实际工作中非常常见。我接过不少类似的需求举几个典型场景设备供应商倒闭了源码头文件没留全只拿到一块跑着旧固件的板子你需要让它跟新的上位机通信。手里的工控仪表或者传感器只知道是某种串口输出但数据格式完全不透明帧头帧尾、校验方式、字节序全得靠猜。更狠的是有些设备壳子上写着协议不公开厂家压根不给你任何文档但项目要求你必须接入它的总线。还有一种是老式设备的维护性逆向设备还在产线上跑你不敢动它内部代码只能通过外部观测去理解它的通信行为。我做这个项目的时候目标设备是一块非常朴实的工业控制板外部引出几根线内部有一个未知的MCU我方拿到的资料只有一张原理图的复印件而且复印件模糊得连芯片丝印都看不清。通信线上没有标注任何信号定义只知道电源、地、还有三根看起来像是通信线的引脚。这种情况下任何正规的代码审计路线都走不通因为根本没有源码给你审。我能依赖的只有三件事示波器和逻辑分析仪、对差分信号和串行协议的敏感度、以及一块自己写好了抓包固件的单片机。项目做完之后我把整个流程整理成了这套方法论物理层盲猜、光耦反相、单片机插桩。这三个阶段分别解决信号是什么样的、逻辑对不对、数据含义是什么三个层面的问题。这篇博文就是记录完整过程适合那些要接未知设备、做设备兼容、或者纯粹想搞明白手里这块板子在说什么的嵌入式工程师。1.2 黑盒逆向的三个层次黑盒逆向的全局思维很重要。别一上来就拿着逻辑分析仪乱点先搞清楚自己在哪个层次工作。我把整个逆向过程拆成三层物理层逆向解决的是电气特征问题。信号幅度是多少伏是TTL电平还是RS232电平还是RS485差分波特率大致在什么范围空闲电平是高还是低一帧数据的起始位是低电平还是高电平。这一层的大部分信息可以靠示波器完成不需要动任何逻辑分析。数据链路层逆向解决的是帧结构问题。知道bit怎么发了之后要判断有没有起始位和停止位有没有校验位是8位数据还是9位数据多个字节之间有没有帧头帧尾是定时发送还是事件触发两帧之间隔多少时间。应用层逆向解决的是语义问题。这一层的信息量最大难度也最高。某个字节是长度、是地址、是命令还是纯数据校验和是累加、异或还是CRC长度字段包含哪些字节多字节数值是大端还是小端什么情况下设备会回复、什么情况下设备会沉默这三个层次不是严格串行的实际工作中会来回跳。比如你发现数据链路层判断的波特率有偏差就得回到物理层重新量一遍。但整体路线一定是先物理、再链路、最后语义。我见过太多人栽在第一步。买了逻辑分析仪直接往通信线上夹看到一堆波形就开始猜协议结果连TTL和RS232电平都没分清前面所有分析全部白费。所以这套指南的第一个重点就是教你怎么在看不见协议的情况下先用物理层的信息把方向定死。2. 物理层盲猜从示波器波形到通信参数的确定2.1 电压域判断先搞清楚是TTL还是RS232拿到未知设备的三根线第一步不是接逻辑分析仪是拿出万用表和示波器先测电压。把示波器探头接地夹子夹到公共地探头点到怀疑是TX的线上观察静态电平。这里有一个非常实用的经验法则如果静态电平是3.3V或者5V大概率是TTL电平空闲为高。如果静态电平是负电压比如-6V到-12V之间那是RS232电平因为RS232规定逻辑1对应负电压逻辑0对应正电压空闲状态就是负电压。如果静态电平在0V附近但通信时总线有跳变且跳变幅度很大那可能是RS485的差分信号此时你需要的是差分探头或者用两个普通探头做A-B数学运算。我遇到的那块板子第一眼看到空闲电平是约3.3V正常。但把示波器时间轴拉长之后发现这个3.3V在通信时会掉到接近1V而且掉电的幅度不像是数字信号。后来用万用表量了对地电阻发现这根线内部有上拉到3.3V的电阻但外部驱动源是开漏结构把电平拉低了。这个细节非常重要。如果你只是看静态电平就判断它是普通TTL推挽输出后面抓到的波形可能全部是错误的。开漏输出有一个特点上升沿会变得比较缓下降沿很陡。因为上升沿靠上拉电阻充电充电时间常数取决于电阻和寄生电容下降沿是驱动管直接拉低速度很快。区分推挽和开漏的意义在于推挽输出信号质量好可以直接接逻辑分析仪开漏输出则需要加上拉电阻才能获得可靠的逻辑电平。如果设备板载上拉已经接好你测到的波形是完整的。如果设备设计者偷懒没接上拉你测到的上升沿会成一团浆糊这时候千万别急着判协议问题先补一个外部上拉电阻再试。上拉电阻的选值也有讲究我一般先试4.7k欧姆不行再换1k。太小了会增加功耗而且可能带不动太大了上升沿时间过长容易误判波特率。2.2 波特率盲测示波器的横轴就是答案物理层判断完之后下一步就是测波特率。这一步核心思想在于别用逻辑分析仪自动识别先用示波器手动量最短脉冲宽度。为什么强调手动量因为逻辑分析仪的自动解码功能依赖正确的协议设置。你不知道协议格式自动识别经常出错尤其遇到非标准的波特率误差时。具体做法示波器时间轴调到每一格20微秒到50微秒触发模式设为单次或者正常触发边沿设为下降沿因为UART空闲是高起始位是低下降沿正好对应起始位开头。等到捕捉到一帧完整波形后放大波形找一个最短的脉冲测量它的宽度。这里需要一点通信协议的基础知识。标准UART的最小脉冲宽度就是1个bit的时间。比如波特率96001bit时间是104.2微秒波特率1152001bit时间是8.68微秒。你量到最短脉冲宽度算倒数基本就是波特率。但是有一个坑如果数据帧里恰好全是0xAA或者0x55这种交替bit模式最短脉冲确实就是1bit。但如果数据恰好是0x7F这种连续1很多的字节你可能找不到1bit宽的脉冲。这时候有一个更靠谱的方法测起始位到第一个停止位的总时长再除以总bit数。举个例子假设测得起始位到第一个停止位是104微秒假设是8N1格式8数据位无校验1停止位总共10bit那波特率就是10/104us约96153可以判定为9600。如果实在抓不到完整帧还可以换个思路把时间轴拉长统计单位时间内有多少个下降沿再结合可能的协议格式推断波特率范围。这个方法精度低但能确定数量级比如确定是几K还是几十K还是几百K再往下精调就快了。2.3 判断信号流向TX和RX怎么找物理层的另一个关键问题怎么判断三根线里哪一根是设备的发送线哪一根是接收线大多数情况下通信系统里安静的线不一定是RX也可能是没接的悬空引脚。我的判断顺序是这样的先测静态电压。发送线在设备上电后通常有明确电平比如TTL的3.3V而接收线如果外部没有驱动可能被内部上拉或者下拉到固定电位也可能浮空电压不稳定。优先怀疑电压不稳的那根是RX。再用示波器看信号活动。设备上电后如果周期性往外发数据TX线上能看到规律波形。如果设备只在收到指令才回复那TX线在初始状态下是安静的不容易直接看到。这时候需要触发抓包或者给设备一个外部激励。还有一种方法是用万用表电流挡串进去观察电流方向这个方法比较粗暴有烧毁风险不建议新手尝试。如果设备支持主动发数据比如上电发送日志或者心跳那TX就非常好找。我那个项目里设备上电后大约每500毫秒发一帧7字节的数据示波器一夹就看到清晰的周期波形TX确认得无比轻松。RX的确认稍微绕一点我直接把疑似RX的线手动接地观察设备行为。如果设备等待指令时突然不发了或者开始发错误帧那说明这根线确实是RX。这个方法虽然粗暴但很有效。2.4 补充判断RS485和CAN的识别现代工业设备里RS485和CAN太常见了这里补充一下怎么区分。RS485是差分信号用A和B两根线空闲时A对B为正电压一般2V以上逻辑1是AB逻辑0是AB。如果你看到两根线静态电压都在2.5V附近晃动且对地电压不是标准的0V或3.3V大概率是RS485。用示波器数学通道A-B就能直接解出差分波形。如果不想用数学通道就看两根线波形是否完全相反而且跳变沿时间几乎一致这也能辅助判断。CAN总线则是显性/隐性电平静态是隐性电平CANH和CANL都约2.5V显性时CANH拉高约1VCANL拉低约1V差分电压约2V。它跟RS485最大的区别是CAN是单总线所有节点都挂在同一条总线上没有严格的主从RX/TX之分。而且CAN帧结构特殊有SOF、仲裁场、CRC等波形上看会有比较长的连续显性位。对于刚接触黑盒逆向的工程师我建议在物理层先用示波器把这是什么类型的电气信号这个问题彻底解决再用逻辑分析仪去抓数据。跳过物理层直接上逻辑分析仪后面所有解码都可能是空中楼阁。3. 光耦反相问题一个容易被忽视的逻辑陷阱3.1 为什么光耦会导致反相项目做到中间阶段我遇到了一个非常典型的问题设备对外接口经过了一个光耦隔离而光耦的输出跟输入是反相的。可能有读者要问了光耦反相不是硬件上很简单的事吗输出端加个反相器不就解决了问题在于很多设备为了省一个三极管或者反相器芯片直接用光耦的输出端去驱动下一级电路不考虑逻辑极性。这在某些场景下没问题因为对端如果也是开漏或者光耦两次反相负负得正。但如果对端是一颗普通的UART外设那它收到的信号比特极性就是反的每一位都取反整个帧就废了。从波形上怎么发现光耦反相先记住正常UART波形的样子空闲高电平起始位是低电平然后8个数据位从LSB到MSB最后停止位回到高电平。如果数据是0x55也就是01010101LSB先发波形上看到的是10101010即高低交替。光耦反相后会发生什么空闲电平变成低起始位变成高数据位全部取反停止位变成低。最直观的特征是你看到一个帧空闲电平是0V起始位却向上跳变到高电平然后跟着一串乱七八糟的位最后的停止位不是回到空闲电平而是变低。用肉眼识别反相帧最典型的特征就是电平极性与标准UART完全倒置。你如果拿逻辑分析仪去解码会解出全是0xFF、0x00之类的奇怪数据而且校验永远不对。3.2 如何在抓包阶段识别反向帧我的项目里光耦反相问题隐蔽在了一个更深的层次因为设备本身处理的是反相信号它知道自己要输出反相的数据所以它对固件的配置跟你对逻辑分析仪的解码设置是相反的。这意味着什么你用逻辑分析仪设成标准UART格式去解它吐出来的波形解出来的数据和它真实想要表达的数据是逐位取反的关系。识别方法总结如下第一看空闲电平。正常UART空闲高逻辑1如果抓到的线上空闲电压是0V而通信时信号跳变到高电平先怀疑反相或者逻辑极性设置问题。第二看起始位和停止位极性。起始位一定跟空闲电平相反停止位一定跟空闲电平相同。如果起始位从低到高停止位也是低那这帧的极性跟标准UART是反的。第三看已知特征字节。比如很多协议有固定帧头典型的是0xAA、0x55、0x5A、0xA5。这些字节都有明显的对称性0x55取反是0xAA0xA5取反是0x5A。如果你从波形里看到取反后的帧头刚好匹配已知协议帧头那就证实了反相的存在。我实际遇到的那个设备它发给我的数据经过光耦后我解出来的第一个字节是0x7A我猜它可能是0x85的取反又试着把0x7A取反得到0x85。0x85在十六进制里不是常见的帧头于是我又翻了设备历史固件的反汇编片段发现它在发送前确实做了一次取反操作。这个案例说明光耦反相在代码层面往往对应一个看似多余的按位取反操作。3.3 光耦反相的修正方案确定了反相之后接下来的问题是怎么把数据修正成正常格式深入一点后你还会面临物理层逻辑与数据链路层的协调。修正方案有几种在逻辑分析仪里设置反向极性。Saleae Logic的逻辑分析仪在UART解码器里有一个信号极性选项可以选Active Low或者Inverted选对之后解码就是正常的。这适合快速验证但不适合当作长期方案因为你没法通过反向解码继续推进更深层协议分析。在硬件层面解决在线路上加一个反相器用74HC04或者三极管接成共射极反相器。这一步的好处是后续所有工具链都不用改变逻辑分析仪、单片机插桩全部按常规设置。缺点是需要动硬件线多了容易接错。代码层面解决写插桩程序读取原始电平值然后按位取反再进协议解析。这个方法最灵活适合在单片机插桩阶段综合处理。我建议的顺序先用逻辑分析仪的反向选项快速确认反相帧里是否藏着有效协议数据然后决定要不要在接入单片机插桩时做软件反相。有一点一定要记住光耦不仅反相还会改变信号的边沿速度。光耦的输入侧LED有一个导通压降输出侧三极管有一个饱和导通延迟整体会让信号上升沿变缓下降沿也可能变缓。对于波特率不高的协议影响不大但对于1Mbps以上的高速UART光耦的延迟可能导致波特率偏高的帧直接解错。如果遇到高速协议还带着光耦先考虑光耦的传播延迟参数是否够用。4. 单片机插桩用一颗MCU做协议分析仪4.1 为什么不用现成逻辑分析仪而要自己插桩逻辑分析仪在物理层盲猜阶段非常好用但到了协议语义分析阶段它的局限性就暴露了。第一逻辑分析仪只能看到电平翻转很难在长时间尺度上做内容比对。我要的是设备发了一帧数据我回了什么它又回了什么这种交互式分析这需要程序化的逻辑判断。第二部分协议是双向同时通信的比如全双工UART或者SPI。逻辑分析仪能同时采集多通道但它不会替你解析这一条回复对应刚才哪条请求。第三也是最重要的一点我需要给设备构造恶意或者异常的输入看它怎么反应。逻辑分析仪没有发送功能不能主动干扰通信过程。所以到了这个阶段我选择用一颗现成的单片机做插桩分析仪。它既能采集又能发送还能做协议逻辑分析。我的选择是STM32系列因为它的定时器资源丰富、串口外设多、而且调试工具成熟。你手头如果有ESP32也可以但它引脚的输入捕获精度和定时器的灵活性不如STM32好用。单片机插桩的核心思路把目标设备的TX/RX接入单片机的两个引脚单片机用GPIO中断或者定时器捕获的方式记录每个电平跳变的时间戳然后把时间戳序列转换成bit流再按协议格式解析。这个单片机本身不跑业务逻辑只做线路侦听和协议注入。4.2 插桩的硬件连接和信号保护插桩硬件的连接其实不复杂但有一个核心注意事项电平匹配。3.3V单片机去采集5V TTL设备信号建议加一个电平转换电路或者用分压电阻。我一般直接用一个1k和2k的电阻分压把5V降到3.3V。如果是TTL 3.3V设备就可以直接连接。还有一种更稳妥的方式使用带保护的逻辑分析仪前端比如用施密特触发器缓冲器比如74HC14先整形信号再送入单片机引脚。这样能过滤掉长线上的噪声和边沿毛刺。更关键的是隔离问题。如果目标设备的通信线不是隔离接口你的单片机插桩接上去之后目标设备和你的分析仪就共地了。这在大多数情况下没问题但如果目标设备是强电系统或者目标设备和你的开发板之间存在地电位差可能烧毁设备或者开发板。我做高压侧采样时一定会用光耦隔离或者隔离式USB转串口来确保分析仪与目标设备物理隔离。另外插桩线不要太长。单片机的引脚输入阻抗高但长线会带来天线效应容易引入干扰。我通常把杜邦线控制在20厘米以内并且尽量使用屏蔽线或者双绞线。4.3 实现一个通用UART捕获器下面给出一个通用的UART捕获器示例代码使用STM32 HAL库核心是用外部中断记录引脚跳变时间戳。思路配置两个引脚的外部中断一个接目标设备的TX一个接目标设备的RX。每次引脚电平变化就记录当前定时器的计数值和引脚电平存入一个环形缓冲区。事后把时间戳转换成bit流。// 捕获通道定义 #define CH_TX_PIN GPIO_PIN_0 #define CH_TX_PORT GPIOA #define CH_RX_PIN GPIO_PIN_1 #define CH_RX_PORT GPIOA // 环形缓冲区存储跳变事件 #define EVENT_BUFFER_SIZE 4096 typedef struct { uint32_t timestamp; // 定时器计数值 uint8_t channel; // 0TX, 1RX uint8_t level; // 0低, 1高 } gpio_event_t; gpio_event_t event_buffer[EVENT_BUFFER_SIZE]; volatile uint16_t event_write_index 0; volatile uint16_t event_read_index 0; // 定时器时钟频率为1MHz即时间戳单位1us void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { gpio_event_t evt; evt.timestamp TIM2-CNT; evt.level 0; evt.channel 0; if (GPIO_Pin CH_TX_PIN) { evt.channel 0; evt.level HAL_GPIO_ReadPin(CH_TX_PORT, CH_TX_PIN); } else if (GPIO_Pin CH_RX_PIN) { evt.channel 1; evt.level HAL_GPIO_ReadPin(CH_RX_PORT, CH_RX_PIN); } else { return; } uint16_t next (event_write_index 1) % EVENT_BUFFER_SIZE; if (next ! event_read_index) { event_buffer[event_write_index] evt; event_write_index next; } } // 从事件缓冲区解析UART帧 // 参数start_idx 起始事件索引baud 波特率 void parse_uart_frame(uint16_t start_idx, uint32_t baud) { uint32_t bit_time_us 1000000 / baud; // 在中断回调中我们已经记录了每个跳变沿的时间戳 // 解析时先找到起始位下降沿且前面至少有半个bit的空闲高电平 // 然后按bit_time_us采样每个bit的电平 // 具体采样逻辑从起始位中心开始之后每bit_time_us取一次电平 // 取8次数据位1次停止位 uint8_t data 0; for (int i 0; i 8; i) { uint32_t sample_time evt.timestamp bit_time_us / 2 i * bit_time_us; // 根据事件时间戳反查对应时间的电平 // 简化做法遍历事件找离sample_time最近的事件取它的电平 data 1; if (get_level_at_time(sample_time, start_idx)) { data | 0x80; } } // 输出解析后的字节 printf(RX byte: 0x%02X\n, data); } uint8_t get_level_at_time(uint32_t time_us, uint16_t start_idx) { // 从start_idx开始顺序遍历事件直到事件时间戳 time_us // 返回上一个事件的电平 uint16_t idx start_idx; uint8_t level 1; // 空闲默认为高 while (1) { gpio_event_t evt event_buffer[idx]; if (evt.timestamp time_us) { break; } level evt.level; idx (idx 1) % EVENT_BUFFER_SIZE; } return level; }这段代码的思路很朴素但实际使用中有一个比较大的问题在中断回调里记录时间戳可以保证精度但是后续的解析如果放在主循环里可能因为主循环被其他任务阻塞而漏掉数据。所以完整版本我一般会加上DMA或者使用空闲中断配合IDLE检测这里给出简化版本是为了讲清楚原理。4.4 双向通信跟踪把TX和RX串成对话单方向解析UART帧只是一个起点真正的协议逆向需要把两条线上的数据串成一组对话。实现方式很简单事件缓冲区的每个事件都带channel字段解析的时候同时处理两个通道。每帧数据解析完成后打印成类似下面的格式[T123456us] DEV-HOST: 01 03 00 02 00 08 [CRC0x24] [T123789us] HOST-DEV: 01 03 04 00 01 02 03 [CRC0x5C]把时间戳打出来特别重要。协议逆向时响应延迟是重要的判断依据。比如某个请求发出后设备在5毫秒内就回复了说明这个请求被直接处理了如果过了200毫秒才回复可能设备内部有延时任务在轮询。这里还有一个提升效率的技巧把时间戳单位从微秒改成毫秒来查看长时间段的通信节奏用微秒来精确分析帧内时序两者配合使用效率高很多。4.5 主动注入让设备暴露更多协议信息插桩分析仪不只是被动监听它还可以主动向设备发送测试数据观察设备反应。这是被动逻辑分析仪完全做不到的。主动注入的思路是猜测设备有某个功能码或者命令用不同的参数反复试探看设备的回复模式。举个例子如果设备是Modbus协议你向它发送一个非法的功能码0x7F正常Modbus设备会回复异常响应0x83加上异常码。如果设备回复了你至少知道它是Modbus。如果它毫无反应可能需要从帧的其他字段继续探索。有一种比较通用的语义探测法向设备发送一帧完全不正确的数据记录它是否会回复以及回复内容里是否包含原始请求里的某些字节。如果设备的错误回复里原样带回了错误字段说明它至少解析到了帧内容而不是在物理层就丢弃了。这一步很有可能让你碰到设备的看门狗或者安全锁定——比如连续发错三次设备会进入死机状态。这时候务必给设备准备一个断电重启的开关否则调试过程会非常痛苦。5. 实战案例一块未知控制板的完整逆向过程5.1 从示波器到神秘光耦的确认过程回到我最初说的那块工业控制板。板子上有一片SOP-8封装的芯片丝印被磨掉了旁边有三个光耦型号也只能看到一部分。线索实在太少我只能靠测量和注入来推进。第一步示波器测量。把探头夹在疑似TX线上看到3.3V空闲电平通信时出现下降沿判断可能是TTL UART。测量最短脉冲宽度发现约104微秒推断波特率9600进一步确认。第二步逻辑分析仪抓包。设置UART RX波特率96008N1抓到了一串看起来很奇怪的字节0x7A, 0xE3, 0x3D, ...。这些字节完全没有规律。当时我差点陷入CRC还是加密的猜测中。第三步仔细观察波形极性。我发现逻辑分析仪波形显示的空闲不是高电平而是低电平起始位是上跳而不是下跳。我立刻意识到可能抓到了反相信号。把逻辑分析仪的极性选项改成反向之后重新解码出来的数据变成0x85, 0x1C, 0xC2, ...。虽然还是看不懂但至少0x85这个字节让我想到了某种已知协议的帧头可能。第四步用插桩单片机做干扰测试。我把STM32插桩接到解码后的逻辑线路上先被动监听一段时间确认设备每次上电会发三个0x85开头的数据帧。然后尝试向疑似RX线发送0x85开头的回复帧设备马上给出了进一步的响应——一个之前没有看到过的数据帧。这个进一步响应非常重要说明设备收到我的数据后确实在应用层做出了应答而不是在物理层就拒收。到这里光耦反相问题的实际影响已经彻底确认如果不修正反相我发送过去的数据就是每一位取反后的内容设备自然无法响应。5.2 跨层联动反向修正与字节序的验证修正反相之后剩下的工作重心变成了字节序和帧格式的判断。我用插桩发送了一串0x85 0x00 0x00 0x00 0x00 0x00观察设备回复。设备回复了一串比较长的数据其中有一个字节序列是0x01 0x02 0x03 0x04我发送的全是0为什么设备回了一串递增的数再结合设备的产品背景——一个多通道检测设备我猜测这些递增字节对应的可能是寄存器地址或者通道编号。用Modbus工具的经验来类比Modbus RTU帧里面地址码后面是功能码功能码后面是寄存器地址寄存器地址是两个字节高字节在前。我把设备回复的数据按照这个格式去套发现有一个16位的数值正好等于我发送的寄存器地址。这说明设备内部很可能使用了Modbus类协议至少数据组织格式是地址功能参数校验。有一个细节我印象很深设备最终返回的校验字节跟标准Modbus CRC16不一致。后来我检查后发现设备不仅做了光耦反相还在代码里对CRC结果做了一次字节交换。这就导致即使协议结构是Modbus直接套用Modbus标准CRC算法也算不对。这类协议结构相似但校验细节不同的坑在国产设备里非常常见。厂家往往会沿用Modbus的帧结构但校验算法的初始值、多项式、结果处理可能跟标准不同。遇到这种情况最直接的办法就是搜集足够多的原始数据原始校验值样本用暴力匹配去反推CRC参数。我个人写过一个简单的CRC参数扫描程序把多项式、初值、输入输出反转的组合全部遍历一遍一般几分钟就能找到正确参数。5.3 插桩日志与协议分析表格为了不让整个项目停留在我已经懂了的状态务必把分析过程中的关键参数和日志留档。我自己每次逆向都会建一个表格记录以下信息参数项我的设备实测值猜测依据电气类型TTL UART3.3V开漏输出空闲3.3V下降沿陡上升沿缓信号极性反向经过光耦波形显示空闲低电平起始位为上跳波特率9600 8N1最短脉冲约104us帧头0x85设备上电自发帧的前导字节帧结构地址功能数据CRC比对回复帧内容得到的推测校验方式类Modbus CRC16高地位字节交换用暴力匹配CRC参数得到的结论这种表格看起来简单但价值非常大。它不仅帮你自己理清了思路也是项目交付物里最容易让甲方信服的部分。很多人逆向做到最后口头讲得头头是道但没有留下结构化文档后期维护完全抓瞎。逆向工程的产出不只是让你自己搞明白了还要让后续接手的人能快速上手所以表格和日志一定不能省。6. 常见问题与排查技巧实录6.1 问题速查表以下这些坑是我在多个项目里反反复复踩过的整理成一个速查表方便大家直接对号入座。现象可能原因排查方向逻辑分析仪解出的数据全是0x00或0xFF信号极性反了波特率偏差太大用示波器量起始位宽度确认极性能抓到帧但校验总是错字节序反了CRC参数不对多字节字段的位序不对在帧尾校验处做暴力匹配检查字节序设备偶发复位或通信中断插桩分析仪的引脚输入电流影响总线共地不良加大串联电阻检查地线连接数据看起来有规律但解不出ASCII可能不是UART而是SPI或I2C确认是否有CLK线是否有片选信号波形毛刺严重边沿抖动大信号线过长缺少上拉光耦延迟缩短杜邦线长度加施密特整形插桩单片机丢帧中断回调时间太长或缓冲区太小降低中断频率扩大环形缓冲区改用DMA6.2 排查技巧一用已知字节校准链路链路校准是我每次逆向的第一步。给目标设备发送一个已知的固定字节比如0xA5然后看抓到的是不是0xA5。如果0xA5变成0x5A说明位序反了。如果变成0xA5但校验不对可能是停止位或者校验位设置错误。如果变成一些完全不相干的字节先怀疑波特率偏了太多。这个方法投入小、见效快能在一分钟之内把数据链路层的绝大部分参数确定下来。我强烈建议所有人在刚开始抓包时先别急着去解复杂的业务帧先发送一个已知字节把链路校准好。6.3 排查技巧二帧边界怎么找Frame boundary是很多新人最容易卡住的地方。UART本身只是一串没有边界的字节流哪里是帧头、哪里是帧尾完全由应用层协议定义。找帧边界有几个比较实用的信号线索时间间隔法。如果设备是定时发送的帧内字节间隔通常很小而帧与帧之间会有一段明显较长的空闲时间。把两帧之间的间隔统计出来取一个阈值比如超过一帧正常间隔的3倍就断定为帧边界。设备如果是事件触发发送这个方法不适用。固定字段法。在大量抓包数据里找出现频率最高的非典型字节。如果某个字节在每个包的开头都出现那它基本就是帧头。比如Modbus的地址字节通常变化不大而功能码可能在某个固定位置变化。响应关联法。给设备发一个请求记录它从收到请求到发出响应的延迟以及响应的起始位置。如果响应总是从请求后的第N个字节开始那N-1很可能就是请求帧的末尾。6.4 排查技巧三错误CRC不是末日很多人在逆向时一看到校验错误就开始挠头。我的建议是先别管CRC对不对先关注数据内容本身是否符合逻辑。比如你发了一条读寄存器指令设备回复了一帧数据里面有一个16位的数值恰好是寄存器地址的递增。即使CRC根本对不上也可以确认协议本身是地址数据结构CRC只是最后要做的一个形式校验。在这种情况下你完全可以把CRC参数逆向留到后面先把语义层确定下来。因为CRC参数通常只有那么几百种组合用暴力匹配很容易解决而语义层的信息才是真正困难的部分。所以进阶经验是不要被CRC挡住先把业务层搞清楚。6.5 避坑心得逆向工程的时间分配最后说一点方法论上的心得。我做的多数逆向项目时间分配大概是物理层20%、数据链路层30%、应用层语义50%。物理层最快通常一两个小时就能解决数据链路层如果碰到光耦反相或者非标波特率会花掉半天应用层语义是最耗时的因为要不断猜测、验证、修正。如果你发现自己在一个项目里卡了很久大概率是卡在应用层这时候别硬猜建议多搜集一些设备的正常通信日志配合主动注入做对比往往会有突破。另外长期做逆向的话建议建立一个自己的常见帧头知识库。很多工业协议都是公开的比如Modbus的帧头就是地址字节有些私有协议会效仿公开协议的结构。你先比对常见协议模板比对不上再考虑私有格式。这个方法能省掉大量从零开始的时间。这趟光耦反相的逆向过程让我对黑盒协议分析有了一个很深的体会很多看似死路的通信现象本质上是物理层或数据链路层的某一个细节没对齐。修正极性之后原本乱七八糟的数据立刻变成了可读的业务内容。逆向没有银弹但按物理层→链路层→应用层的顺序走每一步都留下波形图和日志成功率会高很多。分享一个我后来一直沿用的习惯手里永远备着一块带外部中断引脚的开发板和几个光耦。黑盒逆向随时可能开始一个现成的插桩工具能让你在接手陌生设备时直接进入状态而不是临时翻箱倒柜找器材。毕竟对嵌入式工程师来说解码一块陌生板子上的通信协议就跟拆一颗不知名芯片一样准备工作越充分现场的坑就越少。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门