Avatar: 一个支持嵌入式系统固件的动态安全分析的框架

 原文  Avatar: A Framework to Support Dynamic Security Analysis of Embedded Systems’ Firmwares


1. 前言

      以解决对嵌入式系统安全性的日益关注,即使在源码或硬件文档无法获取的情况下,对固件二进制进行准确的二进制分析是十分重要的。然而,在这方面的研究由于专用工具的缺乏而被阻碍了。例如,诸如动态污点跟踪以及符号化执行等动态分析是一种安全性分析的主要基础。不同于静态分析,动态分析依赖于在可控环境下执行软件的能力,通常在一个模拟器中进行。然而,对嵌入式设备的固件进行仿真需要实现被分析系统使用的所有硬件成分的精确模型。不幸的是,文献的缺乏与市场上硬件种类数目的庞大导致这一方法在现实中无法执行。

      在本文中,我们介绍了一个框架Avatar,该框架通过将仿真器的执行与真实的硬件结合起来,从而支持嵌入式设备的复杂动态分析。我们将首先介绍从模拟器到嵌入式设备的I/O访问的基础机制,然后描述几种通过动态优化介于这两种环境间的代码和数据的分布来增强系统性能的技术。最后,我们通过将我们的工具用于逆向工程、漏洞挖掘、硬编码后门检测这三种不同的安全场景来对其进行评估。为了展示Avatar的灵活性,我们将分别在一个GSM手机、一个硬盘引导程序以及一个无线传感器节点这三个完全不同的设备上进行分析。

 

2. 介绍

      一个嵌入式系统由一些相互依赖的硬件和软件成分组成,往往被设计用于与一个特定的环境(例如一辆汽车、一台电视、一个工业控制系统)交互。这些组件通常基于如cpu和总线控制器之类的基本块,它们集成到一个完整的自定义系统中。当大批量生产时,这种定制会降低成本。对于大批量的,定制的集成电路(ASIC)是首选的,因为它们允许根据特定的需求定制功能,从而降低成本,进行更好的集成,以及减少零件总数。这种芯片,也称为片上系统(SoC),通常是由标准的CPU核心构建的,标准的和定制的硬件模块都被添加了。通常称为IP核的标准块一般是单个组件的形式,可以集成到更复杂的设计中(例如,内存控制器或标准外设)。另一方面,定制硬件块通常是为特定的目的、设备和制造商开发的。例如,一个移动电话调制解调器可能包含一个自定义语音处理DSP,一个用于GSM专有硬件加密的加速器(A5算法)和一个现成的USB控制器。

      多年来,这样的SoC在复杂性上显著增加。现在,它们通常包括多处理器(MPSoC)和复杂的、定制的硬件设备。因此,几乎每个嵌入式系统都依赖于不同的、特定于应用程序的系统配置。作为这一现象的一个证明,嵌入式系统中最常见的CPU核心之一,ARM有限公司的网站列出了大约200个硅合作伙伴。大多数合作伙伴都在生产一些基于ARM内核的SoC产品系列。这导致了市场上有大量的不同系统,但这些系统都依赖于同一个CPU核心家庭。

      不幸的是,嵌入式设备的日益普及和它的连通性大大增加了它们遭受攻击和滥用的风险。这样的系统通常是在没有考虑安全的情况下设计的。此外,他们的工程团队的共同驱动力是可见的特点、短市场时间和成本的降低。因此,最近观察到嵌入式系统开发报告数量的增加,往往带来非常严重的后果([8], [11], [12], [19], [23], [25], [44], [46], [54], [60])。更糟糕的是,此类系统在安全相关的场景中经常扮演重要角色:它们通常是安全关键系统的一部分,集成在家庭网络中,或者它们负责处理个人用户信息。因此,开发能够更容易地分析嵌入式系统安全性的工具和技术是非常重要的。

      在传统的IT世界中,动态分析系统在许多安全活动中扮演着至关重要的角色——从恶意软件分析和逆向工程,到漏洞发现和事件处理。不幸的是,在嵌入式系统世界中没有一个等价的东西。如果攻击者破坏了设备的固件(例如,智能表或类似于stuxnet的攻击场景中的PLC[25]),甚至供应商通常也没有必要的工具来动态分析恶意代码的行为。

      动态分析允许用户克服许多静态分析的局限性(例如,加壳或混淆代码)和执行一系列更复杂的检查[24]——包括污点传播[33],[55],符号化执行和混合执行[10],[15],[22],脱壳[34],恶意软件沙盒[1],[5],和白盒模糊测试[28],[29]。

      不幸的是,所有这些技术及其优点在嵌入式系统的世界中仍然是不可用的。原因是,在大多数情况下,它们需要一个模拟器来执行代码,并可能监视或更改它的执行。但是,正如我们将在第二节中解释的那样,大量的定制和专有硬件组件使得构建一个精确的仿真器的任务变得非常困难。如果我们考虑到市场上每个嵌入式系统都应该开发额外的模块和硬件插件,我们很容易理解这种方法的不可行性。

      本文提出了一种填补这一空白的技术,克服了纯固件仿真的局限性。我们的工具名为Avatar,在物理设备和外部仿真器之间充当编排引擎。通过在嵌入式设备中注入一个特殊的软件代理,Avatar可以在模拟器中执行固件指令,同时将I/O操作导入物理硬件。由于完全模拟一个完整的嵌入式系统是不可行的,而且目前不可能通过在设备本身上运行代码来执行高级的动态分析,所以Avatar采用了混合方法。它利用真正的硬件来处理I/O操作,但从嵌入式设备中提取固件代码,并在外部机器上模拟它。

      总之,在本文中我们作出了以下贡献:

•我们展示了Avatar的设计和实现,一个新的动态分析框架,允许用户模拟嵌入式设备的固件。

•我们讨论了一些技术,可以用来优化系统的性能,并使Avatar适应用户的需要。我们还展示了复杂的动态分析应用 (如混合执行)可以在Avatar之上实现。

•我们通过将Avatar应用于三种不同的安全场景(包括逆向工程、漏洞挖掘和后门检测)来对其评估。为了展示系统的灵活性,每个测试都在一个完全不同的设备类型上执行。

 

3. 动态固件分析

      虽然嵌入式设备固件的安全性分析仍然是一个新兴的领域,但过去已经提出了一些技术来支持嵌入式系统的调试和故障排除。

      目前在许多嵌入式设备中都包含硬件调试功能(主要是围绕在线模拟器[13], [35], [42]和基于JTAG的硬件调试器[3])来简化调试过程。然而,分析仍然极具挑战性,并且常常需要专用的硬件和对测试系统的深刻了解。有几个调试接口存在,比如后台调试模式(BDM)[58]和ARM 内核级别调试和trace技术[58]。用于调试嵌入式设备的独立于体系结构的标准也存在,例如IEEE NEXUS标准 [4]。大多数这些技术允许用户访问、复制和操作内存和CPU核心的状态,插入断点,单步调试,以及收集指令或数据跟踪。

      在可用的情况下,硬件调试接口可用于执行特定类型的动态分析。但是,它们通常在功能上受到限制,不允许用户执行复杂的操作,例如污点传播或符号化执行。事实上,这些高级的动态分析技术需要一个指令集模拟器来解释嵌入式目标的固件。但是对于嵌入式系统的适当仿真,不只是CPU,还有所有外围设备都需要仿真。如果没有这样的支持,模拟的固件通常会挂起,崩溃,最好的情况下,也会执行出与实际硬件不同的行为。这样的偏差可能会导致诸如错误的内存映射,对本应由硬件更改的值进行主动轮询,或者缺少适当的硬件生成中断或DMA操作。

      为了克服这些问题,研究人员和工程师们已经解决了三种类型的解决方案,每种解决方案都有各自的局限性和缺点:

•完全的硬件仿真

      Chipounov[14]和Kuznetsov等[37]通过仿真PCI总线和返回符号值的网卡来分析设备驱动程序。这种方法的主要缺点是需要正确地模拟设备。虽然这对于解析完全的设备来说并不是很大的问题,就像大多数PC仿真软件支持的PCI网卡一样,但是这在嵌入式系统中是一个很大的挑战,而且在硬件没有文档记录的情况下是不可能的。不幸的是,缺乏文档是嵌入式世界的一个规则,特别是在复杂的私有SoC中。

      在某些情况下,在产品开发过程中开发了精确的系统模拟器,以允许固件开发团队在最终的硬件还不可用时开发软件。但是,这些模拟器通常无法在开发团队之外使用,它们通常不是为代码插装而设计的,因此无法执行基本的安全分析,比如污点或符号执行。

