VMProtect 3.09 虚拟机架构浅析

原文网址:https://www.52pojie.cn/thread-586130-1-1.html?t=1489213424424&t=1489213424872

 

0x00 写在前面
最近两周一直在研究VMProtect相关的东西,发现VMProtect 2.x 被研究的比较充分,FKVMP、Zeus、VMP分析插件1.4等神器也出来了很久。但VMProtect 3 没有在论坛内找到相关的资料,这里写一点简单分析作为抛砖引玉之用。新人第一贴,求论坛的大大们多多鼓励。

根据官方公告,VMProtect 3使用了新的虚拟机架构,本文通过分析自己加虚拟化的简单程序,介绍一下VMProtect 3虚拟机的基本架构,并与VMProtect 2.x进行简单对比。
这里强调一下,本贴只介绍虚拟机架构方面的内容,关于脱壳、IAT恢复、过anti等等请参考其他大佬的文章。

0x01 初步分析
分析的样本是用MASM32编写的小程序,源代码如下:

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

include \masm32\include\masm32rt.inc

 

.data?

value dd ?

int_array dd 100 dup(?)

 

.data

;item dd 0

text db "Hello Text",0

caption db "Hello Caption",0

 

.code

 

start:

 

call main

push NULL

call ExitProcess

 

 

main proc

 

mov eax, 0deadbeefh

mov eax, 0deadbeefh

mov eax, 0deadbeefh

mov eax, 0deadbeefh

mov eax, 0deadbeefh

mov eax, 0deadbeefh

mov eax, 0deadbeefh

mov eax, 0deadbeefh

mov eax, 0deadbeefh

mov eax, 0deadbeefh

ret

main endp

 

end start



编译完成后大小1.50 KB (1,536 字节)

使用VMProtect 1.81 demo版本对main函数(地址0x40100C)加虚拟化,最快速度,关掉一切其他保护。
这里之所以使用 1.81 demo这么低的版本,主要原因是该版本VMP中的虚拟机代码没有混淆,生成的bytecode也比较规则,没有2.x版本的冗余分支,同时又能被VMP分析插件1.4完美支持,因此非常适合拿来学习VMP虚拟机结构。
加密后文件大小5.00 KB (5,120 字节)

再使用VMProtect 2.12.3版本加虚拟化,选项相同 ,关掉所有保护。
加密后文件大小15.0 KB (15,360 字节)

再使用论坛的VMProtect 3.09版本加虚拟化,选项相同,关掉所有保护。
加密后文件大小516 KB (528,896 字节)

可见VMProtect 3.x版本加密后的代码量有非常大的变化,注意这里关掉了所有保护选项的情况,因此这些多出来的代码不是壳代码,也不是调试器检测的代码,就是实实在在虚拟化后的指令。
这么为什么会多出这么多,这里简单分析一下。

需要用到OD的追踪功能(很好用的功能,但大家经常忽略掉他)
OD分别打开两个文件,Ctrl+F11 跟踪步入,64位电脑先将ntdll.dll和kernel32.dll标记为系统DLL,避免记录到trace中。
跟踪完成后分析两个程序产生trace的区别。

console.vmp_1.81_demo.exe  生成527条trace
用OD统计下指令数(注意在选项中关掉统计时将相邻指令合并的选项),前几条是这样

[Asm] 纯文本查看 复制代码

?

1

2

3

4

5

Count Address Command Comment

48. 00403025 mov al,byte ptr ds:[esi]

48. 00403027 movzx eax,al

48. 0040302A sub esi,-0x1

48. 0040302D jmp dword ptr ds:[eax*4+0x403103]



console.vmp_2.12.3.exe 生成14277条trace
用OD统计下指令数,前几条是这样

[Asm] 纯文本查看 复制代码

?

1

2

3

4

5

6

7

Count Address Command Comment

149. 004046E5 mov ecx,dword ptr ds:[eax*4+0x405254]

149. 004046EC pushad

149. 004046ED lea esp,dword ptr ss:[esp+0x2C]

149. 004046F1 jle console_.00404622

149. 00404833 neg cx

149. 00404836 sub cx,0xFBDF



console.vmp_3.09.exe 生成2238条Trace

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

Count Address Command Comment

18. 00421B90 jmp edi

18. 0042D77B lea eax,dword ptr ss:[esp+0x60]

18. 0042D77F cmp sp,0x6934

18. 0042D784 cmp ebp,eax

18. 0042D786 ja console_.00421B90

2. 00408854 jmp edi

2. 004099CF inc eax

2. 004099D0 ror eax,0x2

2. 004099D3 jmp console_.0041442D



