首页
社区
课程
招聘
[原创]对虚拟机壳WProtect加密Demo bin的逆向分析
发表于: 2015-11-19 16:14 10705

[原创]对虚拟机壳WProtect加密Demo bin的逆向分析

2015-11-19 16:14
10705

对虚拟机壳WProtect加密Demo bin的逆向分析

以前没有接触过脱壳,非常感谢handsomexiaowei@gmail.com开源WProtect,才对虚拟机壳有了一点认识。

文中分析的bin来自于XiaoWei发布于看雪的WProtect,本文分析其中的add_test.wp.exe.

文题为逆向分析,笔者在实际分析中交叉使用了windbg,并且大量阅读了源码。

贴出add_test的源码
section .text
jmp start

d_add:
add eax,1
ret
nop

w_add:
add ax,1
ret

bl_add:
add al,1
ret
nop
nop

bh_add:
add ah,1
ret
nop
nop

start:
xor eax,eax
call d_add
xor eax,eax
call w_add
xor eax,eax
call bl_add
xor eax,eax
call bh_add
ret

add_test.wp.exe对里面的4个call进行了加密。
.text:00401017 ; START OF FUNCTION CHUNK FOR start
.text:00401017
.text:00401017 loc_401017:                             ; CODE XREF: startj
.text:00401017                 xor     eax, eax
.text:00401019                 call    j_s_1002
.text:0040101E                 xor     eax, eax
.text:00401020                 call    j_s_1007
.text:00401025                 xor     eax, eax
.text:00401027                 call    j_s_100C
.text:0040102C                 xor     eax, eax
.text:0040102E                 call    j_s_1011
.text:00401033                 retn
.text:00401033 ; END OF FUNCTION CHUNK FOR start

j_s_1002是第一个函数,内容本来是“add eax,1”、“ret”,此处明显“入瓮”了;

eax=00000000 ebx=7ffdf000 ecx=0022ffb0 edx=7c92e4f4 esi=0292f758 edi=0292f6ee
eip=00401002 esp=0022ffc0 ebp=0022fff0

.text:00401002 j_s_1002        proc near               ; CODE XREF: start+19p
.text:00401002                 jmp     s_1002
.text:00401002 j_s_1002        endp

这里跳到s_1002,注意这个地址,位于WProtect新增的区段内,
.WProtec:004043A4 s_1002          proc near               ; CODE XREF: j_s_1002j
.WProtec:004043A4                 push    esi
.WProtec:004043A5                 push    ecx
.WProtec:004043A6                 push    edx
.WProtec:004043A7                 push    ebp
.WProtec:004043A8                 push    edi
.WProtec:004043A9                 push    ebx
.WProtec:004043AA                 push    eax
.WProtec:004043AB                 push    1E240h
.WProtec:004043B0                 pushf
.WProtec:004043B1                 mov     ebx, 12345678h
.WProtec:004043B6                 mov     ebp, esp
.WProtec:004043B8                 sub     esp, 0C8h
.WProtec:004043BE                 mov     edi, esp
.WProtec:004043C0                 mov     esi, offset byte_4042D1 ; esi = pcode
.WProtec:004043C5                 jmp     dispatch
.WProtec:004043C5 s_1002          endp

前面8个push加一个pushf,有个1E240,在源码内是取个了变量的地址,作用是某Key;
offset byte_4042D1 这里是虚拟指令的开始。然后跳入了dispatch,也就是vm_handle_dispatch;

.WProtec:00403CD3 dispatch:                               ; CODE XREF: s_1007+21j
.WProtec:00403CD3                                         ; s_100C+21j ...
.WProtec:00403CD3                 not     bx
.WProtec:00403CD6                 sub     bx, 0F3E9h
.WProtec:00403CDB                 movzx   eax, byte ptr [esi-1]
.WProtec:00403CDF                 dec     esi
.WProtec:00403CE0                 ror     al, 4
.WProtec:00403CE3                 not     al
.WProtec:00403CE5                 ror     al, 9
.WProtec:00403CE8                 ror     al, 0Dh
.WProtec:00403CEB                 push    ds:dword_4038D7[eax*4] ; eax = handle_index
.WProtec:00403CF2                 retn
.WProtec:00403CF2 ; END OF FUNCTION CHUNK FOR s_100C

esi指向虚拟指令,源码里可以指定这里的地址增长方向,此处是递减;
.WProtec:00403CEB 处的eax是实际的handle_index,笔者下到的源码有50+个handle,
对应于N多条指令的解析和若干辅助作用的handle;
对于不同的bin或者说不同的虚拟机实体,handle_index是不确定的,源码里对handle_table进行了乱序操作。

