首页
社区
课程
招聘
[原创]对VM逆向的分析(CTF)(比较经典的一个虚拟机逆向题目)
发表于: 2021-5-18 21:02 19907

[原创]对VM逆向的分析(CTF)(比较经典的一个虚拟机逆向题目)

2021-5-18 21:02
19907

简单的vm-re框架
图片描述

虚拟机就是要去模仿一个机器,让机器去执行一个文件(类似用win10去执行一个文件)

首先它需要一些在CPU中的寄存器和内存中的堆栈,这样去模拟一个CPU不断的去读取指令

主要是以循环的形式进行读取

1.在全局变量中分配如下内容:
图片描述
这里就构成了CPU+内存

2.模拟一个CPU读取指令的形式(dispatcher)去写这样一个主程序

主程序:其实就是一个循环,这个循环不断的去读取指令(伪机器码opcode)这个会存在于内存中或文件中,然后执行指令opcode所对应的一些函数(其实这些函数可以理解为伪汇编的执行过程,比如就像一些add,sub,pop,push,jmp等操作),这样下来就可以与真实的程序执行相差无几

拿到程序之后先查壳,发现无壳之后运行一下查看,只有一个plz input:

我们将其直接拖入ida中进行查看

左侧函数窗口CTRL+F查找main函数

跳转到main函数之后发现有个花指令

图片描述

将地址0x401594处的0xB8换成0x90(nop的硬编码)即可

然后P一下main函数按Tab进行反汇编

如下:

dispatcher就是去实现模拟CPU读取指令

opcode_team就是存在于内存中(也可存在于文件中)的opcode(伪机器码)

点开dispatcher,如下(这里就是一些模拟汇编的伪汇编函数),依次进行分析:

分析之后的:

图片描述
图片描述

分析过程:(不一一举例,举几个例子)
图片描述

什么也没做,就只是++了eip,说明该函数模拟的是NOP

图片描述
这里点击过去发现了连续的一个数组和一些字符串数据,说明模拟的是内存和CPU中的寄存器
图片描述
这里模拟的就是mov reg, data

又比如sub_691070

图片描述

这个模拟的就是push data,同时我们也在内存中找到了模拟栈

以此类推,将全部伪汇编的函数分析出来,然后写出解析脚本:

分析和注释:

最后理解程序的逻辑之后写出解题脚本:

得到flag值为:

图片描述

flag{Such_A_EZVM}

再遇见vm-re的题目,先查找到opcode(伪机器码),然后找到dispatcher(就是模拟CPU读取指令的分发器),然后边分析那些伪汇编函数(就是模仿汇编指令的函数)边查找模拟的CPU的栈,寄存器,全局变量(多是字符串)等

参考资料:
https://www.bilibili.com/video/BV1Nv411k7HJ
https://blog.csdn.net/weixin_43876357/article/details/108488762

 
 
 
 
 
 
 
 
 
 
 
 
 
int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int i; // [esp+10h] [ebp-8h]
 
  sub_6914F0();
  while ( 1 )
  {
LABEL_2:
    for ( i = 0; ; i += 2 )
    {
      if ( i >= 0x58 )
        goto LABEL_2;
      if ( dword_6D48F0[4 * i] == opcode_team[ei_p] )// opcode_team存在于内存中的伪机器码(指令)
        break;
    }
    dispatcher[i]();    // 模拟一个CPU不断的去执行指令(其实就是根据opcode去执行一些模拟汇编的一些函数)
  }
}
int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int i; // [esp+10h] [ebp-8h]
 
  sub_6914F0();
  while ( 1 )
  {
LABEL_2:
    for ( i = 0; ; i += 2 )
    {
      if ( i >= 0x58 )
        goto LABEL_2;
      if ( dword_6D48F0[4 * i] == opcode_team[ei_p] )// opcode_team存在于内存中的伪机器码(指令)
        break;
    }
    dispatcher[i]();    // 模拟一个CPU不断的去执行指令(其实就是根据opcode去执行一些模拟汇编的一些函数)
  }
}
 
 
 
 
 
 
 
 
 
 
 
