首页
社区
课程
招聘
[原创]调教angr拆解一道VM逆向
发表于: 2025-3-14 03:07 7122

[原创]调教angr拆解一道VM逆向

2025-3-14 03:07
7122

案例来自
RITSEC 2022 DataFun

这篇随笔记录一次调教 angr 拆解某个比较弱的 VM 的经历
(字节码的控制流不因输入改变、检验函数不由 VM 执行)

函数 sub_1588 中有一个未被修复的跳转表
修复之可以看出这是一个虚拟机
图片描述

综合位于 main 函数中初始化的函数
以及 sub_1588 中对 vm 进行操作的小函数
可以复原出 vm 的数据结构

显然这是一个栈

将数据结构送到 F5 可以轻松理解 main 函数的代码

通关字串为R3V3RS1NG_1S_E4SY

load 函数比较特殊
它使用了 PTRACE_TRACEME 反调试
在有调试器的情况下会将opcode XOR 0x36 而不是 0x37

这题的 opcode 并不复杂并且检验函数并不由 VM 执行

opcode 长度为 139
共有 38 次读取单字节的操作即输入应长 38
尝试 angr 一把梭
不过很快就失败了

对于这道题来说一把梭是行不通的
而作为一个懒人,自然是懒得逆向那一大坨字节码
(后来才知道官方的 wp 真就是纯手工逆的,洋洋洒洒 20 多个方程,不过出题人没说怎么解的估计是用了 z3)
于是一些调整是必须的

这题对抗符号执行的地方主要有两处

图片描述

图片描述

这个多余的分支每取一次指令就会产生一次,因此每一个 state 经过一遍循环就会导出两个后继,时间复杂度从无分支时的 O(n) 变成了 O(2^n)
这显然是跑不完的

因此本懒人的思路如下

钩子(强行让 ptrace 返回 0):

图片描述

图片描述

图片描述

分支筛选的代码(相当于手动执行基本块)

可以看到在 VM 的执行过程中 angr 不断地踏入危险分支
图片描述

最后得到一组合法输入

本地验证
图片描述

25年3月13日于清水湾

00000000 vm_t            struc ; (sizeof=0x18, mappedto_8)
00000000 sz              dd ?
00000004 field_4         dd ?
00000008 buf             dq ?
00000010 top             dd ?
00000014 field_14        dd ?
00000018 vm_t            ends
00000000 vm_t            struc ; (sizeof=0x18, mappedto_8)
00000000 sz              dd ?
00000004 field_4         dd ?
00000008 buf             dq ?
00000010 top             dd ?
00000014 field_14        dd ?
00000018 vm_t            ends
int main() {
  setbuf(stdout, 0LL);
  puts("Welcome to my program. Give me some data, and let's see if you get the flag!");
  read(0, buf, 512uLL);
  vm = load(code, 139);
  v5 = 0;
  for ( i = 0; i <= 138; ++i )
  {
    run(code[i], buf[v5], vm);
    if ( get_byte_from_input(code[i]) )
      ++v5;
  }
  s = clone_stack(vm);
  v3 = strlen(s);
  if ( v3 == strlen(s) && !strcmp(s, "R3V3RS1NG_1S_E4SY") )
    printf("You got it. The flag is RS{%s}\n", "PLACEHOLDER_FLAGS_ROCK_!!!!!!");
  else
    puts("Not quite");
  return 0LL;
}
int main() {
  setbuf(stdout, 0LL);
  puts("Welcome to my program. Give me some data, and let's see if you get the flag!");
  read(0, buf, 512uLL);
  vm = load(code, 139);
  v5 = 0;
  for ( i = 0; i <= 138; ++i )
  {
    run(code[i], buf[v5], vm);
    if ( get_byte_from_input(code[i]) )
      ++v5;

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2025-3-22 23:52 被狗敦子编辑 ,原因: 添加图片
上传的附件:
收藏
免费
支持
分享
最新回复 (4)
雪    币: 233
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
脚本可以分享下吗?学习一下。
2025-3-18 09:45
0
雪    币: 1832
活跃值: (1080)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
3
Eagle丶 脚本可以分享下吗?学习一下。
放附件了
2025-3-18 19:31
0
雪    币: 85
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
问下这个vmp版本啥哪个版本的
2025-3-18 22:39
0
雪    币: 233
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
狗敦子 放附件了
用你的代码还是报错。true_state=next(x for x in simgr.active if x.addr==base_addr+true_addr)
StopIteration
2025-3-19 10:59
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册