Device-agnostic Firmware Execution is Possible: A Concolic Execution Approach for Peripheral Emulation
Abstract
-
Key insight:
- 不将任何有关设备的特定信息编码到模拟器中。相反,Laelaps通过符号执行辅助的外围仿真来推断固件的预期行为并生成适当的输入以动态地引导具体的执行
-
Target:
- 使Laelaps能够运行不同的固件,而无需对目标设备的先验知识。
-
Capability:
- 在模拟器上应用了动态分析技术,并识别了自注入和现实世界的漏洞
Introduction
-
Dynamically analyzing embedded firmware
- 它们要么是特别的、与实际设备紧密耦合的,要么依赖于抽象层(如Linux内核)。
- 特别是,P2IM将外设返回的值限制为所有可能值的一个小子集。这从根本上受到具体执行性质的限制
-
Methods
- 利用符号执行和可满足性模理论(SMT)来推断来自外设的预期输入,并动态地将它们提供给被模拟的固件
-
Aim
- 在不依赖实际设备的情况下执行非linux固件
-
Design
Background
-
ARM Cortex-M Controller
- difference
- Cortex-M 处理器不支持 MMU.
- 这意味着应用程序代码和操作系统代码混合在一个平面内存地址空间中。
- Cortex-M 处理器不支持 MMU.
- difference
-
Firmware Execution
- 4 阶段
- 1 设备启动
- 2 底层系统启动
- 3 RTOS 初始化
- 4 任务的执行
- 固件的执行高度依赖底层硬件,而这种硬件的不确定性已经成为开发通用模拟器的最大障碍
- 固件执行中有多个有效的执行路径
- 一条错误选择的道路仍然可以通向成功
仿真进行分析。
- 4 阶段
-
DSE
- Laelaps的核心
- 外围仿真的Concolic执行方法。
- 提出了一套特定于外围设备的搜索启发式算法来缓解路径爆炸
- Laelaps的核心
Overview
-
Function Gap
- QEMU只支持** 2块TI Stellaris评估板和1块ARM SSE-200子系统设备**
- 对于不受支持的设备,QEMU只模拟ARM定义的核心外围设备
- Gap1
- 当处理器访问一个未实现的外围寄存器时,QEMU不知道如何响应
- Gap2
- QEMU缺乏未实现外围设备的逻辑,因此不知道什么时候发送中断请求
-
Motivating Observations
- 大量的外设访问不会显著影响固件的执行
- 如果影响固件执行对执行路径有重要影响=> 正确建模
-
Overview
-
基于具体的执行来设计系统,但是使用符号执行来运行小的代码片段来为未实现的外围输入计算合适的值。
-
Laelaps只需要设备的基本信息就可以初始化执行环境。
-
QEMU翻译和模拟固件的每个指令,直到有一个读操作到一个未实现的内存。目标是预测一个适当的反应
-
外围写操作被忽略,因为它们不会以任何方式影响程序状态.
-
在符号执行期间,每一个未实现的外围设备
访问被符号化(S2),产生一个符号列表。每次
当遇到分支时,运行路径选择算法(S3/4)
选择最有希望的路径(见§4.3)。 -
E1: Synchronous exception (e.g., software interrupt)
E2: Exception return
E3: Long loop (e.g., memcpy)
E4: Reaching the limit of executed branches- E1和E2终止符号执行
- E3可能会消耗大量时间,执行应该转移到具体引擎
-
当符号执行终止时,计算将执行导航到当前路径(S5)的符号列表的值,并将解决的值提供给QEMU
-
Laelaps通过在QEMU和符号执行传递之间不断切换来推动固件执行
-
提供一个平台,使固件执行到适合进一步动态分析的状态.
-
Laelaps system design
-
State Transfer
- 每当检测到一个未实现的外设读取时,程序状态就会转移到符号执行引擎;
- 同步处理器上下文(通用寄存器、系统寄存器)
-
Symbolic Execution
-
BASIC RULE #1
-
符号执行引擎由未实现的外围读取操作调用
-
即使外设地址已经被提前访问=>仍然分配一个新的符号
- 因为外设内存的易失性
-
Results: 不同的地址得到不同的符号)和时间(不同的时间得到不同的符号.
-
BASIC RULE #2
-
执行应该始终坚持原始的执行模式
-
对于每个需要上下文切换的显式指令,将立即终止符号执行,并将执行转移到QEMU
-
BASIC RULE #3
-
QEMU在重放缓冲的符号值时不应该采取任何非对称异常。
-
基本规则是QEMU不接受任何例外情况,直到所有已解符号被使用。
-
Unrecognized Instructions
-
不影响程序控制的未被识别的指令流
- 用NOP指令替换它们
-
对于直接更改控制流的未识别指令
- 更新通用寄存器=>终止符号执行并切换到QEMU
-
Long Loop Detection
-
如果检测到一个长循环,该执行将被强制转移到QEMU
- Laelaps根据最近执行的基本块维护执行跟踪,并找到最长的重复周期
- threshold ==5 => 符号执行终止
-
-
CPSA
- challenging :
-
一个希望的路径是通过固件的内部检查并避免中断固件执行的路径
-
缺乏关于数据结构和控制流的高级语义信息
-
Two parameters
- Context_Depth指定符号引擎在调用约束求解器并返回到QEMU之前必须积累的分支数。
- Forward_Depth是符号引擎可以从一个分支推进的最大基本块数。当Context_Depth设置为2时,每个符号执行传递决定两个分支的结果(从0x424到0x454和0x800到0x838)。
- Forward_Depth设置为3时,符号引擎将为每个分支探索多达三个未来步骤。当在一个步骤中遇到一个新的分支时,将探索这两个分支。
-
解释性示例
-
NXP的SDK 略
-
启发式1
-
上下文保存。Laelaps通过在QEMU和符号执行传递之间不断切换来引导固件执行向前移动。
-
Inspired by speculative symbolic execution(S2PF: Speculative Symbolic PathFinder. ACM SIGSOFT Software Engineering)
-
Context_Depth可以作为一个在保真度和性能之间进行权衡的可调参数。
-
启发式2
-
指定一个参数Forward_Depth,这是符号引擎可以从一个分支推进的最大步骤数
-
在Forward_Depth步骤中,一个分支可能会导致多个路径。如果所有这些路径都有一个无限的循环,那么这个分支将被丢弃。
-
为了识别一个无限循环,符号引擎维护所探索路径的执行轨迹和状态。然后,它会比较每个路径中的执行状态。如果任意两种状态相同,这意味着处理器寄存器在不同的时间不会发生变化,将相应的路径视为一个无限循环。
-
可能会错误地过滤掉一条似乎是无限循环的合法路径,例如:
- 一段代码可能会不断地查询RAM中的标志,它只由中断处理程序更改。
-
启发式3
-
确定新路径的优先级
-
启发式4
-
倒回路径
-
- challenging :
-
插入中断
- 如何支持固件的顺序执行=>即使固件访问未实现的外设 gap1
- 除了生成可供固件获取的数据外,外设还可以通过中断机制在数据准备就绪时通知固件 => gap2
-
限制和缓解
- 数据输入:
- 动态分析中,这些输入通道被拦截并输入手动生成的测试用例
- 缺乏整体分析:
- 在符号引擎中保留多达Context_Depth的分支来保存上下文信息。
- Context_Depth不能设置得太大,因为它会显著减慢性能。fan
- 数据输入:
Implementation
- 开发了基于QEMU和angr的Laelaps原型,它们分别是具体的执行引擎和符号执行引擎
- 为了方便QEMU和Angr之间的状态传输,集成了Avatar,一个无缝协调的Python框架,包括多个动态分析平台,包括QEMU、真实设备、angr、Panda等。工具继承了Avatar的状态传输接口,增强了Avatar处理Cortex-M设备的能力,实现了QEMU和angr之间的内存同步机制,在angr之上开发了拟议的CPSA,并向固件分析师导出了一个易于使用的Python接口。