opcode_team = [0x01, 0x03, 0x03, 0x05, 0x00, 0x00, 0x11, 0x00, 0x00, 0x01, 0x01, 0x11, 0x0C, 0x00, 0x01, 0x0D, 0x0A, 0x00, 0x01, 0x03, 0x01, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0x02, 0x00, 0x01, 0x00, 0x11, 0x0C, 0x00, 0x02, 0x0D, 0x2B, 0x00, 0x14, 0x00, 0x02, 0x01, 0x01, 0x61, 0x0C, 0x00, 0x01, 0x10, 0x1A, 0x00, 0x01, 0x01, 0x7A, 0x0C, 0x00, 0x01, 0x0F, 0x1A, 0x00, 0x01, 0x01, 0x47, 0x0A, 0x00, 0x01, 0x01, 0x01, 0x01, 0x06, 0x00, 0x01, 0x0B, 0x24, 0x00, 0x01, 0x01, 0x41, 0x0C, 0x00, 0x01, 0x10, 0x24, 0x00, 0x01, 0x01, 0x5A, 0x0C, 0x00, 0x01, 0x0F, 0x24, 0x00, 0x01, 0x01, 0x4B, 0x0A, 0x00, 0x01, 0x01, 0x01, 0x01, 0x07, 0x00, 0x01, 0x01, 0x01, 0x10, 0x09, 0x00, 0x01, 0x03, 0x01, 0x00, 0x03, 0x00, 0x00, 0x01, 0x01, 0x01, 0x06, 0x02, 0x01, 0x0B, 0x0B, 0x00, 0x02, 0x07, 0x00, 0x02, 0x0D, 0x00, 0x02, 0x00, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0x0C, 0x00, 0x02, 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x0D, 0x00, 0x02, 0x05, 0x00, 0x02, 0x0F, 0x00, 0x02, 0x00, 0x00, 0x02, 0x09, 0x00, 0x02, 0x05, 0x00, 0x02, 0x0F, 0x00, 0x02, 0x03, 0x00, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x05, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x01, 0x00, 0x02, 0x07, 0x00, 0x02, 0x07, 0x00, 0x02, 0x0B, 0x00, 0x02, 0x02, 0x00, 0x02, 0x01, 0x00, 0x02, 0x02, 0x00, 0x02, 0x07, 0x00, 0x02, 0x02, 0x00, 0x02, 0x0C, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x01, 0x13, 0x01, 0x02, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x01, 0x0E, 0x5B, 0x00, 0x01, 0x01, 0x22, 0x0C, 0x02, 0x01, 0x0D, 0x59, 0x00, 0x01, 0x01, 0x01, 0x06, 0x02, 0x01, 0x0B, 0x4E, 0x00, 0x01, 0x03, 0x00, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0x03, 0x01, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00]
opcode_key = {
    0: 'nop',
    1: 'mov reg data',
    2: 'push data',
    3: 'push_reg',
    4: 'pop_reg',
    5'printf',
    6'add_reg_reg1',
    7'sub_reg_reg1',
    8'mul',
    9'div',
    10: 'xor',
    11: 'jmp',
    12: 'cmp',
    13: 'je',
    14: 'jne',
    15: 'jg',
    16: 'jl',
    17: 'scanf_strlen',
    18: 'mem_init',
    19: 'stack_to_reg',
    20: 'load_input',
    0xff: 'exit'}
count = 0
code_index = 1
for x in opcode_team:   # 取每个opcode
    if count % 3 == 0# 每3个opcode是一条指令,每个指令的第一个是操作码
        print(str(code_index)+':', end='')
        print(opcode_key[x], end=' ')
        code_index += 1
    elif count % 3 == 1:
        print(str(x)+',', end=' ')
    else:
        print(str(x))
    count += 1
