
你有没有想过,一台摆在客厅的智能音箱、一个装在工厂产线的PLC、一块贴在病人身上的医疗贴片——它们和服务器最大的区别是什么?
不是算力,不是功耗。是攻击者可以物理接触到它们。
服务器锁在机房里,层层门禁、监控、防火墙。嵌入式设备呢?它可能挂在工厂墙角的裸露网口上,藏在公共充电桩内部,甚至被人捡走拆开。一旦攻击者拿到了硬件,软件层面的所有防护都等于在沙滩上建城堡。
我们先看一个真实的攻击链,就会明白问题有多严重。
攻击者只需要三步
第一步,获取固件。大多数MCU的Flash读保护是可以通过暴力手段绕过的。用SPI Flash夹子直接怼在芯片引脚上,或者把Flash芯片吹下来放进编程器,甚至用侧信道攻击——攻击者的工具清单已经非常成熟。
第二步,逆向分析。拿到二进制的bin文件,用binwalk拆开,看到文件系统、uboot、内核。密码硬编码在代码里?抓出来。私有协议的逻辑?反汇编慢慢看。MQTT的连接凭证?明文存着。
第三步,持久化驻留。修改固件、注入后门、重新打包、刷回去。整个过程也许不到一个下午。
听起来像电影情节?ChromeOS的CTF挑战赛里,选手们就在做一模一样的事。
所以安全防护到底要防谁
回答这个问题之前,要先搞清楚一件事:安全不是开关,是成本博弈。
如果你只是做个实验室demo,没人会去攻击你。但如果你的设备控制着一栋楼的门禁、一条产线的机器人、或者十万台设备的OTA通道——你面对的就是有动机的攻击者。
嵌入式安全有4个层次,一层比一层成本高,也一层比一层难绕:
第一层:调试接口封锁。 JTAG/SWD接口在生产后直接熔断保险丝。STM32上叫RDP Level 2,一旦设置就无法还原,芯片只能烧录一次。这条防线对普通爱好者是铜墙铁壁,但对专业团队——他们会用激光探针绕过。
第二层:加密存储。 敏感数据不直接存Flash,而是存到加密芯片里,或者用芯片内置的OTP区域配合唯一ID做绑定。Microchip的ATECC608A就是专门干这个的,能存密钥、能做签名验证、能防物理读取。
第三层:安全启动链。 从BootROM开始一级级验签——BootROM校验uboot,uboot校验kernel,kernel校验rootfs。任何一级被篡改,启动链就断裂,设备不会跑非授权的代码。这个机制在大多数MPU上都实现了,MCU上也越来越多。
第四层:运行时保护。 Stack canary、ASLR、MPU/MMU隔离、TrustZone/TrustZone-M划分安全世界和普通世界。Cortex-M33/M55/P6x系列已经内置了TrustZone支持。
从代码层面开始加固
不扯太远的理论,我们直接从代码层面看两个最基础的加固手段。
第一个是防止缓冲区溢出——C语言里最经典的安全漏洞。一个简单的改进是启用compiler的-fstack-protector-strong。GCC加了这个选项之后,会在函数的栈帧里嵌入一个canary值,函数返回前校验这个值有没有被改写。被改了就调用__stack_chk_fail。
// 开启前
void process_packet(uint8_t *data, int len) {
char buf[64];
memcpy(buf, data, len); // 如果 len > 64,栈就烂了
}
// 开启后,编译器自动生成类似这样的保护
void process_packet(uint8_t *data, int len) {
char buf[64];
int canary = __stack_chk_guard;
memcpy(buf, data, len);
if (canary != __stack_chk_guard) {
__stack_chk_fail();
}
}
就这么一个编译选项,能让绝大多数远程栈溢出攻击直接失效。STM32CubeIDE里在项目属性里加一行-fstack-protector-strong就能打开。
第二个是MPU隔离——Cortex-M3/M4/M7上往往被忽视的功能。MPU可以把内存划分为多个region,给每个region设置不同的访问权限。比如代码区只读不可写、数据区不可执行、外设寄存器只允许特权模式访问。
// MPU配置示例:将Flash区域设为只读
MPU->RNR = 0; // Region 0
MPU->RBAR = 0x08000000; // Flash起始地址
MPU->RASR = (0x1F << 1) // 大小:1MB
| (0x1 << 28) // 使能
| (0x0 << 24) // 全访问权限(特权+用户)
| (0x1 << 18) // 不可执行?这里设为可执行
| (0x1 << 15); // 不允许读取就写… 等等,需要查手册具体bit
以上配置需要对照具体芯片手册的MPU寄存器定义来写,不同厂商的Cortex-M实现有细微差异。但思路是一致的——给每块内存区域设置最小必要权限。
一个反直觉的结论
很多人觉得嵌入式系统"够封闭、够小众、没人攻击",统计数据却是另一回事。根据NIST的漏洞数据库,2019-2023年间嵌入式/物联网相关的CVE数量增长了5倍以上。
不是因为嵌入式变脆弱了。而是因为原来没人看——现在攻击者发现,攻破一个云服务器的难度远大于攻破一个温度传感器,但温度传感器后面连着内网。
更要命的是,嵌入式设备一旦部署就很少更新固件。一个2018年生产的家庭网关,到今天可能还运行着有CVE-2019-xxxx漏洞的Linux内核版本。
做了这么多年嵌入式,你最该操心的不是程序跑不跑得稳,而是跑的东西会不会被人利用——你部署的不是代码,是入口。