•硬件近似

      另一种方法是使用一个通用的、近似的硬件模型。例如,假定中断可以在任何时间发生,或者读取IO端口可以返回任何值。这种方法很容易实现,因为它不需要对真正的硬件有深入的了解,但是它明显地会导致误报(例如,返回真正的系统永远不会返回的值),或者模拟代码产生错误行为(当需要特定值时)。这种方法通常在分析小型的几百行代码的系统和程序时使用,就如Schlich [49]和Davidson等[22]展示的那样。然而,在更大的程序和复杂的外围设备上,这种方法必然会导致状态爆炸,从而无法进行任何有用的分析。

•固件调整

      另一种方法是调整固件(或提取其代码的有限部分),以便在通用仿真器中模拟它。虽然这在某些特定的情况下是可能的,例如基于linux的嵌入式设备,但是这种技术不允许进行整体分析,并且可能仍然受到自定义外围设备的限制。此外,这种方法对于不能很容易分割成独立部分的整体固件来说是不可能的——不幸的是,在低端嵌入式系统中非常常见[20]。

      在下一部分中,我们将介绍基于实际硬件和通用CPU仿真器组合的新型混合技术。我们的方法允许对嵌入式系统进行高级的动态分析,即使在他们的固件和硬件上很少有信息可用,或者在基本硬件调试支持不可用的情况下。这就打开了分析大量设备的可能性,在此之前不可能进行动态分析。

 

4. AVATAR

      Avatar是一个基于事件的仲裁框架,它协调了仿真器和目标物理设备之间的通信。

      Avatar的目标是支持复杂的嵌入式固件动态分析,以协助广泛的安全相关的活动,包括(但不限于)逆向工程、恶意软件分析、漏洞发现、漏洞评估、回溯获取和已知测试用例的根本原因分析。

A.系统架构

图2-1  Avatar概观

      图2-1总结了系统的体系结构:固件代码在运行在传统个人计算机上的被修改了的模拟器内部执行。任何IO访问都会被截获并转发给物理设备,而信号和中断则在设备上收集并被注入模拟器。

      内部架构完全基于事件,允许用户自定义的插件截取数据流,甚至当数据在仿真器和目标之间传递时对其进行修改。

      在最简单的情况下,Avatar需要一个后端与模拟器对话,另一个后端与目标系统对话,但是可以添加更多的插件来自动化,自定义和增强固件分析。在我们的原型中,我们开发了一个模拟器后端。这控制着S2E(选择性符号化执行引擎),这是一个开源平台,用于选择符号化执行二进制代码[15]。它建立在非常流行的开源系统模拟器Qemu[7]的基础之上。 Qemu支持许多处理器系列,如i386,x86-64,Arm,Mips等等。除了作为处理器模拟器之外,Qemu还可以模拟许多通常连接到中央处理器的硬件设备(如串口,网卡,显示器等)的行为。

      S2E利用被称为微型代码生成器(TCG)的Qemu的中间二进制代码表示,并且每当符号执行活动[39]时,动态地将TCG字节代码转换为低级虚拟机(LLVM)字节代码。 KLEE,实际的符号化执行引擎,随后会探索不同的执行路径,并跟踪每个符号值的路径约束[10]。对于某些符号输入,详尽地评估可能的状态可以被同化到模型检查,并且可以从而证明某个软件的一些属性[38]。

      尽管S2E使用二进制代码的TCG表示来生成LLVM代码,但是每个处理器体系结构都有其自身的复杂性,因此需要编写特定于体系结构的扩展来使S2E与新的处理器体系结构一起工作。由于我们关注的是嵌入式系统,而我们分析的所有系统都是ARM系统,所以我们更新和改进了现有的S2E不完整的ARM端口,以满足固件二进制文件动态分析的需要。

      为了更详细地控制代码的执行,S2E提供了一个功能强大的插件接口,可以实现几乎所有执行方面的检测。任何仿真事件(例如,基本块的翻译,指令翻译或执行,内存访问,处理器异常)都可以被插件拦截,然后插件可以根据其需要修改执行状态。这种模块化的架构让我们能够对固件行为进行动态分析,例如记录行为和对沙箱存储访问,执行子程序的实时迁移(参见第III-C节),符号化执行代码的特定部分以及检测漏洞(参见第五节)。

      S2E通过三个不同的控制接口与Avatar连接:第一个接口是使用GDB串行协议的GDB调试连接。Avatar使用通过GDB / MI协议控制的GDB实例连接到此接口。该连接用于对执行进行细粒度的控制,例如设置断点,单步执行以及查看寄存器值。第二个接口是Qemu的管理协议(QMP)接口,一个基于JSON的请求 - 响应协议。尽管通过这个接口可以实现详细的虚拟机控制,但它目前仅用于在运行时动态更改S2E的配置。这是由通过S2E的Lua接口对S2E进行访问来完成的,而Lua接口通过JSON请求中嵌入的Lua代码被调用。第三个接口是S2E的一个插件,只要执行内存访问就会被触发。然后,这个S2E插件将这个请求转发给Avatar,而Avatar又会对该内存访问进行处理(例如,将其发送到Avatar的插件),或转发给目标。

      尽管目前唯一可用的模拟器后端是用于Qemu / S2E的,但模拟器接口是通用的,允许方便地被其他模拟器添加。

      在目标设备这方面,我们开发了三个后端:

•使用GDB串行协议与GDB服务器通信的后端(例如,安装在设备或JTAG GDB服务器上的调试器存根)。

•一个用于支持通过telnet之类的协议对OpenOCD的JTAG调试接口进行底层访问的后端。

•通过优化的二进制协议(比GDB使用的verbose协议更高效)与自定义的Avatar调试器代理进行对话的后端。该代理可以安装在一个嵌入式设备中,该设备缺乏硬件调试支持(例如,没有硬件断点),或者是这种支持被永久停用。

     根据嵌入式设备硬件所提供的特性和调试功能,用户可以选择合适的目标后端。例如,在我们的实验中,我们使用OpenOCD后端连接到移动电话和Econotag的JTAG调试器,使用Avatar代理对硬盘固件进行动态分析。

      要分析固件,需要访问固件的设备。这可以是一个调试链接(例如JTAG),一种加载软件的方法或代码注入漏洞。在调试存根(例如GDB存根)被使用的情况下,还需要一个额外的通信通道,例如UART。

    B. 完全分离模式

    当Avatar第一次运行在一个之前未知的固件上时,它可以进行“全分离模式”。在这个配置中,整个固件代码在模拟器中执行,整个(内存)状态保存在物理设备中。换句话说,对于由模拟器执行的每条指令,所访问的内存地址将被取出并写入到嵌入式系统的实际内存中。与此同时,中断被物理系统中的调试存根截获并转发给模拟器。代码和内存是完全分离的,而化身负责将它们链接在一起。

      尽管该技术在理论上能够对未知的固件进行动态分析,但它也有一些实际的局限性。首先,执行速度非常慢。使用38400波特的串行调试通道,系统可以每秒执行大约5次内存访问,导致总体模拟速度降低到每秒10个指令。更糟糕的是,许多物理设备都有需要在很短的时间内执行的时间重要部分,否则执行将会失败,从而导致系统崩溃。例如,DRAM初始化、定时器精度和稳定性检查属于这一类。

      此外,严格的硬件轮询循环(例如,UARTread-with-timeout)在完全分离模式下变得非常缓慢。最后,定时中断(例如,时钟中断)会快速超载目标系统和模拟器之间的有限带宽。

      这些限制使得完整的分离方法只能用于分析有限数量的指令,或者当用户只想专注于更复杂的固件中的特定事件时。出于这个原因,Avatar支持在模拟器和真实设备之间进行任意的上下文切换。

    C. 上下文切换

      虽然可以在模拟器中从头到尾运行固件代码,但有时让固件在目标设备上运行一段时间是更有效的。例如,这允许不停地执行代码,直到达到一个特定的关注点,跳过可能需要实时执行的密集I/O操作或网络协议通信的初始化例程。在这种情况下,重要的是让目标设备运行固件,同时仍然监视与当前分析相关的代码区域的执行情况。Avatar执行任意上下文切换的能力使用户能够快速地将她的分析集中在代码的特定部分,而不需要模仿整个固件执行的缺点。

      在特定的关注点开始分析:在这种情况下,固件在物理设备上启动并执行,直到一个特定的预定义事件发生(例如,到达一个断点或引发一个异常)。此时,物理设备上的执行被挂起,状态(例如,CPU寄存器的内容)被转移到模拟器,在那里执行恢复。在第六节的c部分描述了这种转换的一个例子,其中移动电话基带芯片的固件被执行,直到手机接收到一个SMS,然后由Avatar在传输到模拟器中以执行进一步的分析。

      返回到硬件的执行:在模拟器上执行所需的分析之后,固件的执行可以转回到真实设备上。在这种情况下,任何保存在虚拟环境中的状态都被复制回物理设备。根据用户的需要,可以在稍后的阶段再次切换到模拟器。该方法在第六节的a部分中使用,其中一个硬盘的固件在模拟器中启动,稍后又切换回磁盘。