opcode_team = [0x01, 0x03, 0x03, 0x05, 0x00, 0x00, 0x11, 0x00, 0x00, 0x01, 0x01, 0x11, 0x0C, 0x00, 0x01, 0x0D, 0x0A, 0x00, 0x01, 0x03, 0x01, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0x02, 0x00, 0x01, 0x00, 0x11, 0x0C, 0x00, 0x02, 0x0D, 0x2B, 0x00, 0x14, 0x00, 0x02, 0x01, 0x01, 0x61, 0x0C, 0x00, 0x01, 0x10, 0x1A, 0x00, 0x01, 0x01, 0x7A, 0x0C, 0x00, 0x01, 0x0F, 0x1A, 0x00, 0x01, 0x01, 0x47, 0x0A, 0x00, 0x01, 0x01, 0x01, 0x01, 0x06, 0x00, 0x01, 0x0B, 0x24, 0x00, 0x01, 0x01, 0x41, 0x0C, 0x00, 0x01, 0x10, 0x24, 0x00, 0x01, 0x01, 0x5A, 0x0C, 0x00, 0x01, 0x0F, 0x24, 0x00, 0x01, 0x01, 0x4B, 0x0A, 0x00, 0x01, 0x01, 0x01, 0x01, 0x07, 0x00, 0x01, 0x01, 0x01, 0x10, 0x09, 0x00, 0x01, 0x03, 0x01, 0x00, 0x03, 0x00, 0x00, 0x01, 0x01, 0x01, 0x06, 0x02, 0x01, 0x0B, 0x0B, 0x00, 0x02, 0x07, 0x00, 0x02, 0x0D, 0x00, 0x02, 0x00, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0x0C, 0x00, 0x02, 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x0D, 0x00, 0x02, 0x05, 0x00, 0x02, 0x0F, 0x00, 0x02, 0x00, 0x00, 0x02, 0x09, 0x00, 0x02, 0x05, 0x00, 0x02, 0x0F, 0x00, 0x02, 0x03, 0x00, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x05, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x01, 0x00, 0x02, 0x07, 0x00, 0x02, 0x07, 0x00, 0x02, 0x0B, 0x00, 0x02, 0x02, 0x00, 0x02, 0x01, 0x00, 0x02, 0x02, 0x00, 0x02, 0x07, 0x00, 0x02, 0x02, 0x00, 0x02, 0x0C, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x01, 0x13, 0x01, 0x02, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x01, 0x0E, 0x5B, 0x00, 0x01, 0x01, 0x22, 0x0C, 0x02, 0x01, 0x0D, 0x59, 0x00, 0x01, 0x01, 0x01, 0x06, 0x02, 0x01, 0x0B, 0x4E, 0x00, 0x01, 0x03, 0x00, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0x03, 0x01, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00]
opcode_key = {
    0: 'nop',
    1: 'mov reg data',
    2: 'push data',
    3: 'push_reg',
    4: 'pop_reg',
    5'printf',
    6'add_reg_reg1',
    7'sub_reg_reg1',
    8'mul',
    9'div',
    10: 'xor',
    11: 'jmp',
    12: 'cmp',
    13: 'je',
    14: 'jne',
    15: 'jg',
    16: 'jl',
    17: 'scanf_strlen',
    18: 'mem_init',
    19: 'stack_to_reg',
    20: 'load_input',
    0xff: 'exit'}
count = 0
code_index = 1
for x in opcode_team:   # 取每个opcode
    if count % 3 == 0# 每3个opcode是一条指令,每个指令的第一个是操作码
        print(str(code_index)+':', end='')
        print(opcode_key[x], end=' ')
        code_index += 1
    elif count % 3 == 1:
        print(str(x)+',', end=' ')
    else:
        print(str(x))
    count += 1
