那CTF,那VMre,那些事(二)
字数 1175 2025-08-07 08:22:23
VM逆向分析与Angr符号执行技术详解
0x00 前言
本文深入探讨虚拟机逆向分析(VMRE)技术及Angr符号执行在CTF逆向题目中的应用,包含两个核心部分:Angr符号执行工具的详细使用方法,以及复杂VM保护程序的逆向分析技巧。
0x01 Angr符号执行技术详解
符号执行基础概念
符号执行是一种程序分析技术,其核心思想是:
- 使用符号变量代替具体数值执行程序
- 能够探索程序所有可能的执行路径
- 每条路径结果表示为约束条件
- 使用约束求解引擎求解有效输入
Angr框架概述
Angr是一个基于Python的二进制程序分析框架,主要特点:
- 支持符号执行、控制流分析、数据依赖分析等
- 内置约束求解引擎
- 提供程序状态管理功能
- 特别适合CTF逆向题目求解
环境搭建方法
两种推荐安装方式:
- Docker方式:
docker pull angr/angr
docker run -it -v /本地目录:/容器目录 angr/angr
- Pip直接安装:
pip install angr
Angr核心使用技术
1. 创建Angr工程
p = angr.Project('program') # 加载目标程序
2. 处理命令行参数
import claripy
argv = [p.filename] # 程序名作为第一个参数
arg = claripy.BVS('arg1', 8) # 创建8位符号变量
argv.append(arg) # 添加为命令行参数
3. 程序状态管理
state = p.factory.entry_state(args=argv) # 创建初始状态
Angr中的几种关键状态:
active: 正在执行的分支(使用step()产生)deadended: 已结束的分支(使用run()产生)found: 探索到的目标状态(使用explore()产生)
4. 符号执行控制
simgr = p.factory.simgr(state) # 创建模拟器
simgr.explore(find=目标地址, avoid=避免地址) # 路径探索
5. 标准输入处理
for _ in range(5):
f = state.posix.files[0].read_from(1) # 每次读取1字节
state.se.add(f < 100) # 添加约束条件
6. 结果提取方法
# 获取程序输出
print(simgr.found[0].posix.dumps(1))
# 获取命令行参数解
print(simgr.found[0].solver.eval(arg, cast_to=str))
# 获取标准输入解
inp = simgr.found[0].posix.files[0].all_bytes()
print(simgr.found[0].solver.eval(inp, cast_to=str))
实战案例1:[Whale CTF] defcamp_r100
题目分析:
- 简单密码检查程序
- 反编译后可见check函数逻辑
Angr解法:
import angr
p = angr.Project('./r100', auto_load_libs=False)
state = p.factory.entry_state()
simgr = p.factory.simgr(state)
simgr.explore(find=0x400844, avoid=0x400855)
flag = simgr.found[0].posix.dumps(0)
print(flag) # 输出: Code_Talkers
实战案例2:2020网鼎杯青龙组re_signal
Angr解法:
import angr
p = angr.Project('./signal.exe')
state = p.factory.entry_state()
simgr = p.factory.simgr(state)
simgr.explore(find=0x004017A5, avoid=0x004016E6)
flag = simgr.found[0].posix.dumps(0)[:15]
print(flag)
0x02 VM逆向分析技术
[UNCTF2019]easyvm题目分析
1. 初始分析
- 32位字符串匹配程序
- 主函数关键逻辑:
if ( (**v3)(v3, &unk_602080, &unk_6020A0, s) ) { printf("The flag is UNCTF{%s}", s); }
2. VM结构解析
虚拟机指令处理函数sub_400806采用switch-case结构:
switch ( **(_BYTE **)(offset + 8) ) {
case 0xA0: ... break;
case 0xA1: ... break;
// ...其他case...
case 0xAF: return 1LL;
}
3. 指令处理流程
通过分析得出指令执行顺序:
0xA9 → 0xA3 → 0xA5 → 0xA6 → 0xA4 → 0xAB → 0xA7 → 0xAE → 0xA2 → 0xAD → 0xAF
4. 关键处理函数分析
-
0xA9: 取输入字符
*(_BYTE *)(offset + 16) = input[*(offset + 18)] -
0xA3: 字符值减去索引
*(_BYTE *)(offset + 16) -= *(_BYTE *)(offset + 18) -
0xA5: 异或操作
*(_BYTE *)(offset + 17) ^= *(_BYTE *)(offset + 16) -
0xA6: 设置固定值
*(_BYTE *)(offset + 16) = 0xCD -
0xAB: 比较操作
if (val1 == val2) status = 0; else if (val1 > val2) status = 1; else status = -1;
5. 逆向算法还原
虚拟机逻辑总结:
- 取字符input[i]
- 减去i值
- 与0xCD异或
- 检查是否等于目标值
- 循环处理所有字符
6. 解密脚本
checknum = ['F4','0A','F7','64','99','78','9E','7D','EA','7B',
'9E','7B','9F','7E','EB','71','E8','00','E8','07',
'98','19','F4','25','F3','21','A4','2F','F4','2F','A6','7C']
flag = ""
for i in range(31, -1, -1):
temp = int(checknum[i], 16)
temp ^= 0xCD
if i == 0:
count = 0
else:
count = int(checknum[i-1], 16)
temp ^= count
temp += i
flag += chr(temp)
print(flag[::-1])
0x03 总结与进阶
Angr使用要点
- 合理设置find和avoid地址减少路径爆炸
- 注意约束条件的添加方式
- 灵活处理不同输入方式(命令行/标准输入)
VM逆向技巧
- 识别虚拟机指令分发结构(通常是switch-case)
- 分析指令处理函数的功能
- 重建指令执行流
- 还原虚拟指令对应的算法
进阶方向
- 带栈结构的虚拟机分析
- 多线程虚拟机保护
- 混合型保护(VM+混淆)
0x04 参考资源
- Angr官方文档
- 《符号执行技术原理与应用》
- 虚拟机保护技术分析论文
- CTF逆向题目集锦