[机翻] [ABD] 07_Miasm框架详解

07 Miasm 框架详解

https://github.com/malrev/ABD

本文档深入介绍 Miasm 逆向工程框架的架构、IR 结构、核心 API 体系,以及代码示例与实践要点。

⚠️ API 版本说明:本文档中的 Miasm API 代码示例已于 2026-07 更新至 Miasm 0.1.5+ 版本。课程原始材料使用的是 0.1.3 版本(2020年),部分 API 已发生 breaking change。更新处以 【更新】 标注,旧版用法以 【旧版 v0.1.3】 标注。


目录

  1. Miasm 简介
  2. 优势与弱点
  3. Miasm 代码示例
  4. Miasm 反汇编结构
  5. Miasm IR 结构
  6. Miasm IR 元素表
  7. Miasm IR 指令转换示例
  8. 核心 Miasm API 体系
  9. 参考资料

一、Miasm 简介

Miasm 是一个开源的逆向工程框架,由 CEA(法国原子能委员会)开发并维护,提供完整的 Python 接口。

核心特性

  • 二进制分析工具:提供从二进制加载、反汇编、IR 提升到符号执行的完整流水线。
  • Python 接口:所有功能均可通过 Python 调用,便于快速原型开发与自动化分析。
  • 多格式支持:支持多种文件格式(ELF, PE, Mach-O 等)。
  • 多架构支持:支持 x86, x86_64, ARM, ARM64, MIPS, PowerPC 等架构。
  • 丰富的功能集
功能 说明
Symbolic Execution(符号执行) 在 IRCFG 上执行符号化计算,收集路径约束
Concolic Execution(混合执行) 结合具体执行与符号执行,探索程序路径
Program Slicing(程序切片) 从程序中提取与特定变量相关的代码切片
Emulation (JIT)(JIT 模拟) 通过 JIT 编译高效模拟指令执行
Simplification(简化) 对 IR 表达式与 IRCFG 进行语义等价简化

二、优势与弱点

1. 优势

  • 比 Angr 更好的向后兼容性:Miasm 的 API 相对稳定,版本升级时的破坏性变更较少,已有分析脚本更易维护。
  • 比 Triton 更丰富的功能:Miasm 提供了从静态反汇编到动态符号执行的全栈能力,功能覆盖面广于专注动态符号执行的 Triton。

【更新 2026-07】上述比较基于 2020 年版本。当前状态:

  • Miasm:已更新至 0.1.5+,经历了 irlifter 等重命名(见本文档其他部分的 【更新】 标注),但仍保持相对稳定。
  • angr:已更新至 9.2.223,经历了多次重大 breaking changes(angr 7/8/9.1),包括 PathGroupSimulationManagerstate.sestate.solverCFGAccurateCFGEmulated 等。向后兼容性确实不如 Miasm。
  • Triton:已更新至 v0.9+,核心 API(TritonContextInstructionprocessing())完全向后兼容,2020 年代码可直接运行。新增了 Bitwuzla 求解器、LLVM 提升等功能。功能仍然聚焦于动态符号执行。

2. 弱点

  • 内存中值的简化性差:Miasm 的表达式简化器(expr_simp)对涉及内存读取的复杂表达式简化能力有限,难以将多层间接内存访问归约为简单形式。
  • 难以从 Miasm IR 还原回 x86 代码:Miasm IR 与 x86 之间的双向转换并非直接支持。若需从 IR 重新生成可执行的 x86 代码,需经过多步翻译链:
x86 汇编 → Miasm IR → LLVM IR → x86 汇编

这条翻译链的每一步都可能引入信息丢失或不一致,且 LLVM IR → x86 的代码生成质量受限于 LLVM 后端对 Miasm IR 语义的还原程度。

  • 自动分析无法扩展到整个二进制:Miasm 的符号执行、数据流分析等技术在面对大规模二进制(整个可执行文件)时会遭遇路径爆炸与性能瓶颈,通常只能应用于函数级或基本块级片段。

三、Miasm 代码示例

以下展示 Miasm 的标准使用流程:加载二进制 → 反汇编 → 提升 IR → 符号执行。

