编辑 | blame | 历史 | 原始文档

单片机固件解密实现 - 文件迁移说明

概述

本文档说明如何将BootLoader中的AES解密和固件下载功能移植到其他STM32工程。


一、必需文件清单

1. AES加密/解密核心模块

文件位置: BootLoaderL051/decadriver/

文件名 说明 必需
aes.c AES-256加密解密核心实现
aes.h AES模块头文件

功能说明:
- 实现AES-256-CBC加密解密算法
- 使用硬编码密钥 kTable(32字节)
- 主要函数:
- aesDecInit() - 解密初始化
- aesDecrypt(buffer, chainBlock) - 解密16字节数据块
- aesEncInit() - 加密初始化
- aesEncrypt(buffer, chainBlock) - 加密16字节数据块

密钥(kTable):
c const unsigned char kTable[32] = { 0x21, 0x05, 0x04, 0x19, 0x89, 0x12, 0x13, 0x00, 0x18, 0xDE, 0xCA, 0x01, 0x30, 0x52, 0x01, 0x23, 0x13, 0x05, 0x33, 0x19, 0x93, 0x07, 0x08, 0x00, 0x20, 0x5B, 0xA3, 0x4A, 0x21, 0xBA, 0xF9, 0xFC };


2. Ymodem协议接收模块

文件位置: BootLoaderL051/decadriver/

文件名 说明 必需
ymodem.c Ymodem协议实现(含解密逻辑)
ymodem.h Ymodem协议头文件

功能说明:
- 通过串口接收加密的固件文件
- 自动调用AES解密(第335-341行)
- 数据包CRC16校验
- 写入Flash存储

核心流程:
c // 在 Ymodem_Receive() 函数中(第330-360行) 1. 接收1024字节数据包 2. 对每16字节进行AES-CBC解密: for (j = 0; j < packet_length; j += 16) { aesDecrypt(BufferIn, bufferOut); BufferIn += 16; } 3. 写入Flash

⚠️ 注意:目前缺少固件校验和验证!


3. Flash操作模块

文件位置: BootLoaderL051/HAL/

文件名 说明 必需
Flash.c Flash擦除、读写操作
Flash.h Flash模块头文件定义

功能说明:
- Flash擦除、读、写操作
- 地址和大小配置
- 写入验证

主要函数:
- FLASH_Prepare(Address, Len) - 擦除Flash
- FLASH_Write(Address, pData, Len) - 写入数据
- FLASH_Read(Address, Readbuff, Len) - 读取数据
- FLASH_If_Write() - Ymodem专用写入接口

需要修改的配置:
c // 在 Flash.h 中根据实际Flash大小修改 #define USER_FLASH_END_ADDRESS 0x0800FBFF // Flash结束地址 #define APPLICATION_ADDRESS 0x8005000 // APP起始地址 #define PAGE_SIZE 0x100 // 页大小


4. 下载管理模块

文件位置: BootLoaderL051/decadriver/

文件名 说明 必需
menu.c 固件下载主流程
menu.h 下载管理头文件

功能说明:
- 调用Ymodem接收固件
- 下载完成后跳转到应用程序
- 错误处理

主要函数:
- SerialDownload() - 串口固件下载
- Main_Menu() - 主菜单循环


5. 串口通信模块(需自行适配)

文件位置: BootLoaderL051/HAL/

文件名 说明 必需
Usart.c 串口收发实现
Usart.h 串口头文件

需要提供的接口函数:
c // 在你的工程中需要实现这些函数 uint32_t SerialKeyPressed(uint8_t *key); // 检测串口是否有数据 void SerialPutChar(uint8_t c); // 发送一个字符 void SerialPutString(uint8_t *s); // 发送字符串


二、依赖关系图

main.c
  └─> menu.c (下载管理)
       └─> ymodem.c (协议+解密)
            ├─> aes.c (解密算法)
            ├─> Flash.c (Flash操作)
            └─> Usart.c (串口通信)

三、移植步骤

Step 1: 复制核心文件

