张 浩 申珊靛 刘 鹏 杨泽霖 周 威 张玉清,5
1(西安电子科技大学网络与信息安全学院 西安 710071)
2(中国科学院大学国家计算机网络入侵防范中心 北京 101408)
3(华中科技大学网络空间安全学院 武汉 430074)
4(分布式系统安全湖北省重点实验室(湖北省大数据安全工程技术研究中心) 武汉 430074)
5(海南大学计算机与网络空间安全学院 海口 570228)
近年来物联网行业发展迅速,嵌入式设备广泛应用于人类生活的各个领域.这些无处不在的嵌入式设备构成了一个万物互联的巨大网络,极大地促进了人类社会的信息化、智能化发展.嵌入式设备作为物联网中的重要组件,往往被针对性地赋予若干专用功能.基于商业成本控制,开发者在设计嵌入式设备时会在足以完成特定功能的情况下尽量节约成本,这使得嵌入式设备开发过程缺乏代码审计、安全测试和地址空间随机化等安全机制[1].同时,嵌入式设备作为万物互联的重要节点,其一旦被非法组织攻破,造成的危害后果极大.
例如,2023 年4 月举行的美国信息安全大会中指出,目前分布式拒绝服务攻击(DDoS)比例超过了总体攻击比重的50%,主要源于大量增长的物联网设备极易被攻击者控制[2].据统计,超过90%的嵌入式实时系统没有实现数据段内存不可执行、段基地址随机化和栈溢出保护等基本的漏洞缓解措施[1].此外,由于嵌入式设备包含多种专用外设接口,其存在的攻击风险远大于通用计算机系统.例如BroadPwn漏洞可以通过嵌入式设备的外设来攻击其嵌入式系统[3].但是这种跨组件的漏洞一般无法通过分析单个嵌入式系统来发现或复制,因为这种漏洞利用的是嵌入式设备中多个硬件和软件间的数据通信和行为模式中的缺陷.
近年来,业界尝试将各种表现良好的漏洞挖掘技术应用到嵌入式领域中,但需要克服嵌入式设备存在的底层硬件复杂多样、软件和硬件资源有限、设备信息文档不公开等问题.一方面,嵌入式设备厂商的软硬件实现方式种类繁多,底层硬件平台也复杂异构,使得通用计算机系统上的先进漏洞挖掘技术难以直接适用;另一方面,不同厂商有不同的设计风格和编码方式,出于行业竞争的考虑,其源码文档并不公开,导致嵌入式设备的安全测试工作难以顺利进行,同时,出于成本和安全的考虑,嵌入式设备的计算资源十分有限,主频通常只有几百兆赫兹,导致动态分析等需要大量运行资源的漏洞挖掘技术迁移到嵌入式设备中的挖掘效率较低[4].
之前多数研究人员对嵌入式设备和固件的安全分析仅通过静态分析技术[5-8].虽然嵌入式设备和固件的静态分析技术取得了不错的成果,但是面对数量庞大的嵌入式设备,静态分析人工成本高、误报率高的缺点逐渐暴露.而相比之下动态分析技术准度度高、自动化程度高,可以大幅提高漏洞挖掘的效率,消除不同平台间的环境壁垒,帮助研究人员实现通用、高效的安全分析方案.然而,嵌入式设备硬件依赖性高,如何分离嵌入式固件和底层硬件或实现嵌入式设备固件全系统硬件仿真,即嵌入式固件仿真技术,成为目前学术界的研究热点和工业界的首选分析工具[9-12].将嵌入式固件重新部署或重新托管到虚拟环境可以增加其物理系统上不具备的功能,例如可检查性、可变性、可复制性、可扩展性和可处置性.这种虚拟环境可脱离嵌入式设备的硬件约束,能够以任何粒度检查重新托管的嵌入式固件的执行状态[13].通俗来讲,这种虚拟系统的状态是完全可变的处理器状态,在嵌入式固件执行的任何阶段都可以修改内存,控制非确定性的来源,以便保证分析结果的可复制性.重新托管的嵌入式系统可以非常容易地克隆来进行分布式动态分析,提高漏洞挖掘效率或者与合作者共享分析进程.同时,虚拟系统的可复制性使得分析者不需要担心某些危险操作损害系统.因此,在原始物理系统上不可行的模糊测试、符号执行等动态分析技术变得可行[14].然而,虽然传统的嵌入式系统仿真研究例如嵌入式Linux 系统仿真已经开展多年,但随着基于微控制器的嵌入设备广泛地应用,其无法适用于各种专用嵌入式系统设备,难以应对日益增长的物联网设备硬件仿真需求.
本文检索了2013—2023 年间的网络与系统安全国际四大顶级会议(IEEE S&P,CCS,USENIX Security,NDSS)中嵌入式设备固件仿真器的相关工作,从图1中可以看到,固件仿真器的相关工作论文数量总体呈上升趋势.
Fig.1 Number of papers related to firmware emulators published in international conferences within 10 years图1 10 年内在国际会议中发表的与固件仿真器相关的论文数量
在嵌入式设备安全研究综述中,大多侧重于安全分析技术的总结与应用,例如 Wright 等人[15]总结了嵌入式固件的现状与挑战,并为固件分析从业者提供选择建议;Fasano 等人[14]主要对固件仿真工具进行对比实验并提出新的发展方向;Feng 等人[16]和于颖超等人[4]总结了物联网设备固件上常见的漏洞检测技术和安全分析技术.由此可见,这些相关研究综述并未侧重固件仿真器.并且,近年来嵌入式设备仿真技术发展迅速,这些综述大多未包含最新成果,以及未对固件仿真器发展体系总结.因此,有必要针对嵌入式设备固件仿真器的研究重新梳理,进一步分析和总结该领域的工作.
本文的架构框图如图2 所示,本文的主要贡献包括5 个方面:
Fig.2 Framework diagram of our proposed paper图2 本文框架图
1)本文系统性地介绍了固件仿真器相关的基础知识和工作流程.对固件仿真器中常见的系统仿真和固件重新托管进行说明和区分,帮助读者初步了解固件仿真器的相关知识,以便更好地理解各种固件仿真器.
2)分析回顾了嵌入式设备固件仿真器的发展历史.根据国际著名安全会议上的相关工作梳理固件仿真器的发展历程与方向,并介绍不同方向的开创性工作和富含重大意义的研究成果.加入了最新的固件仿真器工具并介绍固件仿真器不同方向的发展体系.
3)根据仿真器的主要目标,将固件仿真器分为指令仿真和外设仿真2 个研究方向,进一步将外设仿真研究根据其主要仿真方式细化为硬件在环、基于符号执行、基于人为假设、基于模糊测试反馈和基于硬件手册等,并介绍各种固件仿真器的核心技术原理.
4)使用保真度以及应对各种挑战的处理方式对各种嵌入式设备固件仿真器进行评估和比较,方便读者更加直观地认识各种固件仿真器的特点.固件仿真器在不同的使用场景下的性能各不相同,本文结合固件仿真器的不同特性和优势为使用者提供固件仿真器选择参考.
5)结合各种新技术的发展和嵌入式设备固件仿真器的瓶颈,本文介绍了固件仿真器面临的挑战和机遇,从闭源二进制固件信息提取与还原、高保真度复杂设备外设仿真模型自动化构建、不同设备固件仿真方案的兼容与融合方案、固件仿真器的可扩展性、基于固件仿真器的动态分析技术等方面为固件仿真器的下一步研究提供了方向参考.
嵌入式设备固件指设备上所有软件代码的集合,其通常被存储在只读存储或者闪存中.嵌入式设备固件的格式复杂多样,没有统一的标准,并且嵌入式系统中的数据和代码往往交织在一起,执行入口也通常为了中断向量表,例如ARM Cortex-M 架构而保存在固件中.如图3 所示,常见的嵌入式系统包括固件和设备,系统可分为4 层,分别是硬件层、驱动层、操作系统层和应用层.硬件层是指嵌入式设备依赖的硬件平台,常由处理器、存储器和外设组成.不同的嵌入式设备硬件环境的差异除了处理架构,更多地体现在外设种类的差异性.驱动层通过控制外设读写操作和硬件外设进行交互.驱动层代码大多由设备厂商提供,包含于板级支持包(BSP)中或者使用开源的硬件抽象层接口(HAL).此外,大多嵌入式设备会包含嵌入式系统,例如嵌入式Linux 和实时操作系统(RTOS)层,负责应用程序的任务调度和控制等核心功能,具有内核精简、可配置、与应用程序紧密关联等特点.应用层包含设备核心功能的嵌入式应用程序、逻辑代码和算法等.
Fig.3 Embedded device firmware hierarchy diagram图3 嵌入式设备固件层次图
嵌入式设备的核心功能依靠对外设的控制来实现,例如机器人、无人机等.如图4 所示,外设可分为片上外设和片外外设,固件无法直接访问片外外设,只能通过片上外设与片外外设进行交互.交互方式主要是通过4 种方式:1)外设的硬件寄存器映射到处理器可访问的内存空间(memory mapped I/O,MMIO),并且外设的状态、配置和数据都可以通过读取和写入特定的内存位置来查询.2)外设的硬件寄存器不会映射到内存空间,而是通过使用指令查询端口进行访问.3)外设在计时器完成、新数据到达等情况时,通过触发中断来告知处理器,处理器再进行相应的外设操作处理.4)外设也可以独立于处理器和其他外设直接与主存进行数据传输,即DMA,该方式通常适用于大数据量传输.
Fig.4 Hardware framework diagram of a certain microprocessor图4 某微控制器的硬件框架图
嵌入式设备功能的复杂程度决定它们的嵌入式系统的大小,既有定制版本的Linux 等桌面操作系统的大型多处理器系统,又有几KB 代码的低成本、低功耗微处理器实时操作系统.所以我们选择参考Wright 等人[15]的分类方式,将嵌入式设备固件按系统类型分为3 类.
第1 类是基于通用操作系统的嵌入式设备,主要使用服务器和桌面系统的通用操作系统,包括实时Linux、树莓派等系统.这一类嵌入式设备的操作系统是传统计算机操作系统针对嵌入式空间进行的变种,保留了大部分桌面功能,与外设的交互也是由特定的设备驱动负责的.这一类嵌入式设备的优点是可以直接使用多数通用计算机系统的仿真工具,例如QEMU[17]等.
第2 类是基于嵌入式操作系统的嵌入式设备,通常是商业物联网产品,例如使用μCLinux 和ZephyrOS等系统的嵌入式设备.区分第2 类和第1 类的特征在于,第1 类的嵌入式设备中的系统和应用程序可以单独编译.第2 类系统通常将操作系统与应用程序等编译为统一固件.第2 类操作系统适用于计算能力较低但实时性较高的嵌入式设备,例如小型无人机等.此外,如VxWorks 操作系统,可以根据不同配置同时具备第1 类和第2 类的编译方式和功能特点.需要注意的是,第2 类操作系统的仿真并不能直接使用通用计算机系统的仿真技术,这一类仿真需要重新托管内核和用户空间,并且其内核和用户空间之间分隔的模糊性也给仿真带来了困难[15].
第3 类是不具备操作系统抽象的嵌入式设备,这类嵌入式设备大多只包含一个轻量级的操作系统库,此时应用程序往往直接访问硬件,并且静态链接到单个二进制文件中,如小型传感器.此类嵌入式设备不存在操作系统内核和应用程序代码之间的逻辑隔离.最近的固件重新托管工作也开始侧重重新托管第3 类操作系统.
如表1 所示,第1 类操作系统功能丰富,内存管理系统和文件系统完善,有明确的硬件系统调用接口,容易托管;第2 类操作系统的功能简单,驱动与系统边界模糊,不一定具备独立文件系统,内存管理系统不健全;第3 类操作系统功能更加简单,不存在内存管理系统和文件系统,应用程序可直接对硬件进行操作.根据Chen 等人[18]统计的开源固件映像的信息,第1 类固件约占总固件映像的50%,第2 类固件约占总固件映像的8%,第3 类固件约占总固件映像的42%.但需要注意,实际中更多闭源嵌入式设备,如工控固件和军用设备固件使用第2 类、第3 类嵌入式系统.
Table 1 Comparison of Firmware for Different Devices[4]表1 不同设备固件的比较[4]
在过去,仿真技术主要的用途是在硬件生产之前进行软件开发,从而减少产品开发时间和提高硬件与软件间的兼容性.如今,仿真技术则逐渐成为安全分析和逻辑调试的流行工具.
2005 年QEMU[17]架构首次实现了多种指令集架构跨平台仿真.QEMU 将整个基本块转换为主机系统的指令集然后执行转换之后的指令,从而实现全系统仿真.QEMU 不需要检查每个指令之间的中断,减少了指令转换开销,并且具备一定程度的外设仿真.这些优点使得QEMU 成为使用最广泛的指令仿真器之一.特别是2017 年的Unicorn[19]进一步简化QEMU 的系统仿真功能,只保留其指令仿真部分,同时增加了丰富的内存、寄存器和外设转发调试接口.因此,近十年的固件仿真器相关工作大多在QEMU和Unicorn 仿真器的基础上进行开发.此外,2019 年美国国家安全局开源了强大的逆向分析工具Ghidra,其包含多架构指令仿真器Ghidra Emulator[20].Ghidra Emulator 支持比QEMU 更多的指令集仿真,能够将逆向工程与仿真结合到同一个环境中,并允许用户自行添加新的指令集架构.
然而,随着物联网设备种类的增多,特别是外设种类的多样性和复杂化,无论是QEMU、Unicorn 还是Ghidra Emulator 均不足以支持全系统外设的仿真,所以近年来的研究更多地关注嵌入式设备仿真问题.
2014 年的AVATAR[21]首次提出了一个具有可扩展外设仿真、内存监控等多种功能的接口框架,该框架基于QEMU 将仿真器的执行与真实的硬件结合起来,即将无法仿真的外设的交互转发给真实设备,以此支持嵌入式设备固件进行全系统动态分析.AVATAR 也是最早将符号执行应用于嵌入式设备固件分析领域的工具,它拥有专门的插件接口对事件进行拦截和转发,通过符号执行检测固件中存在的漏洞.2014 年的PROSPECT[22]和2015 年的SURROGATES[23]与AVATAR 一样,均采用基于QEMU 的硬件转发方法来解决缺少外设仿真的问题.SURROGATES 则是在嵌入式设备总线和仿真器之间使用了一个低延迟的FPGA 桥接器,桥接器负责固件与外设之间的转发和状态传输,因此传输速度与AVATAR相比有所提升.
然而,这些“硬件在环”早期仿真器,由于硬件转发的存在导致仿真实现效率过低、难以大规模分析,需要工作人员对嵌入式设备逐个调试.2015 年的PANDA[24]是一个基于QEMU 全系统仿真器的开源平台,它的优点是增加了记录和重放的功能,可以记录所有的硬件交互,然后在全虚拟化的环境下进行重放.然而,其并未建立外设仿真模型,因此当重放结束后仍然无法判断后续硬件输入.在此基础上,2019 年的Pretender[25]通过使用机器学习来学习硬件交互记录进行建模,从而根据模型实现全系统仿真.然而,Pretender 本身仍然无法摆脱硬件依赖.
之后研究人员摒弃了效率依旧较低且无法自动化运行的硬件在环方法,尝试通过固件的重新托管技术牺牲掉部分虚拟运行的保真度来解决外设的仿真问题.2020 年的P2IM[26]首次提出基于外设交互的通用外设建模方案,并结合模糊测试向QEMU 仿真器提供外设的反馈,以此保证嵌入式设备固件的虚拟运行.该工具能够在不使用硬件或外设的详细知识的前提下,利用模糊器提供的输入模拟运行固件.但是P2IM 需要盲猜状态寄存器的响应,搜索范围过大,难以适用于复杂的外设而且会导致崩溃和死机.2021 年的μEmu[27]和Jetset[28]将符号执行框架S2E[9]或Angr[29]与固件重新托管技术结合来提高固件仿真效率.2021 年的DICE[30]在P2IM 的基础上增加DMA仿真功能,支持识别并生成DMA 输入来测试固件中跳过或无法访问的代码和状态,在不需要人工协助的情况下分析和测试DMA 相关的代码.
为了进一提高外设仿真模型的保真度,2022 年的最新的SEmu[31]提出了基于硬件描述手册来自动化构建高精准度外设仿真模型的方案.SEmu 通过自然语言处理技术将外设行为转换为一组结构化的条件规则,然后为每个外设建立一个模型.因此,SEmu拥有更好的仿真准确度,并能够进行固件代码合规性检查.
此外,为了进一步提高外设仿真模型的适用范围,Fuzzware[32]和Ember-IO[33]将部分硬件输入也作为模糊测试输入从而扩大模糊测试输入空间,以此提高模糊测试的代码覆盖率,但却引入了更多的漏报和误报.
除了通过外设仿真实现全系统模拟,还有研究人员通过替换外设依赖代码来实现全系统仿真,例如2020 年的HALucinator[34].HALucinator 观察到嵌入式设备固件与外设的交互通常是由硬件抽象层执行,所以它使用AVATAR2 和QEMU 来拦截硬件抽象层调用并进行替换.但AVATAR2 和QEMU 依赖标准驱动库并且通常需要一定的人工成本.
图5 显示了从2002 年至今经典的嵌入式固件仿真器的研究工作.最初,传统的固件仿真器的相关工作大多针对传统操作系统仿真,例如Linux,或者QEMU,需要大量人工不断添加新的硬件仿真支持,并不适合如今复杂多样的嵌入式设备和外设.后来,针对复杂多样的外设等挑战,相继产生了硬件在环、抽象、基于符号执行、基于模糊化、基于规则等更加先进自动化的嵌入式固件仿真器,不断优化的固件仿真器也提高了嵌入式固件的漏洞挖掘效率,使得嵌入式设备的安全性得以提升,也促进了动态分析技术的发展.
Fig.5 Technical implementation relationships between firmware simulators图5 固件仿真器之间的技术实现关系
仿真和分析一个嵌入式设备固件,首先需要获取固件.固件可以从供应商网站或Github 等第三方网站下载.当固件无法直接下载,或固件属于不易提取的专属文件格式时,可以使用网络捕获工具在固件更新期间捕获网络流量,从中提取固件.在获得固件之后,需要对固件解包以提取固件的关键信息,例如固件的指令集架构、固件基址、分析入口点、内存布局和控制流图等.固件解析包常用的工具有Binwalk[39]和BAT[40],Firmadyne[18]和Costin[41]工具均使用Binwalk 完成解包.虽然本文介绍的大多数固件仿真器默认固件的关键信息是已知的,但是固件获取和固件解包是仿真和分析一个嵌入式固件必不可少的第1 环.
在获得固件的关键信息之后,固件仿真器开始对固件进行仿真运行或重新托管.但大多数固件仿真器并不是完全自动化仿真,此时需要根据实际情况对固件仿真器进行设置.例如为新处理器添加支持说明;为硬件在环的固件仿真器配置外设交互;给外设建模,解决固件缺少代码的问题;设置功能识别或标签等.手动处理的固件仿真器需要在仿真之前进行部分设置,然后才能进入固件仿真运行或重新托管阶段.在此阶段可能会出现运行失败的问题,可能是寄存器分配错误、中断处理错误、计时器未处理、多线程未处理等,此时需要重新设置固件仿真器或者进入调试步骤.如果顺利解决问题,则固件仿真或重新托管成功,固件仿真器进入下一步骤.
当固件仿真器成功运行之后,我们需要使用模糊测试、控制流分析、污点分析、符号执行等固件仿真器支持的动态分析技术对固件进行漏洞挖掘.最后,挖掘到的漏洞仅存在于虚拟环境下运行的固件中.还需要在真实嵌入式设备中对漏洞进行分析和复现,确保漏洞真实存在.至此,嵌入式固件完整的仿真和分析工作完成.
在嵌入式固件的安全分析相关工作中,经常会出现系统仿真(system emulation)、系统模拟(system simulation)和固件重新托管(firmware re-hosting).部分研究人员可能会将这3 类工作混淆,但是这3 类工作是有区别的,特别是系统仿真和系统模拟之间的差距很大.系统仿真是指通过硬件和指令集仿真实现,使嵌入式设备固件能够在其他架构平台上执行.而系统模拟则只是针对设备常见的功能进行建模,并非对真实固件的执行环境进行仿真.固件重新托管则是指将固件重新托管到可以用来运行该固件的虚拟环境中.这个虚拟环境可以是通过系统仿真的虚拟环境,也可以是通过替换一些内部构件对系统进行建模的系统模拟的虚拟执行环境.
为了挖掘真实固件代码中的安全问题,本文主要针对围绕系统仿真或固件重新托管开展的工作进行介绍和总结.
本文参考 Fasano 等人[14]对专有名词进行释义:
1)虚拟执行环境(virtual execution environment):代码可以透明执行的软件环境.
2)硬件仿真系统(hardware emulation system):一种虚拟环境,目的是准确地重新创建1 个或多个特定硬件的特征,即上文提到的仿真器.
3)固件托管(firmware rehosting):为给定的嵌入式系统构件重组嵌入式系统来完成指定的分析任务的过程.托管的过程可能包含对固件的修改,其可以通过系统仿真来实现或者通过硬件依赖代码的替换来完成.
本文把具有外设接口和指令仿真功能的QEMU、Unicorn 等工具看作基础仿真平台,其他工具基于基础仿真平台进行改造或升级,有些工具依旧保持了全系统仿真,有些工具只保留了嵌入式系统的文件系统,有些工具则是实现了固件重新托管,但是本文将这些工具统称为嵌入式设备固件仿真器.
根据 Fasano 等人[14]的描述,仿真的层级可以从指令级到处理器内核,再到片上系统甚至包含到连接嵌入式系统的物理系统,范围很大,各层级保真度要求也不相同.本文中重点关注固件仿真中针对安全分析的基本要求.
例如,对于模糊测试来说,其要求测试对象具有可探测性,必须支持基本块级别的指令仿真,并且能够收集路径和基本块反馈[42].此外,为了保证程序可持续执行以及接受和处理测试程序,模糊测试[43]也需要一定程度的外设仿真,否则固件遇到硬件操作将出现异常行为.而污点分析需要检测对象的可检查性来识别保留输入影响固件的敏感部分的执行路径,以更好地发现漏洞,所以其对数据保真度要求更多,需要更好的指令和外设仿真能力.总而言之,安全分析领域下的固件仿真器的基本要求为:基本块级别的指令仿真和一定程度的外设仿真.
实际上,当前的固件仿真器一般针对特定安全分析目标,牺牲一部分的准确性来提高自动化程度或者适用性.具体的仿真要求不同,其针对不同的测试对象的优势往往不同.虽然最新的研究开始尝试大规模地自动化全系统仿真,但是该领域还有待发展和研究.因此,本文后续将进一步总结不同固件仿真器的优势和特点,以便固件仿真器的使用者根据不同用途选择合适的固件仿真器.
实现全系统的固件仿真主要需要解决2 大技术难点:一是处理器架构仿真,包括指令译码和处理器基础架构仿真;二是固件依赖的周围硬件设备仿真,即外设仿真.根据不同的固件仿真器侧重点不同,本节分别介绍基本的处理器架构仿真工具和通过替换函数解决硬件依赖的固件仿真工具,以及重点介绍各种不同方案的外设仿真器.
无论是系统仿真还是指令仿真均需要以指令仿真为基础.不同处理器的指令集往往不同,所以处理器仿真本质是指动态指令译码,即从原始架构指令动态译码为待测主机架构指令集指令.处理器指令译码和基础的处理器架构仿真都是固件仿真的基础.
常用的基础架构仿真器主要有Simics[44],QEMU[17]和Ghidra Emulator[20].2002 年的仿真器Simics[44]支持Alpha,x86-64,IA-64 ARM,MIPS,MSP430,PowerPC,SPARC-V8 以及x86 等多种架构的指令译码.并且Simics 提供配置管理、脚本编写、自动调试等内置功能来帮助构建仿真系统,并且Simics 允许任何2 条指令间的中断测试,指令级别具有保真度.在2016 年的DARPA 网络大挑战就是使用Simics 自动审查提交的文件来检查提交的二进制文件是否符合标准.
2005 年QEMU[17]推出的QEMU 指令译码是基于基本块级别的,它将整个基本块中的指令转换成中间代码后再整体转换为目标主机架构指令代码执行.因此,与Simics 相比,QEMU 不再需要检查每个指令间的中断,QEMU 利用执行缓存大大减少了译码转换开销,所以QEMU 工作得更快.QEMU 同样支持IA-32,x86,MIPS,SPARC,ARM,SH4,PowerPC,ETRAX CRIS,MicroBlaze 和RISCV 等多种架构,还提供部分常见的外设仿真.此外,Unicorn[19]进一步简化了QEMU 的系统仿真功能,只保留其指令仿真部分,同时增加丰富的内存、寄存器和外设转发调试接口.这使得QEMU 和Unicorn 成为使用最为广泛的全系统仿真器.如今的嵌入式固件仿真器大多都是在QEMU 和Unicorn 仿真器处理器架构的基础上进行的二次开发.
2019 年的Ghidra Emulato[20]是美国国家安全局开发的包含仿真工具的开源软件逆向工具,它的一大优势便是能够将逆向静态分析与指令仿真结合到同一个环境之中.并且Ghidra Emulato 支持比QEMU更多的指令集架构,并且允许用户自行添加新的指令集架构.Ghidra Emulator 介于Simics 和QEMU 之间,基于其自定义的子指令级P-Code 完成译码,每个译码后的机器指令可对应多个仿真器子指令,具有子指令级别的保真度.例如,最新的MetaEmu[38]通过Ghidra Emulator 解决车机固件常使用不常见处理器架构和自定义指令语言架构的难题.MetaEmu 能够为多个不同处理器指令架构的固件统一合成一套仿真环境,并进行符号执行、模糊测试等复杂的动态分析.2023 年的Icicle[37]同样基于Ghidra Emulato r 的P-Code 实现更细粒度的指令插桩,从而支持模糊测试更细粒度的执行反馈,如比较指令对比反馈等功能.
嵌入式设备不同于通用计算机系统,通用计算机系统大多硬件依赖较少,借助通用处理器架构仿真器即可实现全虚拟化执行.但嵌入式设备需要解决硬件环境仿真复杂的难题,所以本节介绍通过识别硬件依赖接口层进行转发替换,实现固件托管的仿真方案.经典的基于硬件依赖层替换的固件仿真器有Firmadyne[18],FirmAE[45],ECMO[46],HALucinator[34]和PMCU[47].
传统的嵌入式Linux 系统的第1 类固件有着明确的系统调用接口,例如Firmadyne,FirmAE 和ECMO 都是通过借助Linux 的系统调用接口实现常用的硬件转发替换仿真.但基于微控制器的固件缺乏标准的系统调用接口,其驱动行为更加多样化和碎片化.因此,2020 年HALucinator 提出通过对固件的硬件抽象层接口进行替换转发,可以适用于大部分裸机第3 类固件.HALucinator 使用Avatar2 和Unicorn 拦截固件对硬件抽象接口层的函数调用,将函数调用替换为软件实现的相似仿真功能.但其不仅需要使用库匹配工具识别嵌入式设备固件中的硬件抽象层函数,还需要人为编写替换函数,适用性受限且人工成本较高.
2021 年的PMCU[47]则直接使用嵌入式操作系统中的便携式操作系统接口POSIX 来实现转发替换.并且PMCU 直接将硬件架构指令依赖相关代码也进行了替换,从而实现将嵌入式源代码直接编译为目标主机代码,直接省去了指令译码的过程,大幅度提高了代码执行效率.但需要注意的是PMCU 需要依赖源代码进行插桩,其替换在本质上并不属于固件仿真器而是属于嵌入式系统源码仿真器.
基于替换的固件托管仿真方案,虽然相比原始外设仿真效率有所提高,保真度适中,但是存在人工成本较高且无法测试被替换代码的安全性问题.
由于物联网的快速发展,物联网设备外设种类的多样性成为现今固件全系统仿真工具的最主要技术挑战.如何自动化构建针对一个物联网设备高保真度的外设仿真模型是一项复杂且艰巨的任务.针对每个外设人工建模的方案显然无法满足自动化大规模的测试需求.因此,现阶段学术界通常以牺牲准确度和适用范围为代价,自动化构建各种外设仿真模型.
根据不同固件仿真器在处理外设建模时的应对方法不同,本节将固件仿真器分为硬件在环的固件仿真器、基于人为假设的仿真模型、基于符号执行的外设仿真模型、基于模糊测试的外设仿真模型和基于硬件手册的外设仿真模型这5 类.
2.3.1 硬件在环的固件仿真器
硬件在环HITL(hardware in the loop)是指使用固件仿真器执行指令,但是当需要对外设进行访问时,固件仿真器将其对外设的读写访问转发到实际硬件中进行交互.简而言之,硬件在环技术是将对硬件和外设的访问转发到实际物理设备上,来实现高保真度的模拟执行.经典的使用硬件在环方法的固件仿真器有AVATAR[21],PROSPECT[22],SURROGATES[23],和Inception[48].
如上所述,2014 年的AVATAR[21]和AVATAR2[35]是最早基于QEMU 设计的可实现硬件转发的框架,其可转发QEMU 对外设的读写访问到真实硬件,同样也可以兼容Angr[29]等动态程序分析工具.PROSPECT[22]通过与嵌入式设备进行正常的总线连接来转发仿真器与外设和硬件之间的交互.与AVATAR 相比,PROSPECT 这种通过总线连接来转发交互的方法提高了仿真器与真实物理硬件之间交互的效率,提高了仿真速度.SURROGATES[23]则是在主机PCI Express总线和被测系统之间使用了一个定制的低延迟的FPGA 桥.通过定制的低延迟FPGA 桥,SURROGATES的系统与外设硬件之间的转发和状态传输速度更快、更稳定,但同时提高了硬件成本.“硬件在环”本质上并不是实现外设仿真,而是通过转发硬件操作到真实设备而绕过外设仿真这一难题.因此,理论上其可以实现更准确的仿真执行效果,但受限于真实设备硬件的效率,硬件在环的固件仿真器难以支持模糊测试自动化测试,效率过低.尽管从AVATAR 到Inception 不断优化硬件在环的固件仿真器运行效率,但是依旧不能满足如今大规模固件自动动态分析的需要.
2019 年Pretender[25]试图将硬件在环和外设仿真分开.即首先记录固件在硬件在环时真实的硬件反馈,然后利用机器学习来生成不同的外设仿真模型从而支持全系统固件仿真.然而Pretender 仍然需要硬件依赖,在实际固件分析中分析人员难以获得原始的真实硬件设备.
2.3.2 基于人为假设的外设仿真模型
2020 年的P2IM[26]提出无硬件依赖的自动化外设仿真模型构建方法,其根据固件对外设的使用模式来推测外设寄存器类型,继而利用更加不同的外设寄存器类别来建立不同的外设反馈模型.实践证明,该方法可以成功重新托管外设简单的嵌入式设备固件.但由于其人为假设无法处理外设,特别是针对复杂的初始化判断时,其寄存器类别常常推断错误,成功率较低.2021 年的DICE[30]采取了类似的方法,通过对DMA 访问模式建模来自动化构建DMA 仿真模型,从而使固件仿真能够支持基于DMA 的外设.但其同样受限于过多的人为假设,无法适用于以太网和USB 等复杂设备的DMA 传输模拟.
2.3.3 基于符号执行的外设仿真模型
基于符号执行的外设仿真模型是指利用符号执行探索可达的路径范围,从而生成对应的路径约束来作为外设反馈模拟,代表性工作包括Laelaps[36],μEmu[27],Jetset[28].
2020 年,Laelasps[36]首先提出结合符号执行来进行外设反馈模拟的方案,即遇到未知外设读取则返回符号值,然后利用符号执行探索一定范围内的后续程序执行路径,从而确定合理的执行路径.但这种仿真一直需要符号执行在执行循环中,实际开销成本过大,特别是具体执行和符号执行切换时.
2021 年的μEmu[27]提出通过观察程序的异常状态从而自动化判断合理的执行路径,从而将其外设建模与模拟执行分离.即首先利用符号执行的学习合理路径约束生成外设反馈知识库,进而在仿真执行时利用此静态知识库即可,无需持续切换符号执行状态,避免了LaeLaps 的性能开销问题,并且其相比人为假设模型准确度更高.与μEmu 相似的基于路径约束条件来实现固件重新托管的工作是2021 年的Jetset[28],Jetset 使用符号执行自动推断嵌入式设备固件对其目标硬件的期望行为,并使用这种推断的行为来合成可用于在模拟器中执行固件的目标硬件设备的模型,从而实现固件的重新托管,它通过基于增量控制流图构建的搜索策略引导符号执行保证固件运行.
2.3.4 基于模糊测试反馈的外设仿真模型
虽然基于符号执行生成的路径约束可以支持构建外设仿真模型,但2022 年的Fuzzware[32]指出基于符号执行路径约束的仿真方案会过度限制可达路径,导致模糊测试时难以覆盖到一些特定程序执行路径.为了提高代码覆盖率,Fuzzware 提出了结合模糊测试的外设反馈方案,即只使用符号执行生成粗粒度的外设反馈约束,而不是直接使用路径约束的具体值,在模糊测试阶段允许输入各种满足约束的变异值,从而探索更多的可达路径.此方案相比于之前的基于符号执行路径约束的外设仿真模型,其在模糊测试阶段代码覆盖率提高更大,但也引入了更多的误报和漏报.
2023 年的Ember-IO[33]进一步提出了全模糊测试反馈方案,即不生成外设仿真模型,所有硬件输入均通过模糊测试生成,只是利用代码覆盖率更大的模糊测试值进行缓存来提高模糊测试的执行速度,从而进一步快速提高代码覆盖率,但与此同时引入了更多的误报和漏报.
2.3.5 硬件手册的外设仿真模型构建
2022 年的SEmu[31]指出之前的通用外设仿真模型无论是基于假设、符号执行或者是模糊测试均缺少真实外设的硬件逻辑如中断触发时机,因此只能实现近似的外设仿真,应用模糊测试时的误报和漏洞较高.为了进一步提高外设仿真模型的自动化,SEmu 利用自然语言处理技术将人类语言中的外设行为描述语句转换为一组结构化条件动作规则,然后通过运行时正确执行和链接这些规则,从而自动为固件执行期间访问的每个外设动态建立一个外设模型,以此在简单外设上达到接近100%的基本块级别的真机一致性.
表2 总结了目前最新的和经典的固件仿真器的分类及其核心技术特点.
Table 2 Classification and Feature of Common Firmware Emulators表2 常见的固件仿真器类别与特性
保真度是固件重新托管工作中非常重要的属性,所以本文决定引入指令执行保真度和数据内存保真度这二维保真度来对各种分析工具进行分类.这种分类下,我们不用详细区分DRAM、SRAM、闪存等各种内存,只需要简单区分内部内存和外部内存即可,其中外部内存可以被认为是外设.根据仿真器在仿真嵌入式设备与真实嵌入式系统运行的匹配程度,本文参考Wright 等人[15]的分类方法,将仿真器的执行仿真度分为黑盒执行保真度、模块执行保真度、函数执行保真度、基本块执行保真度、指令级执行保真度、周期级执行保真度、完美执行保真度.数据和内存保真度则是从最粗糙的粒度到最精细的粒度分为黑盒数据内存保真度、内存级数据内存保真度、寄存器级数据内存保真度和完美数据内存保真度.
保真度的分类是从嵌入式固件的角度看嵌入式设备固件仿真时的执行过程和内存与真实的嵌入式硬件设备的一致程度.执行保真度是指嵌入式固件的指令执行与真实的嵌入式固件指令执行相比,是完全相同的指令执行还是只保证外部行为模式相同而不保证指令执行的顺序和内容相同.数据内存保真度是指嵌入式固件在运行时各个周期的数据和内存与真实的嵌入式固件相比,是保证各个寄存器中的数据与真实嵌入式设备完全相同还是只保证输入和输出的数据与嵌入式设备完全相同.
黑盒执行保真度的仿真器在执行层面表现出和真实嵌入式系统运行相同的外部行为模式,但是它的内部可能仅执行与真实嵌入式系统执行的部分指令,或者直接不执行与真实嵌入式系统执行的任何相似指令.模块执行保真度会提供模块级别的保真度,例如Firmadyne[18]直接将原始固件的内核替换成自己的内核,固件的某些模块未被修改就执行,并且其他模块都被替换,所以这类固件重新托管技术仅提供模块级别的执行保真度.函数执行保真度是在函数级别对真实嵌入式系统进行建模,例如HALucinator[34]替换了函数级别的硬件抽象层调用,以实现外设的建模仿真.基本块执行保真度和指令级执行保真度分别是指仿真器在基本块层面和指令级层面精准地仿真嵌入式系统.周期级执行保真度则是完全模拟处理器指令周期,这是当前仿真器最高级别的执行保真度.完美执行保真度是指完全一致地仿真真实嵌入式系统的执行过程,但当前仿真器无法做到.
黑盒数据内存保真度意味着仿真器只有外部可见的数据是和真实嵌入式系统的数据相同的,即仿真器只能保证给定与真实嵌入式系统相同的输入时相同的输出.内存级数据内存保真度则是指在固件运行的每个执行点,仿真器的随机存取存储器等内部存储器与真实嵌入式设备的内部存储器一致.寄存器级数据内存保真度则是指内部存储器和寄存器在较高的执行保真度下都是正确的.而完美数据内存保真度则是指所有存储组件在所需的执行保真度级别上与给定的真实嵌入式系统完全一致,这是极为困难的,当前仿真器无法完美做到该级别数据内存保真度.
划分完具体的执行保真度和数据内存保真度后,本文为每个固件仿真器分配它的执行保真度和数据内存保真度.当然,每个固件仿真器都是在权衡自己需要解决的问题以及固件仿真器的性能和复杂性等方面之后选择的特定保真度.因为过高的保真度通常意味着较低的性能和较高的复杂性,所以根据保真度对不同固件仿真器进行分类并不是对固件仿真器的好坏进行分类,利用保真度对固件仿真器进行评估的目的是加深对不同固件仿真器间区别的理解.
如表3 所示,Simics[44]和Ghidra Emulator[20]属于指令级仿真,所以其具有指令级执行保真度,指令执行的特点使得Simics 和Ghidra Emulator 在数据内存保真度方面具有寄存器级数据内存保真度,它们属于保真度极高的指令仿真器.而QEMU[17]只支持基本块级别的指令译码,所以QEMU 的执行保真度和数据内存保真度都略低于Simics 和Ghidra Emulator仿真器,属于基本块执行保真度和内存级数据内存保真度.AVATAR2[35],Inception[48],SURROGATES[23]和PROSPECT[22]这些硬件在环仿真器,因为含有真实硬件,所以具有较高的执行保真度和数据内存保真度.
Table 3 Firmware emulator evaluation表3 固件仿真器评估
对于替换类型的固件仿真器,如HALucinator[34],其等效替换了硬件仿真功能,因此其虽然无法达到内存级别的数据一致性,但高于黑盒级别的功能一致性.
最后,对于通用外设仿真模型,其大多仅适用于模糊测试,只需保证程序控制流的基本一致性,数据层面大多低于黑盒级别,即无法仿真真实的硬件输出和功能,特别是对基于模糊测试反馈的外设仿真工具,如Fuzzware[32]等.此外,虽然Pretender[25]使用了硬件记录来生成仿真模型,其仿真模型的准确度相比无硬件依赖的方案有所提高,但其本质并未理解硬件本身的逻辑,因此其数据保真度无法达到内存级别.相比之下,SEmu[31]借助硬件手册,按照真实硬件逻辑生成反馈,其数据保真度大多数情况下可以到达内存级别.
不同的嵌入式固件仿真器各有优劣,使用者的需求不同,适合的固件仿真器也不同.如果单纯考虑仿真执行的准确度,可以考虑将实际硬件与仿真结合使用,那么AVATAR[21],AVATAR2[35],PROSPECT[22],SURROGATES[23],Muench[49]或Inception[48]这些“硬件在环”的固件仿真器,虽然不具备大规模自动化分析的能力,但是在人工配置成功后,可提供最接近真实硬件的仿真调试环境.
如果研究人员目标仅为支持固件模糊测试并且没有原始设备,那么可以选择通用的外设仿真器进行测试,例如P2IM[26],μEmu[27],Jetset[28],DICE[30],Fuzzware[32],Ember-IO[33],但存在需要人为处理误报的问题,特别是基于纯模糊测试反馈的外设仿真方案.虽然P2IM,μEmu,Jetset,Pretender 等仿真器的数据内存保真度有所降低,但是其自动化程度较高,对使用人员的门槛更低.如果需要考虑兼容固件仿真器的保真度,并且不考虑驱动层面的代码测试的话,HALucinator 等基于函数替换的固件托管效率和准确度更高.如果同时需要测试驱动代码,那么最新的SEmu 固件仿真器将是最好的选择,即使需要一定程度的人工成本来修正自然语言处理的结果是错误的.
虽然近年来业界对嵌入式设备固件仿真以及相关动态安全分析技术研究的逐渐兴起,并取得了一定的进展,然而由于物联网设备海量、异构和闭源等问题,相比于通用计算机软件,该领域仍处于起步阶段,面临诸多开放性技术挑战,亟待进一步研究.本节通过对大量工作的总结分析,指出现阶段固件仿真技术特别是针对安全的五大关键挑战讨论相应的解决方案,并在图6 中展示五大挑战对应的研究机遇.
现阶段大多数固件动态仿真器均需要开发者配置固件的基本内存布局、起始地址等信息,或者假设固件为elf 等包含符号表或完整动态运行所需信息格式的固件.但对于真实设备中保存的固件,通常为去除符号信息的纯二进制固件,其加载内存地址、起始运行地址的定义根据不同的微处理器型号均有所不同.同时,一些设备的启动代码、功能代码会分别编译为不同的固件存放在不同的存储器中.因此,真实设备固件代码通常难以被提取完整.待分析的固件代码会出现不完整和信息严重缺失的情况.此外,由于固件中语义缺失和硬件加载方式的差异,导致固件中包含的文件系统和其他关键代码难以被提取和分析.
然而,固件动态仿真必须依赖例如固件指令集架构、入口地址、内存布局等这些基本信息才可正确运行.
1)指令集判定与文件系统提取.Binwalk[39]固件解包工具使用capstone 反汇编程序确定固件的指令集等基本信息并提取其包含的文件系统.然而其只针对标准格式的Linux 系统的elf 文件,对于更多未知架构和系统例如Vxworks 固件等,目前仍然难以实现自动化固件指令集准确判断与文件系统还原.
2)内存布局与基址获取.Firmadyne[18]和Costin等人[41]基于Linux 的工具通过QEMU 定制Linux 内核统一的运行基址.然而针对其他系统固件,业界并没有提供有效的解决方案.
3)固件入口点获取.Ghidra 和IDA[50]针对二进制的固件具有扫描二进制文件的脚本,试图查找函数序言指令和函数返回等信息,生成并分析有向函数调用图,调用图中弱连接组件的任何根节点都可以被视为潜在的入口点.虽然固件通常具有多个有效的入口点,但是也必须从调用图技术返回的多个入口点中识别正确的仿真入口点.Firmadyne[18]和Costin等人[41]依旧是使用替换内核的方法成功绕过了寻找入口点.
这些基础信息缺失大大限制了现阶段学术界研发的动态分析工具面对真实固件的实用性.如何结合静态分析技术例如通过对固定编译变量的分析确定基址等方法实现自动化提取和补充构建丰富的固件信息知识库成为现阶段固件仿真器需要首先解决的技术难题之一.
现阶段虽然研究人员提出了多种自动化外设模型构建方案,但面对复杂的真实嵌入式设备特别是一些自定义硬件设备时,这些自动化构建技术的适用性会大大降低,与真实设备的一致性(保真度)也会大大下降,现阶段还有没有任何一个工具可以准确自动化构建例如USB,WiFi 一类的复杂外设.基于低保真度外设仿真模型的固件模拟与动态分析平台应用的漏洞检测技术效率和准确度均会大大下降.因此,现阶段面对复杂设备的固件分析仍然以人工辅助的静态分析为主,或者是人工定制化外设仿真模型.如何保证自动化以及脱离真实硬件环境下复杂外设仿真模型的高精度复现仍然是现阶段固件仿真技术面临的主要技术瓶颈.
随着固件仿真技术的发展,如本文第2 节介绍这些仿真方案大多面向不同层次,例如基于Linux 系统或硬件抽象层的替换方案和直接面向外设硬件仿真方案,或面向不同应用场景如MetaEmu 主要针对车机汽车控制单元,因此,其适用范围和适用类型各不相同,导致其针对不同的测试集效果差异大、稳定性不足.因此,对于固件模拟器的可扩展性主要分为2个方面,一方面是仿真平台是否能够兼容并且容易扩展到其他硬件架构的固件中,另一方面是仿真平台是否能够支持不同的或者新的动态分析技术,特别是模糊测试以外的技术,例如符号执行、污点分析等.
现阶段的固件仿真器大多仅针对特定架构例如ARM Cortex-M 以及特定的动态分析工具例如模糊测试技术.因此,对于固件仿真器的可扩展性主要可分为2 个方面:1)仿真平台是否能够兼容并且容易扩展到其他硬件架构的固件中;2)仿真平台是否能够支持不同的或者新的动态分析技术,特别是除了模糊测试以外的例如符号执行、污点分析等技术.
目前,固件仿真器工作大多仅关注固件能否成功地在虚拟化环境下执行,从而直接应用面向传统软件的动态分析技术来挖掘嵌入式固件中的漏洞.然而,由于固件自身的特性,专门用于仿真环境下的固件动态分析技术仍然有待进一步研究,其中包括模糊分析、污点分析和符号执行[16].
1)模糊测试.需要针对性地设计一个可处理具有实时性要求的固件的模糊框架,并探索根据固件的时间敏感行为来确定测试案例生成的优先级的策略;此外需要研究自动推断和模拟固件特定的输入格式或协议的技术,例如创建一个可以处理具有复杂或定制通信接口(如I2C、SPI 或CAN 总线)的固件的模糊测试解决方案,以提高嵌入式系统模糊测试的适用性.
2)污点分析.需要研究针对固件中特定数据类型、结构或编码方案的污点传播策略,以提高嵌入式系统中污点分析的精度和召回率;并且探索使用污点分析自动识别和跟踪固件中敏感数据的技术,如加密密钥或认证凭证等.
3)符号执行.利用选择性符号执行、状态合并或协同执行等技术[51-58],研究为大型或复杂固件扩展符号执行的策略;探索识别并模拟固件运行的特定假设或约束(如硬件配置、时钟速率或中断优先级)的方法,以实现更准确和高效的符号执行.
面对日益严峻的嵌入式设备安全威胁,自动化嵌入设备漏洞检测与分析成为迫在眉睫的安全需求.嵌入设备固件仿真已经成为嵌入式设备固件动态分析的首选方案.本文对嵌入式设备固件仿真器进行了全面总结,系统介绍了固件仿真器的发展历程以及工作流程基础信息,帮助读者更好地理解固件仿真器.然后对固件仿真器进行分类和评估,帮助固件仿真器使用者根据不同需求来选择适合的固件仿真器.最后对固件仿真器面临的挑战和机遇进行展望.为实现自动高效的安全分析,固件仿真器既需要追求自动化,又需要有较高的保真度.早期的固件仿真器在自动化和保真度方面往往不能兼得,现阶段随着程序分析与人工智能技术的不断发展,同时具备高自动化、高保真度和广泛适用的固件仿真分析平台将成为未来研究的热点.
作者贡献声明:张浩负责论文的整体撰写;申珊靛、刘鹏、杨泽霖负责部分章节的撰写;周威、张玉清负责论文的指导和修改.