from miasm.analysis.binary import Container
from miasm.analysis.machine import Machine
from miasm.ir.symbexec import SymbolicExecutionEngine
from miasm.core.locationdb import LocationDB

# 1. 创建位置数据库(LocationDB),用于管理地址与标签的映射
loc_db = LocationDB()

# 2. 加载二进制文件
with open('target.bin', 'rb') as fstream:
    # Container.from_stream 解析文件格式(ELF/PE/Mach-O),提取入口点、节区等信息
    cont = Container.from_stream(fstream, loc_db)
    # Machine 根据二进制架构(如 'x86_32', 'x86_64', 'arml')创建对应的机器实例
    machine = Machine(cont.arch)

# 3. 反汇编:从入口点开始,生成汇编级控制流图(AsmCFG)
#    dis_engine 是反汇编引擎,dis_multiblock 处理多基本块(含循环、间接跳转)
mdis = machine.dis_engine(cont.bin_stream, loc_db=cont.loc_db)
asmcfg = mdis.dis_multiblock(cont.entry_point)

# 4. 提升:将 AsmCFG 转换为 IR 级控制流图(IRCFG)
#    【更新】machine.ir() → machine.lifter(),IR 分析器重命名为 Lifter
#    【旧版 v0.1.3】machine.ir() 返回架构对应的 IR 分析器(ira)
#    【旧版 v0.1.3】ir = machine.ir(cont.loc_db)
lifter = machine.lifter(cont.loc_db)
#    【更新】ir.new_ircfg_from_asmcfg() → lifter.new_ircfg_from_asmcfg()
ircfg = lifter.new_ircfg_from_asmcfg(asmcfg)

# 5. 为入口点添加可读标签
cont.loc_db.add_location(offset=cont.entry_point, name='entrypoint')

# 6. 符号执行:创建 SymbolicExecutionEngine 实例
#    【更新】SymbolicExecutionEngine(ir, ...) → SymbolicExecutionEngine(lifter)
#    【旧版 v0.1.3】第二个参数为符号化信息(symbolic init),可指定哪些寄存器/内存为符号变量
#    【旧版 v0.1.3】sb = SymbolicExecutionEngine(ir, ...)
#    【更新】v0.1.5+ 中 symbols 参数不再在构造函数中传递,改用 sb.symbols 设置
sb = SymbolicExecutionEngine(lifter)

# 7. 执行符号执行
#    run_block_at 从指定地址开始执行一个基本块,返回 PC(程序计数器)值
sb.run_block_at(ircfg, cont.entry_point)

流程总结

Container.from_stream()  →  Machine()  →  dis_engine()  →  dis_multiblock()
      ↓                       ↓               ↓                  ↓
   二进制加载              架构选择         反汇编引擎         AsmCFG
                                                                    ↓
lifter.new_ircfg_from_asmcfg()  →  SymbolicExecutionEngine()  →  run_block_at()
      ↓                                                              ↓
   IRCFG                                                       符号执行结果

【更新】流程图中 ir.new_ircfg_from_asmcfg() 已更新为 lifter.new_ircfg_from_asmcfg()
【旧版 v0.1.3】ir.new_ircfg_from_asmcfg()


四、Miasm 反汇编结构

Miasm 的反汇编结果以控制流图形式组织:

AsmCFG(汇编级控制流图)

  • AsmCFG:控制流图(Control Flow Graph),由汇编级基本块(AsmBlock)组成。
  • AsmBlock:基本块(Basic Block),即一段顺序执行、无内部跳转的汇编指令序列。
AsmCFG(汇编级控制流图)
 ├── AsmBlock(基本块 1)
 │    ├── 指令 1: mov eax, 0x1
 │    ├── 指令 2: add eax, 0x2
 │    └── 指令 3: jz 0x401000        ← 跳转指令,基本块边界
 │
 ├── AsmBlock(基本块 2)           ← 跳转目标
 │    ├── 指令 4: xor eax, eax
 │    └── 指令 5: ret
 │
 └── AsmBlock(基本块 3)           ← fall-through
      ├── 指令 6: sub eax, 0x1
      └── 指令 7: jmp 0x401020

AsmCFG 的特点

  • 节点为 AsmBlock,边为控制流转移(跳转、fall-through)。
  • dis_multiblock() 能够处理复杂控制流,包括循环、间接跳转的近似解析。