如果了解VMP架构的小伙伴应该发现问题了。我们具体比较一下
先说1.81,由于是demo版本,所以没有混淆,所以trace数相当的少。
2.12版本加了大量垃圾指令,同时也会额外生成几条bytecode,所以Trace数量猛增。
关键是3.09版本,其文件大小是2.12版本的几十倍,但trace数却只是2.12版本的几分之一。
再看指令数统计,答案就很明白了。

我们知道VMP 1.x 2.x的虚拟机结构都是典型的取指令、解码opcode、根据opcode进行dispatch的过程,整个虚拟机是个大循环结构,handler表最多0xFF大小,所有x86指令都由这0xFF(由于有重复,实际会少)个handler模拟完成。
执行次数最多的jmp dword ptr ds:[eax*4+0x403103]和mov ecx,dword ptr ds:[eax*4+0x405254]都是典型的dispatch指令。
由于循环的存在,很少的指令也会生成较多的Trace

反观3.x 可以看到只有5条指令重复执行了18次,剩下的指令最多只执行过2次。可以说3.x循环的部分是相关少的。那么这些取指令和dispatch的过程都是独立的。最为可怕的是handler本身也不是复用的。也就是说两条相同的指令会生成不同的handler代码,尽管他们实现的功能是完全相同的。因此文件大小会发生极大的变化。
(这里可能不确切,更大的可能是VMProtect的handler表变大了,由于测试指令过少,所以没有发生重复。如果真的每条指令都单独生成一系列handler,那加密指令多的时候文件会膨胀的可怕。但确定的一点是,原本复杂的循环结构消失了。)

0x02 版本对比

前面已经说过没有了循环结构,那么具体分析一下3.x的虚拟机是如何工作的。
首先简单说一下2.x的虚拟机,资料很多,不多说了,只提几个要点。

VMP虚拟机是栈式机,与x86体系结构的寄存器式虚拟机不同。x86的add eax, ebx指令,在VMP中会变成push ebx, push eax, add,  pop eax的形式。这里的栈称为虚拟栈。
EBP指向虚拟机栈顶,充当x86中的ESP功能,即虚拟机的vm_esp。
虚拟机执行的字节码是编码在程序中的,ESI指向其地址,ESI也就是vm_eip
EDI指向寄存器数组,也就是vm_context。2.x版本的vm_context也在栈中,在EBP上方。(其实最特别早期的VMP版本中vm_context也曾位于全局数据区,过段时间会写篇关于VMP各版本变化过程的文章,敬请期待)
vm_context包含16个4字节寄存器(32位的情况下)
栈结构是这样:
栈底--->ebp------>edi(vm_context)---> esp
EBX 经常参与解密,保存密钥。

说了关于旧版本的废话,这里直接抛出新版本的结构对比:
相同点:
ESI依然是vm_eip,指向要解析的字节码。
EBP依然是vm_esp,指向虚拟栈顶
EBX 依然经常参与解密。
不同点:
栈结构是这样:
栈底--->ebp------>esp(vm_context)
ESP成为vm_context指针EDI作为跳转寄存器,统一的dispatcher消失,每次跳转需要从ESI取出4字节进行解密加到EDI上,然后jmp edi(或push edi,ret 功能一样)
这里ESP成为vm_context指针的设计对我们来说是个好消息,因为vm_context不能随便移动,因此涉及到栈操作的垃圾指令大大减少了。
但jmp edi的跳转方式很让人头疼。
FKVMP和VMP分析插件等神器的基本逻辑是找到dispatcher 即jmp dword ptr ds:[eax*4+0x403103]类似的代码
然后枚举eax的值0~0xFF即可得到所有handler的入口,根据指令特征识别handler。依次解密出bytecode。

3.x不再有统一的dispatch过程,使得之前的神器从架构上无法应用到3.x版本。


0x3 具体分析

前面是为了有个统一的印象。
接下来具体分析。

我们直接看前面OD得到的Trace,实际的trace中有大量的垃圾指令,但正如前面所说,多不涉及栈操作,因此比较简单,以下的分析均是去了垃圾指令的,所以会发现地址不太连续。

虚拟机初始化部分

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

<ModuleEntryPoint>call console_.0040100C                   ; ESP=0019FF80

 

; jmp push call入口, 和vmp 2.x差不多

0040100C Main     jmp console_.00481F2C

00481F2C Main     push 0x6D3BB7D7                          ; ESP=0019FF7C

00481F31 Main     call console_.00436946                   ; ESP=0019FF78

 

;寄存器入栈

00436946 Main     push eax                                 ; ESP=0019FF74

00436948 Main     push edi                                 ; ESP=0019FF70

00436949 Main     push ebp                                 ; ESP=0019FF6C

0043694A Main     pushfd                                   ; ESP=0019FF68

