PyVEX 是 VEX IR 的 Python 绑定。
项目仓库:https://github.com/angr/pyvex
文档:https://api.angr.io/projects/pyvex/en/latest/
PyVEX 可以通过 pip 安装:
pip install pyvex
import pyvex
import archinfo
# translate an AMD64 basic block (of nops) at 0x400400 into VEX
irsb = pyvex.lift(b"\x90\x90\x90\x90\x90", 0x400400, archinfo.ArchAMD64())
# pretty-print the basic block
irsb.pp()
# this is the IR Expression of the jump target of the unconditional exit at the end of the basic block
print(irsb.next)
# this is the type of the unconditional exit (i.e., a call, ret, syscall, etc)
print(irsb.jumpkind)
# you can also pretty-print it
irsb.next.pp()
# iterate through each statement and print all the statements
for stmt in irsb.statements:
stmt.pp()
# pretty-print the IR expression representing the data, and the *type* of that IR expression written by every store statement
import pyvex
for stmt in irsb.statements:
if isinstance(stmt, pyvex.IRStmt.Store):
print("Data:", end="")
stmt.data.pp()
print("")
print("Type:", end="")
print(stmt.data.result_type)
print("")
# pretty-print the condition and jump target of every conditional exit from the basic block
for stmt in irsb.statements:
if isinstance(stmt, pyvex.IRStmt.Exit):
print("Condition:", end="")
stmt.guard.pp()
print("")
print("Target:", end="")
stmt.dst.pp()
print("")
# these are the types of every temp in the IRSB
print(irsb.tyenv.types)
# here is one way to get the type of temp 0
print(irsb.tyenv.types[0])
请记住,这是基本块的语法表示。也就是说,它会告诉你这个块的含义,但你没有任何上下文来说明,例如,存储指令实际写入了什么实际数据。
为了处理广泛多样的架构,在中间表示上进行分析是很有用的。 IR 在处理不同架构时抽象掉了若干架构差异,从而允许在所有架构上运行单一分析:
rax 存储在该内存空间中从地址 16 开始的位置)。对于 IR 有很多选择。我们使用 VEX,因为将二进制代码提升为 VEX 得到了相当好的支持。 VEX 是一种与架构无关、无副作用的表示,适用于多种目标机器语言。 它将机器代码抽象为一种旨在使程序分析更容易的表示。 这种表示有五大类对象:
t0 开始编号。这些临时变量是强类型的(即,“64 位整数”或“32 位浮点数”)。VEX IR 实际上在 VEX 仓库中的 libvex_ir.h 文件(https://github.com/angr/vex/blob/dev/pub/libvex_ir.h)中有相当完善的文档。为了方便起见,我们将详细介绍一些你可能会经常接触的 VEX 部分。首先,这里有一些 IR 表达式:
| IR 表达式 | 求值结果 | VEX 输出示例 |
|---|---|---|
| Constant | 一个常量值。 | 0x4:I32 |
| Read Temp | 存储在 VEX 临时变量中的值。 | RdTmp(t10) |
| Get Register | 存储在寄存器中的值。 | GET:I32(16) |
| Load Memory | 存储在某个内存地址处的值,该地址由另一个 IR 表达式指定。 | LDle:I32 / LDbe:I64 |
| Operation | 将指定的 IR 操作应用于指定的 IR 表达式参数后得到的结果。 | Add32 |
| If-Then-Else | 如果给定的 IR 表达式求值为 0,则返回一个 IR 表达式。否则,返回另一个。 | ITE |
| Helper Function | VEX 对某些操作使用 C 辅助函数,例如计算某些架构的条件标志寄存器。这些函数返回 IR 表达式。 | function_name() |
然后,这些表达式又会被用于 IR 语句中。以下是一些常见的:
| IR 语句 | 含义 | VEX 输出示例 |
|---|---|---|
| Write Temp | 将 VEX 临时变量设置为给定 IR 表达式的值。 | WrTmp(t1) = (IR Expression) |
| Put Register | 使用给定 IR 表达式的值更新寄存器。 | PUT(16) = (IR Expression) |
| Store Memory | 使用一个值(同样由 IR 表达式给出)更新由 IR 表达式给出的内存位置。 | STle(0x1000) = (IR Expression) |
| Exit | 从基本块进行条件退出,跳转目标由 IR 表达式指定。条件由 IR 表达式指定。 | if (condition) goto (Boring) 0x4000A00:I32 |
下面给出了一个在 ARM 上的 IR 转换示例。在该示例中,减法操作被转换为一个包含 5 个 IR 语句的单一 IR 块,每个语句至少包含一个 IR 表达式(尽管在现实中,一个 IR 块通常由不止一条指令组成)。寄存器名称被转换为赋予 GET 表达式和 PUT 语句的数值索引。
细心的读者会注意到,实际的减法由该块的前 4 个 IR 语句建模,而将程序计数器递增以指向下一条指令(在本例中位于 0x59FC8)则由最后一条语句建模。
以下 ARM 指令:
subs R2, R2, #8
变为以下 VEX IR:
t0 = GET:I32(16)
t1 = 0x8:I32
t3 = Sub32(t0,t1)
PUT(16) = t3
PUT(68) = 0x59FC8:I32
很酷的东西!
如果你在学术工作中使用 PyVEX,请引用其开发所对应的论文:
@article{shoshitaishvili2015firmalice,
title={Firmalice - Automatic Detection of Authentication Bypass Vulnerabilities in Binary Firmware},
author={Shoshitaishvili, Yan and Wang, Ruoyu and Hauser, Christophe and Kruegel, Christopher and Vigna, Giovanni},
booktitle={NDSS},
year={2015}
}