五、Miasm IR 结构

将 AsmCFG 提升为 IR 后,得到 IRCFG。Miasm 的 IR 结构层次清晰:

层次结构

  • IRCFG:IR 级控制流图,由 IRBlock 组成。
  • IRBlock:IR 基本块,包含若干 AssignBlock。
  • AssignBlock:赋值块,由若干 dst = src 赋值组成,采用 SSA 形式。
IRCFG(IR 级控制流图)
 ├── IRBlock(IR 基本块 1)
 │    ├── AssignBlock: dst = src, dst = src, ...     ← 对应一条指令的副作用
 │    └── AssignBlock: dst = src, dst = src, ...     ← 对应下一条指令
 │
 └── IRBlock(IR 基本块 2)
      ├── AssignBlock: dst = src, dst = src, ...
      └── AssignBlock: dst = src, dst = src, ...

AssignBlock 的含义

  • 每条汇编指令被提升为一个 AssignBlock,包含该指令的所有副作用。
  • 例如,add eax, ebx 的副作用包括:更新 EAX、更新标志位(zf, cf, of 等)——这些都打包在同一个 AssignBlock 中。
  • AssignBlock 内的赋值采用 SSA 形式,确保每个变量只被赋值一次。
# 示例:add eax, ebx 的 AssignBlock
AssignBlock({
    ExprId('EAX', 32): ExprOp('+', ExprId('EAX', 32), ExprId('EBX', 32)),
    ExprId('zf', 1): ExprCond(ExprOp('+', ExprId('EAX', 32), ExprId('EBX', 32)),
                               ExprInt(0, 1), ExprInt(1, 1)),
    ExprId('cf', 1): ...,  # 进位标志
    ExprId('of', 1): ...,  # 溢出标志
    ...
})

六、Miasm IR 元素表

Miasm IR 中的所有表达式都是 Expr 类的子类。下表列出核心元素及其人类可读形式:

Element(IR 元素) Human Form(人类可读形式) 说明
ExprInt 0x18 整数常量
ExprId EAX 变量标识符(寄存器名、临时变量等)
ExprLoc loc_17 位置引用(代码地址的符号化表示)
ExprCond A ? B : C 条件表达式:若 A 非零则取 B,否则取 C
ExprMem @16[ESI] 内存读取:从地址 ESI 读取 16 位值
ExprOp A + B 运算:对操作数应用操作码(+, -, *, <<, >>, &, | 等)
ExprSlice AH = EAX[8:16] 位切片:取 EAX 的第 8-15 位
ExprCompose AX = AH.AL 位组合:将多个切片拼接为新值
ExprAssign A = B 赋值:将 B 的值赋给 A。【更新】ExprAff 已弃用,使用 ExprAssign。【旧版 v0.1.3】ExprAff

各元素详解

ExprInt — 整数常量

from miasm.expression.expression import ExprInt

# 创建一个 32 位的整数常量 0x18
expr = ExprInt(0x18, 32)
# 人类可读形式: 0x18

ExprId — 变量标识符

from miasm.expression.expression import ExprId

# 创建一个 32 位的变量标识符 EAX
expr = ExprId('EAX', 32)
# 人类可读形式: EAX

ExprLoc — 位置引用

# ExprLoc 引用控制流图中的某个位置(地址)
# loc_db 为位置 0x401000 分配标签 loc_17
loc_db.add_location(offset=0x401000, name='loc_17')
# 人类可读形式: loc_17

ExprCond — 条件表达式

from miasm.expression.expression import ExprCond, ExprInt

# cond ? value_if_true : value_if_false
expr = ExprCond(cond_expr, ExprInt(1, 32), ExprInt(0, 32))
# 人类可读形式: cond ? 0x1 : 0x0

ExprMem — 内存读取

from miasm.expression.expression import ExprMem

# 从地址 ESI 读取 16 位值
expr = ExprMem(ExprId('ESI', 32), 16)
# 人类可读形式: @16[ESI]

ExprOp — 运算

from miasm.expression.expression import ExprOp

# EAX + EBX
expr = ExprOp('+', ExprId('EAX', 32), ExprId('EBX', 32))
# 人类可读形式: EAX + EBX