0043694C Main     push edx                                 ; ESP=0019FF64

00436953 Main     push ebx                                 ; ESP=0019FF60

00436958 Main     push ecx                                 ; ESP=0019FF5C

0043695C Main     push esi                                 ; ESP=0019FF58

0043695D Main     mov eax,0x0                              ; EAX=00000000

00436965 Main     push eax                                 ; ESP=0019FF54

 

 

; ESI = 入口处压入的常数,解密ESI

00436968 Main     mov esi,dword ptr ss:[esp+0x28]          ; ESI=6D3BB7D7

0043696C Main     neg esi                                  ; FL=CAS, ESI=92C44829

0043696E Main     not esi                                  ; ESI=6D3BB7D6

0043697C Main     neg esi                                  ; FL=CAS, ESI=92C4482A

00436984 Main     bswap esi                                ; ESI=2A48C492

0043698A Main     not esi                                  ; ESI=D5B73B6D

0043698C Main     xor esi,0x2A080CB4                       ; FL=S, ESI=FFBF37D9

00436996 Main     not esi                                  ; ESI=0040C826

0043699B Main     lea esi,dword ptr ds:[esi+eax]

0043699E Main     inc al                                   ; FL=C, EAX=00000001

 

; 分配栈空间,仍为0xC0

; ebp仍为栈指针, esp指向context

004369A7 Main     mov ebp,esp                              ; EBP=0019FF54

004369B0 Main     sub esp,0xC0                             ; ESP=0019FE94

 

; 初始化EBX

004369BB Main     mov ebx,esi                              ; EBX=0040C826

 

 

; edi = 0x4369DE 初始化EDI

004369DE Main     lea edi,dword ptr ds:[0x4369DE]          ; EDI=004369DE



跳转到第一个handler

 

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

; 取ESI的4字节

004369EA Main     mov eax,dword ptr ds:[esi]               ; EAX=D7FE101E

; vEIP += 4

 

004369F0 Main     add esi,0x4                              ; FL=0, ESI=0040C82A

 

; 解码EAX

004369FE Main     xor eax,ebx                              ; FL=S, EAX=D7BED838

00436A00 Main     inc eax                                  ; FL=PS, EAX=D7BED839

0045D528 Main     ror eax,0x2                              ; FL=PS, EAX=75EFB60E

00410CB9 Main     inc eax                                  ; FL=P, EAX=75EFB60F

00410CBD Main     xor eax,0x75EF373B                       ; FL=0, EAX=00008134

0044A61F Main     dec eax                                  ; FL=P, EAX=00008133

00449463 Main     xor ebx,eax                              ; FL=0, EBX=00404915

 

; 跳转到 edi+eax

00449465 Main     add edi,eax                              ; FL=PA, EDI=0043EB11

00438226 Main     push edi                                 ; ESP=0019FE90

00438227 Main     retn                                     ; ESP=0019FE94



这里就开始是handler部分了,与2.x 一样,最开始的几条bytecode指令是把栈中的真实寄存器pop到虚拟寄存器数组vm_context中 

 

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

; ======================= handler

; vPopReg4 R14 (0x38)

; 这里handler命令规则采用VMP分析插件的命名风格,表示从栈中弹出4字节数据到寄存器R13中(以R0开头)

; 解码寄存器下标 al

0043EB11 Main     movzx eax,byte ptr ds:[esi]              ; EAX=000000F3

0043EB14 Main     add esi,0x1                              ; FL=P, ESI=0040C82B

0043EB1D Main     xor al,bl                                ; FL=S, EAX=000000E6

0043EB2A Main     not al                                   ; EAX=00000019

0043EB38 Main     xor al,0x7D                              ; FL=0, EAX=00000064

0043EB3F Main     inc al                                   ; FL=P, EAX=00000065

0043EB43 Main     xor al,0xF6                              ; FL=PS, EAX=00000093

0043EB49 Main     ror al,1                                 ; EAX=000000C9

0043EB4E Main     dec al                                   ; FL=CS, EAX=000000C8

0043EB58 Main     neg al                                   ; FL=CA, EAX=00000038

0043EB64 Main     xor bl,al                                ; EBX=0040492D

 

; 取出栈顶的值,退出4字节栈空间,赋到vm_context中

; 明显的pop操作,除了寄存器使用不同,实现方法其实和2.x是一样的

0043EB6B Main     mov ecx,dword ptr ss:[ebp]               ; ECX=00000000

0042B819 Main     add ebp,0x4                              ; FL=0, EBP=0019FF58

004539B2 Main     mov dword ptr ss:[esp+eax],ecx



第一条handler结束后,又是跳转
虽然功能完全一样,但VMP又实现出了完全独立了另一套代码
以此规避掉了循环 
 

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