//打印出来的结果如下
1:mov reg data 3, 3
2:printf 0, 0
3:scanf_strlen 0, 0
4:mov reg data 1, 17
5:cmp 0, 1
6:je 10, 0
7:mov reg data 3, 1
8:printf 0, 0
9:exit 0, 0
10:mov reg data 2, 0
11:mov reg data 0, 17
12:cmp 0, 2
13:je 43, 0
14:load_input 0, 2
15:mov reg data 1, 97
16:cmp 0, 1
17:jl 26, 0
18:mov reg data 1, 122
19:cmp 0, 1
20:jg 26, 0
21:mov reg data 1, 71
22:xor 0, 1
23:mov reg data 1, 1
24:add_reg_reg1 0, 1
25:jmp 36, 0
26:mov reg data 1, 65
27:cmp 0, 1
28:jl 36, 0
29:mov reg data 1, 90
30:cmp 0, 1
31:jg 36, 0
32:mov reg data 1, 75
33:xor 0, 1
34:mov reg data 1, 1
35:sub_reg_reg1 0, 1
36:mov reg data 1, 16
37:div 0, 1
38:push_reg 1, 0
39:push_reg 0, 0
40:mov reg data 1, 1
41:add_reg_reg1 2, 1
42:jmp 11, 0
43:push data 7, 0
44:push data 13, 0
45:push data 0, 0
46:push data 5, 0
47:push data 1, 0
48:push data 12, 0
49:push data 1, 0
50:push data 0, 0
51:push data 0, 0
52:push data 13, 0
53:push data 5, 0
54:push data 15, 0
55:push data 0, 0
56:push data 9, 0
57:push data 5, 0
58:push data 15, 0
59:push data 3, 0
60:push data 0, 0
61:push data 2, 0
62:push data 5, 0
63:push data 3, 0
64:push data 3, 0
65:push data 1, 0
66:push data 7, 0
67:push data 7, 0
68:push data 11, 0
69:push data 2, 0
70:push data 1, 0
71:push data 2, 0
72:push data 7, 0
73:push data 2, 0
74:push data 12, 0
75:push data 2, 0
76:push data 2, 0
77:mov reg data 2, 1
78:stack_to_reg 1, 2
79:pop_reg 0, 0
80:cmp 0, 1
81:jne 91, 0
82:mov reg data 1, 34
83:cmp 2, 1
84:je 89, 0
85:mov reg data 1, 1
86:add_reg_reg1 2, 1
87:jmp 78, 0
88:mov reg data 3, 0
89:printf 0, 0
90:exit 0, 0
91:mov reg data 3, 1
92:printf 0, 0
93:exit 0, 0
94:nop
//打印出来的结果如下
1:mov reg data 3, 3
2:printf 0, 0
3:scanf_strlen 0, 0
4:mov reg data 1, 17
5:cmp 0, 1
6:je 10, 0
7:mov reg data 3, 1
8:printf 0, 0
9:exit 0, 0
10:mov reg data 2, 0
11:mov reg data 0, 17
12:cmp 0, 2
13:je 43, 0
14:load_input 0, 2
15:mov reg data 1, 97
16:cmp 0, 1
17:jl 26, 0
18:mov reg data 1, 122
19:cmp 0, 1
20:jg 26, 0
21:mov reg data 1, 71
22:xor 0, 1
23:mov reg data 1, 1
24:add_reg_reg1 0, 1
25:jmp 36, 0
26:mov reg data 1, 65
27:cmp 0, 1
28:jl 36, 0
29:mov reg data 1, 90
30:cmp 0, 1
31:jg 36, 0
32:mov reg data 1, 75
33:xor 0, 1
34:mov reg data 1, 1

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2021-7-29 11:52 被SYJ-Re编辑 ,原因:
上传的附件:
收藏
免费 8
支持
分享
最新回复 (5)
雪    币: 21
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2

 为何将0xB8换成0x90?


2021-7-3 22:48
1
雪    币: 3668
活跃值: (9375)
能力值: ( LV9,RANK:319 )
在线值:
发帖
回帖
粉丝
3

简单patch,这样就可以改变从你光标开始的字节,你还可以下载插件keypatch,放到plugins目录即可

最后于 2021-7-29 11:56 被SYJ-Re编辑 ,原因:
2021-7-29 11:55
0
雪    币: 4793
活跃值: (4484)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
呦西get到了vm_nop
2021-7-29 13:44
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
终于更新文章了,呜呜
2021-10-22 07:44
0
雪    币: 669
活跃值: (2178)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
hello1  为何将0xB8换成0x90?
去除花指令
2024-5-27 11:18
0
游客
登录 | 注册 方可回帖
返回
//