# EAX << 2
expr_shift = ExprOp('<<', ExprId('EAX', 32), ExprInt(2, 32))
# 人类可读形式: EAX << 0x2

ExprSlice — 位切片

from miasm.expression.expression import ExprSlice

# 取 EAX 的第 8 位到第 15 位(即 AH 寄存器)
expr = ExprSlice(ExprId('EAX', 32), 8, 16)
# 人类可读形式: EAX[8:16]

ExprCompose — 位组合

from miasm.expression.expression import ExprCompose

# 将 AH(高字节)和 AL(低字节)组合为 AX
expr = ExprCompose(
    (ExprId('AL', 8), 0, 8),    # AL 占第 0-7 位
    (ExprId('AH', 8), 8, 16),   # AH 占第 8-15 位
)
# 人类可读形式: AH.AL

ExprAssign — 赋值

【更新】ExprAff 已弃用,使用 ExprAssign 替代。ExprAff 仍可作为别名使用但会产生弃用警告。
【旧版 v0.1.3】使用 ExprAff

# 【更新】使用 ExprAssign 替代已弃用的 ExprAff
from miasm.expression.expression import ExprAssign
# 【旧版 v0.1.3】from miasm.expression.expression import ExprAff

# EAX = EBX
# 【更新】expr = ExprAssign(ExprId('EAX', 32), ExprId('EBX', 32))
# 【旧版 v0.1.3】expr = ExprAff(ExprId('EAX', 32), ExprId('EBX', 32))
expr = ExprAssign(ExprId('EAX', 32), ExprId('EBX', 32))
# 人类可读形式: EAX = EBX

七、Miasm IR 指令转换示例

以下展示 x86 指令如何被提升为 Miasm IR:

示例 1:mov eax, ebx

mov eax, ebx
→ ExprAssign(ExprId("EAX", 32), ExprId("EBX", 32))

【更新】ExprAff 已弃用,使用 ExprAssign
【旧版 v0.1.3】→ ExprAff(ExprId("EAX", 32), ExprId("EBX", 32))

语义:将 EBX 的值赋给 EAX。仅一条赋值,无副作用。

示例 2:push eax

push eax
→ esp = esp - 0x4; @32[esp - 0x4] = eax

语义

  1. 栈指针 ESP 减 4(栈向低地址增长)。
  2. 将 EAX 的值写入栈顶(新 ESP 指向的地址)。

IR 表示

AssignBlock({
    ExprId('ESP', 32): ExprOp('-', ExprId('ESP', 32), ExprInt(4, 32)),
    ExprMem(ExprOp('-', ExprId('ESP', 32), ExprInt(4, 32)), 32): ExprId('EAX', 32)
})

示例 3:cmp eax, ebx

cmp eax, ebx
→ zf = (EAX - EBX) ? 0 : 1; cf = ...; of = ...

语义cmp 指令执行 EAX - EBX 但不保存结果,仅更新标志位:

  • zf(零标志):若 EAX - EBX == 0(即 EAX == EBX),则 zf = 1,否则 zf = 0。
  • cf(进位标志):若发生借位(即 EAX < EBX,无符号),则 cf = 1。
  • of(溢出标志):若有符号运算溢出,则 of = 1。

IR 表示

AssignBlock({
    ExprId('zf', 1): ExprCond(
        ExprOp('-', ExprId('EAX', 32), ExprId('EBX', 32)),
        ExprInt(0, 1), ExprInt(1, 1)  # 差非零→zf=0,差为零→zf=1
    ),
    ExprId('cf', 1): ...,  # 进位标志的具体表达式
    ExprId('of', 1): ...,  # 溢出标志的具体表达式
    ExprId('af', 1): ...,  # 辅助进位标志
    ExprId('pf', 1): ...,  # 奇偶标志
    ExprId('sf', 1): ...,  # 符号标志
})

八、核心 Miasm API 体系

下表按模块组织 Miasm 的核心 API:

模块 关键 API 用途
analysis.machine Machine('x86_32' / 'x86_64') 架构入口,创建对应架构的机器实例
analysis.machine machine.lifter(loc_db) / machine.lifter_model_call(loc_db) 【更新】IR Lifter 创建。【旧版 v0.1.3】machine.ir() / machine.ira() 【已弃用】
analysis.binary Container.from_stream / from_string 二进制加载,解析文件格式(ELF/PE 等)
analysis.data_flow DeadRemoval, remove_empty_assignblks, merge_blocks 数据流优化(死代码移除、空块清理、块合并)
analysis.cst_propag propagate_cst_expr 常量传播
analysis.simplifier IRCFGSimplifierCommon IRCFG 通用简化
analysis.dse DSEPathConstraint 动态符号执行(DSE),含路径约束
analysis.sandbox Sandbox_Linux_x86_64 Linux 沙箱环境,用于安全执行/模拟
ir.symbexec SymbolicExecutionEngine 符号执行引擎,在 IRCFG 上执行
ir.translators Translator.to_language("z3") 表达式翻译(IR → Z3 / LLVM 等)
expression.expression ExprInt / ExprMem / ExprId / ExprCond / ExprAssign 表达式构造,构建 IR 元素。【更新】ExprAff 已弃用,使用 ExprAssign。【旧版 v0.1.3】ExprAff
expression.simplifications expr_simp 表达式简化,语义等价化简
core.parse_asm parse_txt 汇编文本解析,从文本生成 AsmCFG

各模块使用要点

analysis.machine — 架构入口

from miasm.analysis.machine import Machine

# 创建 x86_32 架构实例
machine = Machine('x86_32')

# 【更新】获取 IR Lifter(替代旧版 ira)
# 【旧版 v0.1.3】ir_arch = machine.ira(loc_db)  # 【已弃用】
# 【更新】machine.ira() → machine.lifter_model_call(),变量名 ir_arch → lifter
lifter = machine.lifter_model_call(loc_db)

# 获取反汇编引擎
mdis = machine.dis_engine(bin_stream, loc_db=loc_db)

# 【更新】获取 jitter(模拟执行器),jitter 参数命名变化
# 【旧版 v0.1.3】jitter = machine.jitter(loc_db, 'python')  # 'python' 或 'gcc' 后端
# 【更新】jitter 的第二个参数改为关键字参数 jit_type
jitter = machine.jitter(loc_db, jit_type='python')  # 'python' 或 'gcc' 后端

API 变更说明

  • 【更新】machine.ir()machine.lifter() 【旧版 v0.1.3 已弃用】
  • 【更新】machine.ira()machine.lifter_model_call() 【旧版 v0.1.3 已弃用】
  • 【更新】machine.jitter(loc_db, 'python')machine.jitter(loc_db, jit_type='python')
  • 【更新】变量命名约定:irlifterir_archlifter

analysis.binary — 二进制加载

from miasm.analysis.binary import Container
from miasm.core.locationdb import LocationDB

loc_db = LocationDB()

# 从文件流加载
with open('target.elf', 'rb') as f:
    cont = Container.from_stream(f, loc_db)

# 从字节串加载
cont = Container.from_string(data, loc_db)

# 访问入口点、节区等
print(cont.arch)           # 架构名,如 'x86_32'
print(cont.entry_point)    # 入口点地址
print(cont.bin_stream)     # 二进制字节流

analysis.data_flow — 数据流优化

from miasm.analysis.data_flow import (
    DeadRemoval,              # 死代码移除 pass
    remove_empty_assignblks,  # 移除空赋值块
    merge_blocks,             # 合并相邻基本块
)

# 死代码移除:基于活跃度分析,删除非活跃的赋值
# 【更新】DeadRemoval(ir_arch)(ircfg) → DeadRemoval(lifter)(ircfg)
# 【旧版 v0.1.3】DeadRemoval(ir_arch)(ircfg)
DeadRemoval(lifter)(ircfg)

# 移除空 AssignBlock(无任何赋值的块)
remove_empty_assignblks(ircfg)

# 合并可合并的相邻 IRBlock
merge_blocks(ircfg)

analysis.cst_propag — 常量传播

from miasm.analysis.cst_propag import propagate_cst_expr