; 接下来又从ESI取4字节,解密并跳转  上次解密代码的地址是004369EA

004539B5 Main     mov eax,dword ptr ds:[esi]               ; EAX=D7FCBE81

004539B7 Main     lea esi,dword ptr ds:[esi+0x4]           ; ESI=0040C82F

004539BE Main     xor eax,ebx                              ; FL=PS, EAX=D7BCF7AC

0044BADF Main     inc eax                                  ; FL=S, EAX=D7BCF7AD

0044BAE2 Main     ror eax,0x2                              ; EAX=75EF3DEB

0043D2C3 Main     inc eax                                  ; FL=0, EAX=75EF3DEC

0043D2C6 Main     xor eax,0x75EF373B                       ; FL=P, EAX=00000AD7

0045507E Main     dec eax                                  ; FL=0, EAX=00000AD6

00455081 Main     xor ebx,eax                              ; FL=0, EBX=004043FB

0045508A Main     add edi,eax                              ; FL=P, EDI=0043F5E7

0045508C Main     push edi                                 ; ESP=0019FE90

0045508D Main     retn                                     ; ESP=0019FE94



这里又是一个pop handler。
 

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

; ======================= handler

; vPopReg4 R8 (0x20)

0043F5E7 Main     movzx eax,byte ptr ds:[esi]              ; EAX=0000004D

0043F5EA Main     add esi,0x1                              ; FL=PA, ESI=0040C830

0043F5F6 Main     xor al,bl                                ; FL=S, EAX=000000B6

0043F5FD Main     not al                                   ; EAX=00000049

0043F5FF Main     xor al,0x7D                              ; FL=0, EAX=00000034

0043F605 Main     inc al                                   ; FL=P, EAX=00000035

0043F60E Main     xor al,0xF6                              ; FL=PS, EAX=000000C3

0043F616 Main     ror al,1                                 ; FL=CPS, EAX=000000E1

0043F61D Main     dec al                                   ; FL=CS, EAX=000000E0

0043F626 Main     neg al                                   ; FL=C, EAX=00000020

0043F628 Main     xor bl,al                                ; FL=PS, EBX=004043DB

 

0043F630 Main     mov ecx,dword ptr ss:[ebp]               ; ECX=00401000

0043F63A Main     add ebp,0x4                              ; EBP=0019FF5C

0043F643 Main     mov dword ptr ss:[esp+eax],ecx



这里又是一个跳转。
 

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

; 跳转到下条指令,与之前代码位置又都不同

0043F64E Main     mov eax,dword ptr ds:[esi]               ; EAX=28020B6A

0043F652 Main     lea esi,dword ptr ds:[esi+0x4]           ; ESI=0040C834

0043F65D Main     xor eax,ebx                              ; FL=P, EAX=284248B1

00467C71 Main     inc eax                                  ; EAX=284248B2

00467C72 Main     ror eax,0x2                              ; FL=CP, EAX=8A10922C

00426C32 Main     inc eax                                  ; FL=CPS, EAX=8A10922D

00426C34 Main     xor eax,0x75EF373B                       ; FL=S, EAX=FFFFA516

00426C39 Main     dec eax                                  ; EAX=FFFFA515

00426C3A Main     xor ebx,eax                              ; EBX=FFBFE6CE

00426C3C Main     add edi,eax                              ; FL=CP, EDI=00439AFC

00408854 Main     jmp edi



这里又是一个pop handler
 

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

; ======================= handler

; vPopReg4

00439AFC Main     movzx eax,byte ptr ds:[esi]              ; EAX=00000068

00439B06 Main     lea esi,dword ptr ds:[esi+0x1]           ; ESI=0040C835

00439B0C Main     xor al,bl                                ; EAX=000000A6

00439B18 Main     not al                                   ; EAX=00000059

00439B21 Main     xor al,0x7D                              ; FL=P, EAX=00000024

0047EF23 Main     inc al                                   ; FL=0, EAX=00000025

0047EF2C Main     xor al,0xF6                              ; FL=S, EAX=000000D3

0047EF32 Main     ror al,1                                 ; FL=CS, EAX=000000E9

0047EF3D Main     dec al                                   ; FL=CPS, EAX=000000E8

0047EF3F Main     neg al                                   ; FL=CPA, EAX=00000018

0047EF49 Main     xor bl,al                                ; FL=S, EBX=FFBFE6D6

 

0047EF50 Main     mov ecx,dword ptr ss:[ebp]               ; ECX=00401000

0047EF55 Main     lea ebp,dword ptr ss:[ebp+0x4]           ; EBP=0019FF60

0047EF5B Main     mov dword ptr ss:[esp+eax],ecx