D. 中断处理

      软件中断不会为我们的框架带来问题,因为它们是由固件代码发出的,而模拟器会直接调用相应的中断处理程序。但是,硬件中断需要被限制在真正的硬件中,并被转发给模拟器。在这种情况下,嵌入式系统中的存根接收到中断并将它们转发到Avatar的目标后端。最后,使用模拟器后端,Avatar暂停固件执行并在模拟器中注入中断。

      根据产生中断的情况,我们区分三种不同的情况:

•指示任务完成的硬件中断。这些中断由一个设备发出,用来指示由代码发起的特定任务已经完成。例如,UART发送中断指示发送缓冲区已成功传输。这种类型的中断很容易处理,因为它只需要从目标转发到模拟器。

•定期硬件中断,例如,定时器通知。这些中断可以被转发到仿真器,但是它们的频率需要缩小到仿真器的实际执行速度。在模拟器中两个中断之间执行的等效指令数应该与它在正常运行的目标上执行时相同。在我们当前的实现中,一个Avatar插件可以检测到周期性的中断并向用户报告他们的信息,用户可以决定如何处理每个类。例如,用户可以指示Avatar在设备上停止时钟中断,并在模拟器上生成它们(以正确的频率),从而节省带宽和增加分析性能。

•硬件中断通知外部事件。例如,UART的接收中断表明可以使用UART缓冲区上的新数据。这些中断的仿真策略取决于外部事件的频率。对于需要提前活动的事件(例如,响应触发中断的请求-响应协议),可以使用简单的转发策略。对于经常发生的不相关事件(例如,当模拟器中的处理程序无法在下一次中断发生前处理中断时),用户可以选择是否要抑制其中的一些中断或通过将处理程序本身迁移回嵌入式设备来处理中断(参见第4节)。

      虽然Avatar直接进行中断转发没有任何问题,但是当用户需要调整框架以处理特定的情况(例如,常规的或非常频繁的中断)时,存根需要能够区分它们。不幸的是,这项任务往往很困难。

      中断去复用:在传统的基于x86的个人计算机中,有一个标准的中断控制器,可以处理来自每个设备和外围设备的中断行。然而,在基于arm的系统中,只有两个直接连接到处理器的中断行:IRQ和FIQ。由于这种嵌入式设备经常使用一个中断多路复用器(或控制器)外围设备,它通常被包括在同一芯片上的硬件块(“IP核”)。对于用户的一个缺点是,在调用中断向量例程的时候,所有中断信号仍然是多路复用的。特定中断多路复用器的驱动程序将查询底层硬件多路复用器,以识别实际触发哪个行,然后将事件转发给注册为该中断的处理程序。

      现在,假设用户想要指示Avatar在设备(例如,计时器)上抑制某个特定的中断,而仍然通过与需要转发给仿真器的重要硬件事件关联的内容。在这种情况下,代理需要基于中断类型作出决定,这在收到中断时不幸是不可用的。

      在这种情况下,用户需要分解中断向量处理程序,并跟踪代码流,直到中断控制器驱动程序的代码进入不同的处理每个设备中断的函数。在这一点上,她可以指定这些程序指向Avatar,它可以终止中断向量的执行,并将信号发送给已识别中断的代理。然后代理将中断事件发送给Avatar。现在,Avatar的目标后端可以通过指示代理删除相应的事件来抑制特定的中断。

E. 重演硬件交互

      对于固件来说,有几个部分只需要与专用外围设备进行有限的交互是很常见的。在这种情况下,I/O操作可以被Avatar记录,并且在接下来的固件执行过程中透明地重放。

      这允许用户在没有与物理设备交互的瓶颈的情况下测试固件。在这种操作模式下,固件本身或其中部分(例如,应用程序)可以被显著地改变,只要I/O交互的顺序没有被修改。这是恢复快照的一个主要优势,它需要完整的代码路径,直到快照点被执行以确保外围设备处于快照期待其在的状态下。


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值