第4章:VxWorks镜像格式深度解析
好,咱们今天来聊聊VxWorks的镜像格式。说实话,这个知识点在安全启动里特别关键——你连镜像长什么样都不清楚,怎么给它做签名和验证?
我在做安全启动方案时,经常看到有人把ELF文件直接扔给签名工具,结果签名完启动就挂了。为什么?因为没搞懂VxWorks镜像的结构。今天咱们就把这个坑填上。
4.1 VxWorks 7 镜像结构
VxWorks 7的镜像,说白了就是一个大杂烩。它把操作系统内核、驱动、文件系统、应用程序全部打包在一起。我习惯把它分成三个层次来看:
- 引导层:负责从Flash或网络加载镜像
- 内核层:VxWorks微内核本体
- 应用层:你的业务代码和资源文件
你可能会问:「这三层怎么组合到一起的?」嗯,VxWorks用了一个叫VxWorks Image Format (VIF)的容器格式。它有点像快递箱——外面是统一的包装,里面装什么你自己定。
关键点:VxWorks 7默认使用ELF格式作为最终镜像格式。但内部会嵌入一个ROMFS文件系统,用来存放启动脚本、配置文件等。
我记得第一次接触VxWorks 7时,用readelf命令一看,好家伙,一个镜像里塞了十几个段。后来才明白,这是为了支持动态加载和模块化设计。
4.2 ELF格式解析
ELF(Executable and Linkable Format)是VxWorks 7的主力镜像格式。咱们做安全启动,必须得懂它的结构。我把它拆成四个核心部分:
| ELF组成部分 | 作用 | 安全相关 |
|---|---|---|
| ELF Header | 文件头,描述整体结构 | 签名时需保护 |
| Program Headers | 段表,描述内存映射 | 加载时校验 |
| Section Headers | 节表,描述链接信息 | 调试时可忽略 |
| Segment Data | 实际代码和数据 | 签名核心区域 |
这里有个坑,我必须要说:签名时千万别只签Segment Data。为什么?因为ELF Header里记录了入口地址和段偏移,如果有人篡改这些信息,你的镜像加载后可能直接跑飞。
我的经验:在项目中,我通常对整个ELF文件做哈希,但只对特定的Program Headers做签名验证。这样既保证了完整性,又不会因为签名数据过大影响启动速度。
来看一个实际的ELF解析示例:
# 查看VxWorks镜像的ELF头
readelf -h vxWorks.bin
# 输出示例:
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Machine: ARM
Entry point address: 0x80001000
Program Headers: 5
Section Headers: 18
# 查看程序段
readelf -l vxWorks.bin
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x1000 0x80000000 0x80000000 0x1234 0x5678 RWE 0x1000
LOAD 0x3000 0x80010000 0x80010000 0x9abc 0xdef0 RW 0x1000
DYNAMIC 0x5000 0x80020000 0x80020000 0x0020 0x0020 RW 0x8
看到没?Entry point address是0x80001000,这就是CPU第一条指令的位置。如果有人把这个地址改成0x80000000,你的系统可能直接跳到数据区执行——想想都可怕。
4.3 VxWorks ROMFS文件系统
ROMFS(ROM File System)是VxWorks里一个轻量级的只读文件系统。它通常被嵌入到ELF镜像中,用来存放:
- 启动脚本(如
startup.scr) - 设备树文件(
.dtb) - 证书和公钥
- 配置文件
我刚开始做安全启动时,一直以为ROMFS就是个简单的文件列表。直到有一次,客户反馈说「我改了配置文件,但系统启动后还是旧配置」。查了半天,发现ROMFS是只读的,而且被嵌在ELF的.romfs段里——你改不了!
注意:ROMFS在VxWorks 7中默认使用压缩存储。签名时一定要先解压再签名,否则签名验证会失败。我曾经因为这个坑,浪费了整整两天时间。
ROMFS的内部结构其实很简单:
ROMFS结构:
+------------------+
| Superblock | // 文件系统元数据
+------------------+
| File Header 1 | // 文件名、大小、偏移
| File Data 1 |
+------------------+
| File Header 2 |
| File Data 2 |
+------------------+
| ... |
+------------------+
| Checksum | // 整个ROMFS的校验和
+------------------+
你想想看,如果ROMFS里的公钥被篡改了,那签名验证还有什么意义?所以我在项目中,会对ROMFS整体做哈希,然后把这个哈希值放到ELF的另一个段里。这样环环相扣,谁也别想动手脚。
4.4 实战:解析一个VxWorks镜像
光说不练假把式。咱们来实际操作一下,看看一个真实的VxWorks 7镜像长什么样。
假设你有一个vxWorks.bin文件,第一步:
# 检查文件类型
file vxWorks.bin
# 输出:ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV)
# 提取ROMFS区域
objcopy -O binary -j .romfs vxWorks.bin romfs.bin
# 查看ROMFS内容
strings romfs.bin | head -20
# 输出:
# startup.scr
# device_tree.dtb
# public_key.der
# app_config.ini
看到public_key.der了吗?这就是咱们安全启动要用到的公钥。我习惯把它放在ROMFS里,这样更新固件时只需要替换公钥文件,不用重新编译整个内核。
小技巧:在VxWorks 7的配置中,可以通过INCLUDE_ROMFS宏来控制是否嵌入ROMFS。如果不需要文件系统,可以关掉它,能省下不少空间。
最后,咱们总结一下镜像格式和安全启动的关系:
- ELF Header:签名时保护,防止入口点被篡改
- Program Headers:加载时校验,确保内存映射正确
- ROMFS:整体哈希,保护公钥和配置文件
- Segment Data:核心签名区域,保证代码完整性
嗯,这一章的内容就到这儿。下一章咱们会讲如何给这些镜像做签名,以及VxWorks的Bootloader是怎么验证签名的。到时候我会分享一个我踩过的坑——签名算法选错了,导致启动时间增加了3秒。你猜猜是为什么?