基本模式:
ESI保存bytecode和跳转地址,每条Bytecode前有4字节跳转地址
进行跳转时将跳转地址读出来,进行解密,解密后的值再加上EDI
就是最终的跳转地址,即handler地址。
按照惯例,虚拟机入口pop出所有寄存器到vm_context
应该会产生大量的pop指令,不一一分析了,这里直接跳过。

跳过的bytecode如下

[Asm] 纯文本查看 复制代码

?

1

2

3

4

5

6

7

vPopReg4 R1 (0x4)  

vPopReg4 R3 (0xC)

vPopReg4 R0 (0x0)

vPopReg4 R11 (0x2C)

vPopReg4 R12

vPopReg4 R2

vPopReg4 R13



执行完10个pop,这里终于出现了新handler, 是压入立即数指令,也就是mov eax, 0deadbeefh对应的bytecode。

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

; vPushImm4 DEADBEEF

; 取立即数并解密

0047D362 Main     mov eax,dword ptr ds:[esi]               ; EAX=39A96DBB

0040F037 Main     lea esi,dword ptr ds:[esi+0x4]           ; ESI=0040C865

0040F03D Main     xor eax,ebx                              ; FL=S, EAX=C612D09B

0040F044 Main     neg eax                                  ; FL=CPA, EAX=39ED2F65

0040F047 Main     rol eax,0x2                              ; EAX=E7B4BD94

0040F04C Main     add eax,0x4B906ADA                       ; FL=C, EAX=3345286E

00477C1C Main     xor eax,0x7A4740E4                       ; FL=0, EAX=4902688A

00477C23 Main     sub eax,0x69850CCD                       ; FL=CPAS, EAX=DF7D5BBD

00477C28 Main     ror eax,1                                ; EAX=EFBEADDE

00477C2A Main     bswap eax                                ; EAX=DEADBEEF

00477C31 Main     xor ebx,eax                              ; FL=P, EBX=211603CF

; 压到栈中

00477C38 Main     sub ebp,0x4                              ; FL=A, EBP=0019FF7C

00477C42 Main     mov dword ptr ss:[ebp],eax




又是跳转,但这次稍有不同,因为是push指令,需要检查栈是否发生溢出,这与2.x是一致的

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

; 取下次跳转的地址

00477C46 Main     mov eax,dword ptr ds:[esi]               ; EAX=095A81E6

0043B383 Main     lea esi,dword ptr ds:[esi+0x4]           ; ESI=0040C869

0043B391 Main     xor eax,ebx                              ; FL=0, EAX=284C8229

0043ED4B Main     inc eax                                  ; EAX=284C822A

0043ED4C Main     ror eax,0x2                              ; FL=C, EAX=8A13208A

00436549 Main     inc eax                                  ; FL=CPS, EAX=8A13208B

00436550 Main     xor eax,0x75EF373B                       ; FL=S, EAX=FFFC17B0

00418A8E Main     dec eax                                  ; FL=PAS, EAX=FFFC17AF

00418A91 Main     xor ebx,eax                              ; FL=PS, EBX=DEEA1460

00418A93 Main     add edi,eax                              ; FL=CPA, EDI=0043EB11

; 由于是push指令,为了避免栈中数据与vm_context重合,需要检查栈溢出,这与2.x是一致的

; 检查栈溢出

0042D77B Main     lea eax,dword ptr ss:[esp+0x60]          ; EAX=0019FEF4

0042D784 Main     cmp ebp,eax                              

0042D786 Main     ja console_.00421B90



这部分代码不在trace中,因为实际不会溢出,这里单独从OD中拿出来作为分析,同样都去掉了垃圾指令
 

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

; 如果发生溢出

0042D78C    8BD4            mov edx,esp

0042D790    B9 40000000     mov ecx,0x40

; 分配0x80空间

0042D79C    8D4425 80       lea eax,dword ptr ss:[ebp-0x80] 

00472FEA    24 FC           and al,0xFC

00472FEC    2BC1            sub eax,ecx   

; 上抬vm_context位置

004783AE    8BE0            mov esp,eax 

004061B8    57              push edi                                ; console_.<ModuleEntryPoint>

004061B9    9C              pushfd

00474547    56              push esi                                ; console_.<ModuleEntryPoint>

00474548    8BF2            mov esi,edx                             ; console_.<ModuleEntryPoint>

0043AA70    8BF8            mov edi,eax

; 复制vm_context内容

004307BC    FC              cld   

00416140    F3:A4           rep movs byte ptr es:[edi],byte ptr ds:[esi]

 

 

00416148    5E              pop esi                                 ; kernel32.755362C4

004166E8    9D              popfd

