第四章:目标板编程与通信
好,咱们进入正题。这一章要解决一个实际问题——怎么让目标板(比如XMEGA)乖乖听话,跑我们写的加密算法,并且能跟PC通信。说白了,就是打通从PC到芯片的这条“路”。
我在做第一个侧信道项目时,卡在最开始的地方整整两天——固件烧不进去。后来发现是熔丝位配置错了。嗯,这种坑,我帮你先踩一遍。
4.1 目标板固件烧录
先说说烧录这件事。XMEGA这类芯片,通常用JTAG或PDI接口。ChipWhisperer自带编程器,省了不少事。
核心要点:烧录前必须确认三件事——供电电压、时钟源、熔丝位。这三样错了,芯片直接“装死”。
我个人习惯用ChipWhisperer的Python API来烧录。代码其实很简单:
import chipwhisperer as cw
# 连接目标板
scope = cw.scope()
target = cw.target(scope)
# 烧录固件
fw_path = "path/to/your/firmware.hex"
cw.program_target(scope, cw.programmers.XMEGA, fw_path)
你看,就这几行。但有个细节——program_target函数会自动处理复位和握手。如果你用别的编程器,得手动控制复位时序。
我的经验:烧录失败时,先检查连接线。杜邦线太长或接触不良,是头号杀手。我建议用屏蔽线,长度控制在10cm以内。
4.2 串口通信协议
固件烧进去了,怎么跟它说话?串口(UART)是最常用的方式。XMEGA的USART模块,波特率设115200,8N1格式,基本够用。
这里有个坑——波特率误差。晶振不准,或者分频系数算错,都会导致乱码。我曾经被这个坑过,调了一下午才发现是晶振负载电容没焊对。
通信协议我建议用简单的“命令-响应”模式。比如:
// PC发送:0x01 0x00 0x05 0x06 0x07 0x08 0x09
// 含义:命令字(0x01=加密) + 数据长度(0x05) + 数据(5字节)
// 目标板响应:0x81 0x05 0xAA 0xBB 0xCC 0xDD 0xEE
// 含义:响应字(0x81=成功) + 数据长度(0x05) + 加密结果
你想想看,这种协议简单到用状态机就能解析。XMEGA的RAM有限,别搞太复杂的协议栈。
| 命令字 | 功能 | 数据格式 |
|---|---|---|
| 0x01 | 加密 | 明文数据(1-16字节) |
| 0x02 | 解密 | 密文数据(1-16字节) |
| 0x03 | 设置密钥 | 密钥数据(16字节) |
| 0xFF | 复位 | 无数据 |
注意:串口通信没有校验机制。如果数据传错了,加密结果就错了,功耗分析也会跟着错。我建议在数据包末尾加一个简单的累加和校验。
4.3 自定义加密算法部署
好了,通信搞定了。接下来把加密算法部署到目标板上。这里说的“自定义”,不是让你从零发明一个算法,而是把标准算法(比如AES)做一些改动,方便我们做功耗分析。
比如,我常用的一个“教学版AES”:
// 简化版AES-128,只做一轮加密
void aes_one_round(uint8_t* plaintext, uint8_t* key) {
uint8_t state[16];
memcpy(state, plaintext, 16);
// AddRoundKey
for(int i=0; i<16; i++) {
state[i] ^= key[i];
}
// SubBytes
for(int i=0; i<16; i++) {
state[i] = sbox[state[i]];
}
// ShiftRows
// ... 标准实现
// MixColumns
// ... 标准实现
memcpy(plaintext, state, 16);
}
为什么只做一轮?因为功耗分析时,我们只需要抓住第一轮SubBytes的输出就够用了。轮数多了,反而增加分析难度。
部署步骤:
- 在PC上写好算法代码(C语言)
- 用AVR-GCC交叉编译成.hex文件
- 通过ChipWhisperer烧录到XMEGA
- 用串口发送测试数据,验证输出
这里有个小技巧——编译时加-O0优化选项。为什么?因为优化后的代码会打乱指令顺序,让功耗轨迹变得难以分析。不优化,每条指令都老老实实执行,功耗特征更明显。
4.4 验证加密输出正确性
算法部署完了,怎么知道它算对了?拿已知的测试向量验证。
比如AES-128的官方测试向量:
密钥: 2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c
明文: 6b c1 be e2 2e 40 9f 96 e9 3d 7e 11 73 93 17 2a
密文: 3a d7 7b b4 0d 7a 36 60 a8 9e ca f3 24 66 ef 97
你在PC上算一遍,再通过串口发给目标板算一遍,对比结果。如果一致,说明算法部署正确。
我的习惯:写一个Python脚本,自动发送测试向量并比对结果。这样每次改完固件,跑一遍脚本就知道有没有搞坏东西。
为什么会不一致?常见原因有三个:
- 字节序问题:XMEGA是小端,PC是大端,数据传过去要转换
- 密钥加载错误:密钥扩展算法写错了
- S盒实现错误:查表时索引越界或值不对
我曾经犯过一个低级错误——S盒数组定义成const uint8_t,但忘了加PROGMEM。结果XMEGA从Flash读数据时,地址算错了,整个S盒都是乱的。嗯,从那以后我每次都会检查.lss文件,确认S盒数据在正确的位置。
知识体系图
下面这张图,把本章的核心逻辑串起来了:
这张图展示了从烧录到验证的完整闭环。每一步都有坑,但走通了,后面的功耗分析就顺了。
好了,这一章就到这里。记住,目标板是你的“实验小白鼠”,把它调教好了,后面的攻击才能玩得转。
本章要点回顾:
- 烧录固件前检查熔丝位和时钟
- 串口协议要简单可靠,加校验
- 部署算法时关掉编译优化
- 用测试向量验证输出正确性
公众号:蓝海资料掘金营,微信deep3321