2. 固件镜像CRC校验
任务说明
在OTA升级流程中,固件镜像在传输或存储过程中可能发生比特翻转,导致设备运行异常。本案例实现标准CRC32(IEEE 802.3,多项式0xEDB88320)校验算法,对固件镜像文件计算校验值,并在模拟升级前验证镜像完整性。
实现方案
采用查表法提升计算效率,预生成256项CRC表,对镜像文件按字节流式处理。核心代码包含:CRC表生成、CRC计算函数、文件读取与校验主流程。该实现不依赖第三方库,可直接在Linux主机或嵌入式Linux环境编译运行。
关键完整代码
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
static uint32_t crc_table[256];
/* 生成CRC32查表(多项式0xEDB88320) */
static void crc32_init(void) {
for (uint32_t i = 0; i < 256; i++) {
uint32_t c = i;
for (int k = 0; k < 8; k++) {
c = (c & 1) ? (0xEDB88320 ^ (c >> 1)) : (c >> 1);
}
crc_table[i] = c;
}
}
/* 计算缓冲区CRC32值 */
static uint32_t crc32_compute(const uint8_t *data, size_t len, uint32_t crc) {
crc = ~crc;
for (size_t i = 0; i < len; i++) {
crc = crc_table[(crc ^ data[i]) & 0xFF] ^ (crc >> 8);
}
return ~crc;
}
/* 计算整个文件的CRC32 */
static int file_crc32(const char *path, uint32_t *out_crc) {
FILE *fp = fopen(path, "rb");
if (!fp) return -1;
uint8_t buf[4096];
size_t n;
uint32_t crc = 0xFFFFFFFF; /* 初始值 */
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
crc = crc32_compute(buf, n, crc);
}
fclose(fp);
if (ferror(fp)) return -1;
*out_crc = crc;
return 0;
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "用法: %s <固件镜像文件>\n", argv[0]);
return 1;
}
crc32_init();
uint32_t crc;
if (file_crc32(argv[1], &crc) != 0) {
fprintf(stderr, "错误: 无法读取文件 %s\n", argv[1]);
return 1;
}
printf("CRC32: 0x%08X\n", crc);
return 0;
}
编译与运行
# 编译
gcc -O2 -o crc32_check crc32_check.c
# 生成测试镜像(1KB随机数据)
head -c 1024 /dev/urandom > firmware.bin
# 计算校验值
./crc32_check firmware.bin
验证方法
- 一致性验证:连续运行两次
./crc32_check firmware.bin,两次输出CRC32值必须完全相同。 - 完整性验证:使用
cksum命令对比结果——cksum firmware.bin输出的第一个数字(十进制)转换为十六进制后,应与本程序输出一致(注意字节序,cksum输出为十进制无符号数)。 - 错误检测验证:修改镜像任意一个字节(例如
printf 'A' | dd of=firmware.bin bs=1 seek=500 count=1 conv=notrunc),重新计算CRC32,校验值必须发生变化。
通过以上三项测试,即可确认CRC32算法实现正确且能有效检测固件镜像的完整性破坏。