004166EC    5F              pop edi                                 ; kernel32.755362C4

00456F23    57              push edi                                ; console_.<ModuleEntryPoint>

00456F24    C3              retn




接下来回到原本的分析过程中,由于重复了10遍mov eax, 0deadbeefh,
因此会出现10遍同样的handler,但地址各不相同
 

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

vPopReg4 R15(0x3C)

vPushImm4 DEADBEEF

vPopReg4 R4 (0x10)

vPushImm4 DEADBEEF

vPopReg4 R12(0x30)

vPushImm4 DEADBEEF

vPopReg4 R13(0x34)

vPushImm4 DEADBEEF

vPopReg4 R2 (0x08)

vPushImm4 DEADBEEF

vPopReg4 R9 (0x24)

vPushImm4 DEADBEEF

vPopReg4 R10(0x28)

vPushImm4 DEADBEEF

vPopReg4 R15(0x3C)

vPushImm4 DEADBEEF

vPopReg4 R4 (0x10)

vPushImm4 DEADBEEF

vPopReg4 R7 (0x1C)



这里可以看到同样的mov eax, 0deadbeefh ,pop的寄存器是不同的,可见2.x的寄存器轮转机制并没有变化。


新跳转

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

00450F03 Main     mov eax,dword ptr ds:[esi]               ; EAX=D7E129A1

00450F08 Main     lea esi,dword ptr ds:[esi+0x4]           ; ESI=0040C8E3

00450F0E Main     xor eax,ebx                              ; FL=0, EAX=2858C061

0043F841 Main     inc eax                                  ; EAX=2858C062

0042D4DE Main     ror eax,0x2                              ; FL=C, EAX=8A163018

00444518 Main     inc eax                                  ; FL=CS, EAX=8A163019

0044451A Main     xor eax,0x75EF373B                       ; FL=PS, EAX=FFF90722

0044451F Main     dec eax                                  ; EAX=FFF90721

00442C79 Main     xor ebx,eax                              ; FL=P, EBX=0040EEE1

00442C83 Main     add edi,eax                              ; FL=C, EDI=0040AC3D

00442C85 Main     jmp edi


新的handler ,这次是push,用于退出虚拟机时保存虚拟寄存器到栈中

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

; vPushReg4 R7(0x1C)

; 解密寄存器下标

0040AC3D Main     movzx eax,byte ptr ds:[esi]              ; EAX=0000005F

00454BB7 Main     add esi,0x1                              ; FL=P, ESI=0040C8E4

00454BBD Main     xor al,bl                                ; FL=PS, EAX=000000BE

00475727 Main     not al                                   ; EAX=00000041

00413C0A Main     xor al,0x7D                              ; FL=P, EAX=0000003C

00456F67 Main     inc al                                   ; FL=0, EAX=0000003D

00456F6B Main     xor al,0xF6                              ; FL=S, EAX=000000CB

00456F6E Main     ror al,1                                 ; EAX=000000E5

00447E46 Main     dec al                                   ; FL=CPS, EAX=000000E4

00447E48 Main     neg al                                   ; FL=CA, EAX=0000001C

00481E64 Main     xor bl,al                                ; FL=S, EBX=0040EEFD

; push reg

00481E67 Main     mov eax,dword ptr ss:[esp+eax]           ; EAX=DEADBEEF

00481E71 Main     lea ebp,dword ptr ss:[ebp-0x4]           ; EBP=0019FF7C

00481E7A Main     mov dword ptr ss:[ebp],eax



接下来的bytecode都是push指令,不一一分析了

[Asm] 纯文本查看 复制代码

?

1

2

3

4

5

6

7

vPushReg4 R11(0x2C)

vPushReg4 R0(0x0)

vPushReg4 R3(0xC)

vPushReg4 R1(0x4)

vPushReg4 R5(0x14)

vPushReg4 R6(0x18)

vPushReg4 R8(0x20)



又是一段跳转代码

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

00429FBE Main     mov eax,dword ptr ds:[esi]               ; EAX=D7F040B7

00429FC0 Main     lea esi,dword ptr ds:[esi+0x4]           ; ESI=0040C90B

00429FC6 Main     xor eax,ebx                              ; FL=S, EAX=D7B04B20

00429FC8 Main     inc eax                                  ; FL=PS, EAX=D7B04B21

00429FC9 Main     ror eax,0x2                              ; EAX=75EC12C8

00425BD2 Main     inc eax                                  ; FL=P, EAX=75EC12C9

00425BD4 Main     xor eax,0x75EF373B                       ; FL=0, EAX=000325F2

00425BD9 Main     dec eax                                  ; EAX=000325F1

00425BDA Main     xor ebx,eax                              ; FL=P, EBX=00432E66

