【文章标题】: VMProtect进入虚拟机时
【文章作者】: BUG
【作者邮箱】: bughoho@hotmail.com
【作者QQ号】: 393277421
【使用工具】: OD and IDA5.0
【操作平台】: XP SP2
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
进入虚拟机时当前堆栈
0013FEA8 00000000 偏移
0013FEAC 7FFD6000 ebx
0013FEB0 00000000 edi
0013FEB4 00900C90 eax
0013FEB8 00000246 e_flag
0013FEBC 7C92EB94 edx
0013FEC0 00000001 ecx
0013FEC4 7C92EB94 edx
0013FEC8 00000A28 esi
0013FECC 0013FEE4 ebp
0013FED4 93CA0014 Key
根据forgot当时的堆栈
[00000000]
[EDI]
[EFlags]
[ESI]
[EAX]
[EBP]
[EDX]
[ECX]
[EBX]
[EAX]
[VMSEG_CODE]
[key0]//这里为E95243D1
可以看出这个寄存器压入的位置是乱序的,并且在结构中的位置也是随机生成的
进入虚拟机之前压入所有寄存器的值,
然后用伪代码存放到自己的VM_Register中
/*笔记*/
esi 存放的加密伪代码在当前位置
edi 寄存器的基地址
ebx 存放的加密伪代码起始地址,然后每解密一句就把bl变形,得到一个新的地址,
粗略看了下,好象当前解密的是1字节的就使用1字节解密,4字节就使用4字节解密
/*笔记*/
struct VM_Context
{
DWORD VM_Stack[256];//240*4+已占用堆栈长度*16,总长度1024字节
//随机排序的
//VM_Register:
DWORD e_flag3;//+00 //多个地址保存多个符号?或者一行伪代码用一个地址保存符号?
DWORD AddResult;//+04//加法结果?
DWORD edi;//+08
DWORD ;//+0c
DWORD offset;//+10//弹出虚拟机进入时压入的偏移,通过这个偏移来找VM_CODE
DWORD vmencode_code;//+14 //加密的伪代码存放地址.....又存放了符号?
DWORD ebp;//+18
DWORD XorResult;//+1c//异或结果
DWORD eax;//+20
DWORD ebx;//+24
DWORD e_flag;//+28
DWORD ecx;//+2c
DWORD edx;//+30
DWORD edx2;//+34
DWORD e_flag2;//+38
DWORD esi;//+3c
}
VMPBEGIN
addr = (DWORD)str;
_asm
{
pushad
mov eax,addr
nop
popad
}
func2();
VMPEND
反汇编得到
jump到执行虚拟机的地方
.vmp0:0040F40B push ebp
.vmp0:0040F40C push esi
.vmp0:0040F40D push edx
.vmp0:0040F40E push ecx
.vmp0:0040F40F push edx
.vmp0:0040F410 pushf
.vmp0:0040F411 push eax
.vmp0:0040F412 push edi
.vmp0:0040F413 push ebx
.vmp0:0040F414 push 0
.vmp0:0040F419 mov esi, [esp+28h] ; esi = VMEncode_Code
.vmp0:0040F41D mov edx, offset VM_Stack ; 1024字节?
.vmp0:0040F422 call ds:GetCurrentThreadId()
.vmp0:0040F428 mov ebx, eax
.vmp0:0040F42A mov ecx, 100h
.vmp0:0040F42F mov edi, edx
.vmp0:0040F431 pushf
.vmp0:0040F432 cld
.vmp0:0040F433 repne scasd
.vmp0:0040F435 jz short loc_40F444
.vmp0:0040F435
.vmp0:0040F437 mov eax, 100h
.vmp0:0040F43C xchg eax, ecx
.vmp0:0040F43D mov edi, edx
.vmp0:0040F43F repne scasd
.vmp0:0040F441 mov [edi-4], ebx ; 堆栈第一项 = 当前线程ID
.vmp0:0040F441
.vmp0:0040F444
.vmp0:0040F444 loc_40F444: ; CODE XREF: sub_40F40B+2Aj
.vmp0:0040F444 mov ebp, edi
.vmp0:0040F446 sub edi, edx
.vmp0:0040F448 shl edi, 1
.vmp0:0040F44A lea edi, [edx+edi*8+3C0h] ; VM虚拟机环境寄存器基地址
.vmp0:0040F451 popf
.vmp0:0040F451
.vmp0:0040F452
.vmp0:0040F452 loc_40F452: ; CODE XREF: sub_40F40B+581j
.vmp0:0040F452 mov ebx, esi
.vmp0:0040F454 add esi, [esp+0] ; VMEncode_Code += 偏移,就是上面最后压入的一个0
.vmp0:0040F454
.vmp0:0040F457
.vmp0:0040F457 executeEip: ; CODE XREF: sub_40F40B-E3EBj
.vmp0:0040F457 ; sub_40F40B-5j
.vmp0:0040F457 ; sub_40F40B+73j
.vmp0:0040F457 ; sub_40F40B+81j
.vmp0:0040F457 ; sub_40F40B+8Bj
.vmp0:0040F457 ; sub_40F40B+92j ...
.vmp0:0040F457 mov cl, [esi] ; 解密当前的p_code
.vmp0:0040F459 add cl, bl
.vmp0:0040F45B xor cl, 64h
.vmp0:0040F45E neg cl
.vmp0:0040F460 add esi, 1
.vmp0:0040F463 sub cl, 6Fh
.vmp0:0040F466 neg cl
.vmp0:0040F468 xor cl, 31h
.vmp0:0040F46B add bl, cl ; 变换一下密钥
.vmp0:0040F46D movzx eax, cl ; eax = 解密后的p_code
.vmp0:0040F470 jmp dword ptr ds:JumpCommand[eax*4] ;
看了下前面执行的一点点伪代码
伪代码:
vPop offset;//还没明白做什么的
vPop ebx
vPop edi
vPop eax
vPop e_flag
vPop edx
vPop ecx
vPop edx
vPop esi
vPop ebp
vPop VM_Context->vmencode_code
vvPushdw dword //压入一个DWORD到真实堆栈(写在伪代码中解密得到)
vAdd [esp],[esp+4] //add...
vPop VM_Context->e_flag //保存符号
vvPushdwResultAddr //压入结果的地址
vvPushdwResult //压入结果
vPop VM_Context->AddResult;//保存结果
vvPushVM_Addresult //从VM结构中将加法结果压入到真实堆栈
vXor [esp],[esp-4] //[esp-4]表示弹出的操作数..
//做异或操作,Key和这个操作数做异或操作
//VM的操作是 1 not 0;1 not 0;0 and 0 = 0; 1 xor 1 = 0;
vPop VM_Context->XorResult//保存异或结果
vvPushdw dword //压入操作数
vXor [esp],[esp-4] //Key和这个操作数做异或操作
vPop VM_Context->e_flag2 //把符号保存到这个地址.
vvPushdw dword //压入操作数
vvPushVM_Addresult //从VM结构中将加法结果压入到真实堆栈
vXor [esp],[esp-4] //做异或操作
vPop VM_Context->e_flag3 //保存符号
vXor [esp],[esp-4] //Key和上一次保存的值做异或
vPop VM_Context->e_flag2 //保存符号
vvPushdwResultAddr //压入结果的地址
vvPushdwResult //压入结果
vXor [esp],[esp-4] //Key和上一次保存的值做异或
vPop VM_Context->e_flag* //保存符号
vvPushbyte //压入操作数.长度byte
vadd [esp],[esp-4] //Key和上一次保存的值做异或
vPop VM_Context->e_flag2 //保存符号
....单步时,OD卡住了....
反正跟到这里时还没看到我想看到的伪代码.
--------------------------------------------------------------------------------
【经验总结】
总结下,进入虚拟机时VMP随机把寄存器压入堆栈,存放当前ThreadID到VM_Stack.
然后通过自己的伪代码取出存放到自己的VM_REG中去(当然这个随机顺序是生成EXE时定下来的).
结构中的位置也是随机生成的.
and al,0x3C 一得到VM结构一些寄存器或其他位置的骗移
解密伪代码有一个庞大的算法,写出来不易..
VM_CODE的地址当KEY,然后每解一个操作码会变形这个KEY的低字节,每一个伪代码都是不同的变形算法.
这些涉及到立即数的地方应该是VMP随机生成的.
换句话说,写解密算法得把每一个命令的函数里的那一小块加密代码全搞出来
结论是..不是人干的活.
VMP的伪代码都是在堆栈中做运算的,带vv的是涉及到传输VM数据的伪代码.
另外相关伪代码会保存标志寄存器.防止出错.
就研究了这么点...
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2007年03月15日 0:38:37