第四章:通信协议分析基础——UART、SPI、I2C协议时序分析

各位同学,欢迎来到第四章。前面几章我们聊了怎么拆芯片、怎么找引脚,但说实话,那只是热身。真正的硬仗,是从你看到示波器或逻辑分析仪上那些跳动的波形开始的。

我经常跟团队里的小朋友说:“读懂协议时序,你就拿到了逆向工程的入场券。” 安全芯片再牛,它也得跟外界说话。而说话的方式,无非就是UART、SPI、I2C这老三样。今天我们就来把它们扒个底朝天。

本章核心目标: 掌握三种协议的本质区别,学会用逻辑分析仪抓取并解码通信数据,最终能从波形中还原出通信内容。

4.1 为什么是这三种协议?

你可能会问:嵌入式通信协议那么多,为什么偏偏盯着UART、SPI、I2C?

原因很简单:安全芯片的“对外接口”几乎被这三兄弟包圆了。 我在逆向一款NXP安全芯片时,发现它的调试接口就是UART,而跟主控MCU通信用的是I2C。你想想看,如果连这都看不懂,后面还怎么玩?

这三种协议各有各的脾气:

  • UART:最古老,也最简单。两根线(TX、RX),异步通信,不需要时钟线。说白了就是两个人约定好语速,直接开聊。
  • SPI:速度最快,全双工。四根线(SCK、MOSI、MISO、CS),主从模式。适合大数据量传输,比如往Flash里写固件。
  • I2C:最省引脚,两根线(SCL、SDA)挂一堆设备。半双工,有地址机制。安全芯片跟主控“咬耳朵”时最爱用这个。

嗯,这里要注意:千万别以为协议是固定的。 有些芯片厂商会魔改时序,比如把UART的波特率设成非标值,或者给I2C加个校验位。我遇到过一款芯片,它的UART波特率是134400,标准库里根本没有,当时排查了好久。

4.2 协议时序深度拆解

4.2.1 UART:最朴素的“喊话”

UART的时序其实特别直观。空闲时,TX线是高电平。要发数据了,先拉低一个bit的时间——这叫起始位。然后从低位到高位依次发送数据位,最后来个停止位(高电平)。

我个人的习惯是,拿到一个未知的UART信号,先看它的位宽。怎么算?用逻辑分析仪抓一段波形,量一下一个bit持续多长时间,然后取倒数就是波特率。比如一个bit是8.68微秒,那波特率就是1/0.00000868 ≈ 115200。

实战技巧: 很多安全芯片的UART波特率是115200或9600。如果抓到的波形看起来像UART但解码乱码,试试把波特率翻倍或减半——我遇到过厂商把115200的时钟分频搞错了,实际跑的是57600。

来看一个典型的UART数据帧(8N1,即8数据位、无校验、1停止位):

空闲态(高) | 起始位(低) | D0 | D1 | D2 | D3 | D4 | D5 | D6 | D7 | 停止位(高) | 空闲态(高)

举个例子,如果你看到波形是:起始位(0) → 1 → 0 → 1 → 0 → 1 → 0 → 1 → 0 → 停止位(1),那数据位就是10101010(注意是低位在前),换算成十六进制就是0x55。这个0x55在ASCII里是字符'U',很多芯片的Bootloader启动时都会发这个字符作为握手信号。

4.2.2 SPI:四线制的“快枪手”

SPI就比UART复杂一些了。它有专门的时钟线SCK,所有操作都跟着时钟沿走。我个人觉得,SPI最坑的地方是时钟极性和相位(CPOL和CPHA)。

简单说:

  • CPOL=0:空闲时时钟为低电平
  • CPOL=1:空闲时时钟为高电平
  • CPHA=0:在时钟的第一个边沿采样数据
  • CPHA=1:在时钟的第二个边沿采样数据

四种组合,对应四种模式。我曾经在逆向一款NXP安全芯片时,默认用模式0去抓SPI,结果全是乱码。折腾了两小时才发现,人家用的是模式3(CPOL=1, CPHA=1)。

避坑指南: 抓SPI信号时,一定要同时抓CS(片选)线。CS拉低表示一次传输开始,拉高表示结束。有些芯片会在CS拉高后立即进入低功耗模式,如果你没抓到CS,可能会漏掉关键数据。

SPI的典型时序(模式0):

SCK:  ▔▁▔▁▔▁▔▁▔▁▔▁▔▁▔▁
MOSI: ▔▔▁▁▔▔▁▁▔▔▁▁▔▔▁▁
MISO: ▁▁▔▔▁▁▔▔▁▁▔▔▁▁▔▔
CS:   ▔▔▔▁▁▁▁▁▁▁▁▁▁▁▔▔▔

注意看,CS拉低后,SCK开始产生时钟,MOSI和MISO在SCK的上升沿被采样。每个时钟周期传输1个bit,8个时钟周期就是1个字节。

4.2.3 I2C:两根线的“外交官”

I2C是我个人最喜欢的协议——两根线搞定一切,但也是最容易出问题的。它的时序有几个关键点:

  • 起始条件:SCL高电平时,SDA从高变低
  • 停止条件:SCL高电平时,SDA从低变高
  • 数据有效性:SCL低电平时改变SDA,SCL高电平时采样SDA
  • 应答机制:每发送8个bit后,接收方必须拉低SDA表示应答(ACK)