00425BDF Main     add edi,eax                              ; FL=0, EDI=0045E09E

0042D77B Main     lea eax,dword ptr ss:[esp+0x60]          ; EAX=0019FEF4

0042D784 Main     cmp ebp,eax                              ; FL=PA

0042D786 Main     ja console_.00421B90

00421B90 Main     jmp edi




新的handler 
vRet, 功能与之前一样,从栈中将寄存器弹出到真实寄存器中。

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

0045E09E Main     mov esp,ebp                              ; ESP=0019FF60

0045E0AA Main     pop esi                                  ; ESP=0019FF64, ESI=00401000

0045E0AB Main     pop ecx                                  ; ECX=00401000, ESP=0019FF68

0045E0B2 Main     pop ebx                                  ; EBX=003F7000, ESP=0019FF6C

0045E0BA Main     pop edx                                  ; EDX=00401000, ESP=0019FF70

0045E0C7 Main     popfd                                    ; FL=PZ, ESP=0019FF74

0045E0CD Main     pop ebp                                  ; ESP=0019FF78, EBP=0019FF94

0045E0D6 Main     pop edi                                  ; ESP=0019FF7C, EDI=00401000

0045E0DB Main     pop eax                                  ; EAX=DEADBEEF, ESP=0019FF80

004822D1 Main     retn                                     ; ESP=0019FF84

 

; 这里已经是非虚拟化的代码了

00401005 Main     push 0x0                                 ; ExitCode = 0x0, ESP=0019FF80

00401007 Main     call <jmp.&kernel32.ExitProcess>         ; FL=0, EAX=00401000, ECX=00000000, EDX=00000000, EBX=7707F9A0, ESP=0019FE8C, EBP=0019FF64, ESI=00000000, EDI=00000000

; 程序退出



我们把1.81 demo的bytecode提取出来,这里用到VMP分析插件1.4这个神器(由于是demo版本,FKVMP是不行的)
 

[Asm] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

00403765 VMP_0040100C  /$  78           vPopReg4 vR14        DWORD _t0 = 0

00403766               |.  6B F70C72B7  vPushImm4 0B7720CF7  DWORD _t1 = 0B7720CF7

0040376B               |.  39           vAdd4                DWORD _t2 = 0B7720CF7; DWORD _t3 = AddFlag(_t1, check00000000)

0040376C               |.  74           vPopReg4 vR13        DWORD _t4 = _t3

0040376D               |.  5C           vPopReg4 vR7         DWORD _t5 = 0B7720CF7

0040376E               |.  40           vPopReg4 vR0         DWORD _t6 = EAX

0040376F               |.  48           vPopReg4 vR2         DWORD _t7 = EDX

00403770               |.  64           vPopReg4 vR9         DWORD _t8 = EBP

00403771               |.  50           vPopReg4 vR4         DWORD _t9 = EDX

00403772               |.  74           vPopReg4 vR13        DWORD _t10 = ECX

00403773               |.  44           vPopReg4 vR1         DWORD _t11 = EDI

00403774               |.  54           vPopReg4 vR5         DWORD _t12 = ESI

00403775               |.  70           vPopReg4 vR12        DWORD _t13 = EFL

00403776               |.  68           vPopReg4 vR10        DWORD _t14 = EBX

00403777               |.  4C           vPopReg4 vR3         DWORD _t15 = 401016

00403778               |.  58           vPopReg4 vR6         DWORD _t16 = 403765

00403779               |.  FA EFBEADDE  vPushImm4 0DEADBEEF  DWORD _t17 = 0DEADBEEF

0040377E               |.  4C           vPopReg4 vR3         DWORD _t18 = 0DEADBEEF

0040377F               |.  E9 EFBEADDE  vPushImm4 0DEADBEEF  DWORD _t19 = 0DEADBEEF

00403784               |.  58           vPopReg4 vR6         DWORD _t20 = 0DEADBEEF

00403785               |.  23 EFBEADDE  vPushImm4 0DEADBEEF  DWORD _t21 = 0DEADBEEF

0040378A               |.  40           vPopReg4 vR0         DWORD _t22 = 0DEADBEEF

0040378B               |.  B4 EFBEADDE  vPushImm4 0DEADBEEF  DWORD _t23 = 0DEADBEEF

00403790               |.  60           vPopReg4 vR8         DWORD _t24 = 0DEADBEEF

00403791               |.  23 EFBEADDE  vPushImm4 0DEADBEEF  DWORD _t25 = 0DEADBEEF

00403796               |.  4C           vPopReg4 vR3         DWORD _t26 = 0DEADBEEF

00403797               |.  6B EFBEADDE  vPushImm4 0DEADBEEF  DWORD _t27 = 0DEADBEEF