# 对指定地址执行常量传播
# 【更新】ir_arch → lifter(变量名变更)
# 【旧版 v0.1.3】# ir_arch: IR 分析器
# ircfg: IR 控制流图
# addr: 起始地址
# init_infos: 初始符号信息(已知常量值)
# 【旧版 v0.1.3】propagate_cst_expr(ir_arch, ircfg, addr, init_infos)
propagate_cst_expr(lifter, ircfg, addr, init_infos)

analysis.simplifier — IRCFG 通用简化

from miasm.analysis.simplifier import IRCFGSimplifierCommon

# 创建简化器实例
# 【更新】IRCFGSimplifierCommon(ir_arch) → IRCFGSimplifierCommon(lifter)
# 【旧版 v0.1.3】simplifier = IRCFGSimplifierCommon(ir_arch)
simplifier = IRCFGSimplifierCommon(lifter)

# 对整个 IRCFG 执行简化
simplifier.simplify(ircfg)

analysis.dse — 动态符号执行

from miasm.analysis.dse import DSEPathConstraint

# 创建 DSE 实例,带路径约束追踪
# 【更新】DSEPathConstraint(machine, ir_arch, ...) → DSEPathConstraint(machine, loc_db, ...)
# 【旧版 v0.1.3】dse = DSEPathConstraint(machine, ir_arch, ...)
# 【更新】v0.1.5+ 中 DSEPathConstraint 需要 loc_db 参数,ir_arch 参数已移除
# 【更新】dse.ir_arch 属性 → dse.lifter;dse.cur_solver 属性 → dse.z3_cur
dse = DSEPathConstraint(machine, loc_db)
dse.run(...)  # 执行动态符号执行

analysis.sandbox — Linux 沙箱

from miasm.analysis.sandbox import Sandbox_Linux_x86_64

# 创建 Linux x86_64 沙箱
sandbox = Sandbox_Linux_x86_64(loc_db, 'target.bin')
# 沙箱提供安全的执行环境,支持系统调用模拟

ir.symbexec — 符号执行引擎

from miasm.ir.symbexec import SymbolicExecutionEngine

# 创建符号执行引擎
# 【更新】SymbolicExecutionEngine(ir, symbols) → SymbolicExecutionEngine(lifter)
# 【旧版 v0.1.3】sb = SymbolicExecutionEngine(ir, symbols)
# 【更新】v0.1.5+ 中 symbols 参数不再在构造函数中传递,通过 sb.symbols 设置
sb = SymbolicExecutionEngine(lifter)

# 执行一个基本块
pc = sb.run_block_at(ircfg, addr)

# 访问符号状态
print(sb.symbols)  # 当前符号状态(寄存器/内存的符号值)

ir.translators — 表达式翻译

from miasm.ir.translators import Translator

# 翻译为 Z3 表达式(用于约束求解)
z3_translator = Translator.to_language('z3')
z3_expr = z3_translator.from_expr(miasm_expr)

# 翻译为 LLVM IR(用于复用 LLVM 优化 pass)
llvm_translator = Translator.to_language('llvm')
llvm_expr = llvm_translator.from_expr(miasm_expr)

expression.simplifications — 表达式简化

from miasm.expression.simplifications import expr_simp

# 对表达式进行简化
simplified = expr_simp(complex_expr)
# 例如: (x + 0) → x, (x * 1) → x, (x - x) → 0

core.parse_asm — 汇编文本解析

from miasm.core.parse_asm import parse_txt
from miasm.analysis.machine import Machine

machine = Machine('x86_32')
mn_x86 = machine.mn()  # 获取汇编器/反汇编器

assembly = """
main:
    MOV EAX, 0x1
    MOV EBX, 0x2
    ADD EAX, EBX
    RET
"""

# 解析汇编文本,生成 AsmCFG
asmcfg, loc_db = parse_txt(mn_x86, 'x86_32', loc_db, assembly)

九、参考资料

  • Desclaux, F. and Mougey, É. Miasm: Reverse Engineering Framework. Black Hat USA 2018.
  • Miasm 官方文档与 GitHub 仓库:https://github.com/cea-sec/miasm
  • Salwan, Bardin, Potet. Symbolic Deobfuscation: From Virtualized Code Back to the Original. DIMVA 2018.(基于 Miasm 的虚拟化代码反混淆)
posted @ 2026-08-05 15:55  DirWangK  阅读(12)  评论(0)    收藏  举报