我刚开始学I2C时,总觉得起始条件和停止条件长得差不多,容易搞混。后来总结了一个口诀:“起始是下降,停止是上升,都在时钟高。”

来看一个完整的I2C写操作时序:

SDA: ▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▔
SCL: ▔▔▁▁▔▔▁▁▔▔▁▁▔▔▁▁▔▔▁▁▔▔▁▁▔▔▁▁▔▔▁▁▔▔▁▁▔▔
      ↑起始   ↑地址+写   ↑ACK   ↑数据   ↑ACK   ↑停止

这里要注意:I2C的地址是7位或10位。安全芯片通常用7位地址,比如0x50。但发送时,地址左移1位,最低位表示读写方向(0=写,1=读)。所以如果你看到逻辑分析仪显示地址是0xA0,那实际设备地址是0x50(0xA0 >> 1)。

4.3 使用逻辑分析仪抓取通信数据

好了,理论说完了,咱们来点实操。我推荐大家用Saleae Logic或者PulseView,前者商业软件但好用,后者开源免费。

抓取步骤其实很简单:

  1. 接线:把逻辑分析仪的通道夹到目标引脚上。记得共地!不共地的话,波形会飘得像心电图。
  2. 设置采样率:至少是信号频率的4倍,建议10倍。比如UART 115200,采样率设2MHz就够。
  3. 触发设置:抓UART可以设下降沿触发(起始位),抓I2C可以设SDA下降沿触发(起始条件)。
  4. 开始抓取:让目标芯片跑起来,或者手动触发通信。
  5. 解码:在软件里选择对应的协议,配置好参数(波特率、极性等),瞬间就能看到解码结果。

我的经验: 第一次抓取时,采样率设高一点没关系,数据量大可以后期裁剪。我见过有人采样率设太低,结果一个bit只采到两个点,波形都变形了,解码自然全是错的。

4.4 解码与分析通信内容

解码只是第一步,分析才是重头戏。拿到解码后的数据,我们要问自己几个问题:

  • 这是握手阶段还是数据传输阶段? 通常通信开始会有固定的握手包,比如0x55 0xAA或者一串ASCII字符串。
  • 数据有没有加密? 如果看到的数据全是乱码,或者有规律但看不懂,那很可能是加密了。我遇到过一款芯片,它的I2C通信内容看起来像随机数,后来发现是XOR了一个固定密钥。
  • 有没有校验? 很多安全芯片会在数据包末尾加CRC或Checksum。如果你发现数据长度固定,且最后几个字节跟前面内容有关,那大概率是校验。

举个例子,我曾经抓到一个SPI通信,数据如下:

0xA5 0x5A 0x01 0x02 0x03 0x04 0x0A

前两个字节0xA5 0x5A看起来像同步头,中间四个字节像是数据,最后一个字节0x0A。我算了一下,0x01+0x02+0x03+0x04 = 0x0A,正好是Checksum。这就是典型的“同步头+数据+校验”结构。

分析技巧: 把抓到的数据导出为CSV或TXT,用Python或Excel做统计分析。看看哪些字节出现频率高,哪些是固定值。我经常用这种方法快速定位协议中的命令字和状态字。

4.5 本章知识体系

为了让大家更直观地理解本章的知识结构,我画了一张图:

通信协议分析知识体系 通信协议分析 UART协议 异步、全双工 起始位+数据位+停止位 波特率:9600/115200 SPI协议 同步、全双工 SCK/MOSI/MISO/CS CPOL/CPHA四种模式 I2C协议 同步、半双工 SCL/SDA + 地址机制 起始/停止/应答 逻辑分析仪:Saleae / PulseView 解码 → 分析 → 还原通信内容

这张图把本章的核心内容串起来了。你看,三种协议各有特点,但最终都要落到逻辑分析仪这个工具上。工具是死的,人是活的。我见过有人拿着几万块的示波器抓I2C,结果连起始条件都认不出来——因为他没设置好触发。

4.6 实战建议

最后,给大家几点实战建议:

  • 先看文档,再动手。 如果芯片有数据手册,先查通信接口的电气特性和时序参数。没有文档?那就只能靠猜了,但猜也有技巧——先试最常用的配置。
  • 多通道同时抓。 比如抓SPI时,把SCK、MOSI、MISO、CS四根线都接上。只抓两根线的话,你永远不知道数据是从主到从还是从从到主。
  • 保存原始数据。 解码后的数据可以导出,但原始波形也要保存。我经常在分析到一半时发现解码参数设错了,这时候重新抓可能来不及,但原始波形还在,改个参数重新解码就行。
  • 别迷信自动解码。 逻辑分析仪的自动解码功能很强大,但也会出错。尤其是遇到非标协议或时序有抖动时,一定要肉眼核对一下波形。

好了,这一章的内容就到这里。通信协议分析是个熟能生巧的活,多抓几次,多分析几次,你就能从波形里读出芯片的“心事”。


公众号:蓝海资料掘金营,微信deep3321