0040379C               |.  7C           vPopReg4 vR15        DWORD _t28 = 0DEADBEEF

0040379D               |.  B4 EFBEADDE  vPushImm4 0DEADBEEF  DWORD _t29 = 0DEADBEEF

004037A2               |.  4C           vPopReg4 vR3         DWORD _t30 = 0DEADBEEF

004037A3               |.  6B EFBEADDE  vPushImm4 0DEADBEEF  DWORD _t31 = 0DEADBEEF

004037A8               |.  6C           vPopReg4 vR11        DWORD _t32 = 0DEADBEEF

004037A9               |.  E9 EFBEADDE  vPushImm4 0DEADBEEF  DWORD _t33 = 0DEADBEEF

004037AE               |.  40           vPopReg4 vR0         DWORD _t34 = 0DEADBEEF

004037AF               |.  23 EFBEADDE  vPushImm4 0DEADBEEF  DWORD _t35 = 0DEADBEEF

004037B4               |.  58           vPopReg4 vR6         DWORD _t36 = 0DEADBEEF

004037B5               |.  28           vPushReg4 vR10       EBX DWORD v0 = EBX

004037B6               |.  30           vPushReg4 vR12       EFL DWORD v1 = EFL

004037B7               |.  14           vPushReg4 vR5        ESI DWORD v2 = ESI

004037B8               |.  04           vPushReg4 vR1        EDI DWORD v3 = EDI

004037B9               |.  34           vPushReg4 vR13       ECX DWORD v4 = ECX

004037BA               |.  10           vPushReg4 vR4        DWORD _t42 = _t9

004037BB               |.  24           vPushReg4 vR9        EBP DWORD v5 = EBP

004037BC               |.  08           vPushReg4 vR2        EDX DWORD v6 = EDX

004037BD               |.  18           vPushReg4 vR6        EAX DWORD v7 = 0DEADBEEF

004037BE               |.  30           vPushReg4 vR12       DWORD _t46 = _t13

004037BF               |.  1C           vPushReg4 vR7        DWORD _t47 = 0B7720CF7

004037C0               \.  A4           vRet                 return Stack(34, 4)



对比可以发现其实bytecode是基本一致的,但是内部实现已经发生了巨大变化。

这里简单分析就结束了。
至于条件跳转等其他实现细节,以后再慢慢讨论

0x04 写在最后

从今年2月分开始的连续几个月时间可能都要研究虚拟机保护相关的内容,因此也会陆续发些心得笔记上来。
这里希望论坛大大们可以给些指点,好少走点弯路。
也希望其他坛友们能多多回帖讨论。


相关文件都放在附件中了
解压密码www.52pojie.cn
深夜发贴,求鼓励
以上
 

vmp_3.09.zip

 

373.85 KB, 下载次数: 379, 下载积分: 吾爱币 -1 CB

 

VMP3分析文件

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Vprotect 是一款基于虚拟机的可执行文件加密授权系统。 VProtect支持多种类型的文件格式: Win32 可执行文件 (*.exe); Windows 屏幕保护程序 (*.scr); 动态链接库 (*.dll); 32位 ActiveX 控件 (*.ocx); 32位驱动程序(*.sys) 其他32位可执行程序; 64位可执行程序; 支持的编译器: Assembly language: MASM, FASM, POASM, TASM Basic: Visual Basic, Pure Basic, Power Basic C/C++: Visual C/C++, Borland C++ builder, Intel C++, Dev C++, Digital Mars C++, MinGW Pascal: Delphi, Free Pascal D Programming language: DMD 其他编译器保护功能: 一键授权系统,无需写一行代码就可以给您的软件添加高安全性注册授权系统 原创虚拟机保护引擎(什么是虚拟机保护参考帮助文档) 原创深度虚拟化保护(深度虚拟化保护和普通保护区别参考帮助文档) 随机指令集 随机填充代码 代码乱序执行 外壳保护 反内存转储存 区段合并 资源加密 反调试 防修改 自效验 输入表加密 软件特色: 加密强度 使用目前先进的虚拟机乱序等引擎,可以阻止绝大多数人逆向分析。 执行效率 软件使用SDK,进行重点加密(如:注册逻辑,关键算法,详细信息参考帮助文档)。 所以基本上不影响程序运行效率 兼容性 全面支持主流的 32/64(*) 位 Windows 操作系统,能够稳定运行于 Intel/AMD 各种 32/64 位及多核处理器; 绝大多数加密后的程序均可正确运行。 自带授权系统,正常用户管理系统,黑名单,加密SDK,授权API等实用功能 分析使用Vprotect保护后的程序,将不仅仅是一项技术活,同时也会成为高强度的体力活。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值