对下面两处下断:
.text:00401019                 call    j_s_1002
.text:0040101E                 xor     eax, eax
步入call    j_s_1002;
然后对00403CEB下断,然后连续F5,直到0040101E断点命中,可以看到j_s_1002内dispatch被执行了25次;
其中每次断到00403CEB push    ds:dword_4038D7[eax*4],eax即是handle_index;
可见eax=0x22的handle被调用了很多次,原因是一开始需要save_vm_context(8个push和1个pushf共9次);
保存最开始push的寄存器值,到栈中的vm_context里;这个操作是用d_pop_reg来实现的,d_pop_reg在该bin中的handle_index为0x22;
而在对于指令的模拟中,WProtect是用栈来传递值的,所以在指令的模拟中也大量用到了d_pop_reg。

.WProtec:004035BC f_22_d_pop_reg  proc near
.WProtec:004035BC
.WProtec:004035BC ; FUNCTION CHUNK AT .WProtec:0040379A SIZE 00000017 BYTES
.WProtec:004035BC
.WProtec:004035BC                 not     ebx
.WProtec:004035BE                 sub     bl, 3Ah
.WProtec:004035C1                 add     ebx, 6690842Eh
.WProtec:004035C7                 not     bl
.WProtec:004035C9                 ror     ebx, 7
.WProtec:004035CC                 xor     ebx, 2E2EDB1Ah
.WProtec:004035D2                 xor     bl, 0BBh
.WProtec:004035D5                 mov     al, [esi-1]
.WProtec:004035D8                 dec     esi
.WProtec:004035D9                 add     al, bl
.WProtec:004035DB                 sub     al, bl
.WProtec:004035DD                 rol     al, 2
.WProtec:004035E0                 ror     al, 7
.WProtec:004035E3                 ror     al, 1Eh
.WProtec:004035E6                 inc     al
.WProtec:004035E8                 movzx   eax, al
.WProtec:004035EB                 mov     edx, [ebp+0]
.WProtec:004035EE                 mov     [edi+eax], edx
.WProtec:004035F1                 add     ebp, 4
.WProtec:004035F4                 jmp     j_checkstack
.WProtec:004035F4 f_22_d_pop_reg  endp

重点是:
mov     edx, [ebp+0]
mov     [edi+eax], edx
add     ebp, 4
这里edi是vm_context的基址,里面存一开始push的9个值,eax代表偏移,跟踪每次的变化,结合开始push的顺序,得:

edi = 0x22fed4 --- vm_context_Base
Offset, Value, Register

0022ff9c  00000246 0001e240 00000000 7ffde000
0022ffac  0292f6ee 0022fff0 7c92e4f4 0022ffb0
0022ffbc  0292f758

+44 0292f758  push    esi
+30 0022ffb0  push    ecx
+28 7c92e4f4  push    edx
+48 0022fff0  push    ebp
+50 0292f6ee  push    edi
+ c 7ffde000  push    ebx
+14 00000000  push    eax
+5c 0001e240  push    1E240h
+10 00000246  pushf

接着就是对实际指令的模拟了,可见跳入了handle_index(eax = d)中;

eax=0000000d
00403ceb  push    dword ptr image00400000+0x38d7 (004038d7)[eax*4]
00403cf2  ret

.WProtec:00403316 f_d_Push_Imm    proc near
.WProtec:00403316                 inc     ebx
.WProtec:00403317                 not     ebx
.WProtec:00403319                 ror     ebx, 19h
.WProtec:0040331C                 mov     eax, [esi-4]
.WProtec:0040331F                 sub     esi, 4
.WProtec:00403322                 sub     eax, ebx
.WProtec:00403324                 sub     eax, ebx
.WProtec:00403326                 sub     ebp, 4
.WProtec:00403329                 mov     [ebp+0], eax
.WProtec:0040332C                 jmp     j_checkstack
.WProtec:0040332C f_d_Push_Imm    endp

跟到这里,重点是:
sub     ebp, 4
mov     [ebp+0], eax
前后指令是对已加密的pcode的解密,动态调试只要跟到这里就能看到真值eax = 1,
可见此handle在本次调用中意义是push 1;

后面跳入了handle_index(eax = 6)中;

eax=00000006
00403ceb  push    dword ptr image00400000+0x38d7 (004038d7)[eax*4]
00403cf2  ret

.WProtec:00403254 f_6_d_push_reg proc near
.WProtec:00403254                 inc     bx
.WProtec:00403256                 ror     ebx, 4
.WProtec:00403259                 dec     ebx
.WProtec:0040325A                 mov     al, [esi-1]
.WProtec:0040325D                 dec     esi
.WProtec:0040325E                 xor     al, bl
.WProtec:00403260                 inc     al
.WProtec:00403262                 sub     al, bl
.WProtec:00403264                 rol     al, 7
.WProtec:00403267                 rol     al, 1Bh
.WProtec:0040326A                 movzx   eax, al
.WProtec:0040326D                 mov     eax, [edi+eax]
.WProtec:00403270                 sub     ebp, 4
.WProtec:00403273                 mov     [ebp+0], eax
.WProtec:00403276                 jmp     j_checkstack
.WProtec:00403276 f_6_d_push_reg endp