将以下文件复制到新工程:
新工程/ ├── AES/ │ ├── aes.c │ └── aes.h ├── Bootloader/ │ ├── ymodem.c │ ├── ymodem.h │ ├── menu.c │ └── menu.h └── HAL/ ├── Flash.c ├── Flash.h ├── Usart.c (或自行实现) └── Usart.h

Step 2: 修改配置参数

  1. Flash.h - 根据芯片型号修改:
  • USER_FLASH_END_ADDRESS - Flash结束地址
  • APPLICATION_ADDRESS - 应用程序起始地址
  • PAGE_SIZE - Flash页大小
  1. aes.c - 确认密钥(通常不需要改):
  • kTable[32] - 确保与上位机一致

Step 3: 实现串口接口

在你的串口模块中实现:
```c
uint32_t SerialKeyPressed(uint8_t *key) {
// 检查串口是否收到数据,如果有则读取到*key
// 返回1表示有数据,0表示无数据
}

void SerialPutChar(uint8_t c) {
// 发送一个字符
HAL_UART_Transmit(&huart1, &c, 1, 100);
}

void SerialPutString(uint8_t s) {
// 发送字符串
HAL_UART_Transmit(&huart1, s, strlen((char
)s), 1000);
}
```

Step 4: 添加到编译系统

  • 将所有.c文件添加到编译器
  • 将头文件路径添加到include path
  • 确保HAL库和CMSIS已正确配置

Step 5: 在main中调用

int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    
    // 检查是否需要进入BootLoader
    if (/* 检测条件,如按键或标志位 */) {
        Main_Menu();  // 进入固件下载
    }
    
    // 否则跳转到应用程序
    IAP_JumpTo(APPLICATION_ADDRESS);
    
    while(1);
}

四、当前已知问题 ⚠️

1. 缺少固件校验和验证

  • 上位机在固件末尾添加了4字节校验和(小端序)
  • 单片机接收后**没有验证**此校验和
  • 风险: 固件损坏也可能被执行

需要添加的代码位置: menu.cSerialDownload() 函数完成后

建议实现:
```c
// 在 SerialDownload() 成功后,跳转前添加
uint32_t VerifyFirmwareChecksum(uint32_t address, uint32_t size) {
uint32_t i;
uint32_t calculated_sum = 0;
uint32_t stored_sum;
uint8_t *firmware = (uint8_t *)address;

// 计算固件数据的校验和(不包括最后4字节)
for (i = 0; i < size - 4; i++) {
    calculated_sum += firmware[i];
}
calculated_sum &= 0xFFFFFFFF;

// 读取存储的校验和(小端序)
stored_sum = firmware[size - 4] |
             (firmware[size - 3] << 8) |
             (firmware[size - 2] << 16) |
             (firmware[size - 1] << 24);

return (calculated_sum == stored_sum) ? 1 : 0;

}
```

2. C#程序的密钥未被使用

  • C#界面显示的密钥输入框实际无效
  • 真正的密钥硬编码在aes.c的kTable中
  • 如需更改密钥,需要同时修改C#的MYT.dll和单片机的aes.c

五、测试验证

加密固件格式验证

已通过Python程序验证:
- ✓ 使用AES-256-CBC,IV为全0
- ✓ 密钥为kTable(32字节)
- ✓ 校验和正确添加在固件末尾
- ✓ 解密后数据与原始固件一致

测试命令:
bash python verify_encryption.py


六、技术细节

AES-256-CBC 加密流程

1. 原始固件数据(HEX转BIN)
2. 计算32位累加和(校验和)
3. 追加4字节校验和(小端序)
4. 填充至16字节倍数(0xFF填充)
5. AES-256-CBC加密(IV=全0)
6. 保存为.Bin文件

解密流程(单片机端)

1. 通过Ymodem接收加密数据包
2. 每16字节进行AES-CBC解密
3. 写入Flash
4. (缺失)验证校验和
5. 跳转到应用程序

七、联系与支持

如有问题,请检查:
1. Flash地址配置是否正确
2. 串口波特率和配置
3. 密钥是否一致
4. HAL库版本兼容性

建议优先实现固件校验和验证功能,以提高系统可靠性!


文档创建日期: 2025-12-20
适用于: STM32L051及类似型号
工具链: Keil MDK / STM32CubeIDE