重点是:
mov     eax, [edi+eax]  ;eax = 0x14
sub     ebp, 4
mov     [ebp+0], eax

结合前面save_vm_context我们记录的表,
可以得出这里把[edi+eax]代表的寄存器的值(eax)压到栈里,就是push eax;

后面跳入了handle_index(eax = c)中;

eax=0000000c
00403ceb  push    dword ptr image00400000+0x38d7 (004038d7)[eax*4]
00403cf2  ret

.WProtec:004032ED f_c_AddEbpEbp4 proc near
.WProtec:004032ED                 ror     bl, 8
.WProtec:004032F0                 xor     bl, 0F1h
.WProtec:004032F3                 dec     ebx
.WProtec:004032F4                 sub     ebx, 15C852E7h
.WProtec:004032FA                 add     ebx, 24DA07FEh
.WProtec:00403300                 xor     ebx, 6FA1625Ch
.WProtec:00403306                 dec     ebx
.WProtec:00403307                 mov     eax, [ebp+0]
.WProtec:0040330A                 add     [ebp+4], eax
.WProtec:0040330D                 pushf
.WProtec:0040330E                 pop     dword ptr [ebp+0]
.WProtec:00403311                 jmp     j_checkstack
.WProtec:00403311 f_c_AddEbpEbp4 endp

重点是:
mov     eax, [ebp+0]
add     [ebp+4], eax
pushf
pop     dword ptr [ebp+0]

结合上面,push 1,push eax,可见把这两个值相加,结果存到[ebp+4],eflag存到[ebp+0];

后面跳入了handle_index(eax = 22)f_22_d_pop_reg中;

mov     edx,dword ptr [ebp]
mov     dword ptr [edi+eax]  ;eax = 0x10
add     ebp, 4

可见把前面add后的eflag经过栈传回了vm_context;
再一个f_22_d_pop_reg把add的结果传回vm_context中的eax中。

总结上面的若干vm_handle,共同实现了add eax, 1

=====================================================================

d_add:
add eax,1
ret
nop

WProtect里忽略了nop,所以d_add里的有效指令只有两条,也就是ret是最后一条指令,
实际会在return前把vm_contect里的值退回Register中。

{  // 这两步是对程序结果是无影响的
继续跟,进入到handle_index(eax = d)f_d_Push_Imm中;
eax=00404367
ebp=0022ffbc
sub     ebp, 4
mov     dword ptr [ebp],eax

后面跳入了handle_index(eax = 22)f_22_d_pop_reg中;

mov     edx,dword ptr [ebp]       ; ebp=0022ffbc
mov     dword ptr [edi+eax], edx  ; eax = 0x20
add     ebp, 4
}

后面跳入了handle_index(eax = 6)f_6_d_push_reg中(8次);

.WProtec:0040326D                 mov     eax, [edi+eax]
.WProtec:00403270                 sub     ebp, 4
.WProtec:00403273                 mov     [ebp+0], eax

edi=0022fed4
push [edi+0x10] eflag
push [edi+0x14] eax
push [edi+0x30] ecx
push [edi+0x28] edx
push [edi+0x c] ebx
push [edi+0x48] ebp
push [edi+0x44] esi
push [edi+0x50] edi

后面跳入了handle_index(eax = a)f_a_ret中;

.WProtec:004032B8 f_a_ret         proc far
.WProtec:004032B8                 mov     esp, ebp
.WProtec:004032BA                 pop     edi
.WProtec:004032BB                 pop     esi
.WProtec:004032BC                 pop     ebp
.WProtec:004032BD                 pop     ebx
.WProtec:004032BE                 pop     edx
.WProtec:004032BF                 pop     ecx
.WProtec:004032C0                 pop     eax
.WProtec:004032C1                 popf
.WProtec:004032C2                 retn
.WProtec:004032C2 f_a_ret         endp

.WProtec:004032C2 处这一个 retn就破壳而出了,注意这里的8个pop是与8个f_6_d_push_reg相对应的。

这个retn为什么能出壳,纵观一下栈的变化:
入壳时push 4Bytes*9,
   f_22_d_pop_reg*9,
   f_d_Push_Imm
   f_6_d_push_reg
   f_22_d_pop_reg*2
   {  // 无意义
    f_d_Push_Imm,
    f_22_d_pop_reg
   }
   f_6_d_push_reg*8
   pop 4Bytes*8
   retn

-END-


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 3
支持
分享
最新回复 (2)
雪    币: 70
活跃值: (72)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
谢谢分享。。
2015-11-19 16:35
0
雪    币: 12321
活跃值: (5078)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
只能说强大,虽然强大
2015-11-19 18:41
0
游客
登录 | 注册 方可回帖
返回
//