今天儿子较乖,让我能静下心来多打会电脑。
早就想试下heXer的key了,没找到vmp1.64,只好装了个1.63,竟然也注册成功,感受了key的强大。
之前跟过一个海风提供的用vmp1.60的notepad,变形很多,vmopcodetable也加密了,虽然rokyxue给出了简化方法,不过还是不敢跟进去,怕浪费太多的时间和精力。手工怎能和机器对抗?
目的:希望能正向分析一个自己设计的加了vmp的代码,在vmp中寻找原始代码的痕迹。
根据rokyxue分析,vmp1.63虽然加了很多变形,但与vmp1.2x虚拟机主体相同。确认在vmp引擎中仍保留的是vmp1.2x的opcode结构,同时esi为opcode指针。ecx为opcode解密后的地址。
过程:
写了个最简单的明码比较的crackme,在注册按钮事件里加了vmp的marker。
代码:
#define VMBEGIN __asm { _emit 0xEB _emit 0x10 _emit 0x56 _emit 0x4D _emit 0x50 _emit 0x72 _emit 0x6F _emit 0x74 _emit 0x65 _emit 0x63 _emit 0x74 _emit 0x20 _emit 0x62 _emit 0x65 _emit 0x67 _emit 0x69 _emit 0x6E _emit 0x00 } namelen=GetDlgItemText(IDC_NAME,&name[0],15); regcodelen=GetDlgItemText(IDC_REGCODE,®code[0],4095); if (namelen==0||namelen>15||regcodelen!=16) return; strupr(®code[0]);// for(tmpi=0;tmpi<regcodelen;tmpi++) { if (regcode[tmpi]>0x39||regcode[tmpi]<0x30) if(regcode[tmpi]>'F'||regcode[tmpi]<'A') return; } if(!strcmp(&name[0],"wangdell")&&!strcmp(®code[0],"FEDCBA9876543210")) MessageBox("OK!",0,MB_OK); else MessageBox("FAILED!",0,MB_OK); #define VMEND __asm { _emit 0xEB _emit 0x0E _emit 0x56 _emit 0x4D _emit 0x50 _emit 0x72 _emit 0x6F _emit 0x74 _emit 0x65 _emit 0x63 _emit 0x74 _emit 0x20 _emit 0x65 _emit 0x6E _emit 0x64 _emit 0x00 }
一、最大速度的(vmptest-ms.exe)
1、寻找虚拟机引擎
在vmp段下断点。shift-f9直达vmp引擎。(0x42d91d)
2、寻找虚拟机取指令处
f7单步执行直到
代码:
0042DD96 8B0C85 A6F64200 mov ecx, dword ptr [eax*4+42F6A6] ; getcodeaddr
继续f7,观察ecx直到其变化为一个正常的位于vmp区段地址,此时opcode地址已解密。
继续f7,观察代码窗口,当发现retn xx时,停止,并加标签vm_execute,如下
代码:
0042F35E C2 5400 retn 54 ; vm_excute
通常如果第一次通过retn跳转进入的就是VM_push_CT了。不过我们用个统计的方法,使用如下OD脚本
代码:
/* Script written by wangdell record opcode 20080802 //0x42e30b */ //test for vmprotect 1.63 release var tmp1 var tmp2 var tmp3 var tmp4 var tmp5 var tmp6 var tmp7 var tmp8 var tmp9 var tmp10 var imgbase var bpVMenginejmp var count cmp $VERSION, "1.47" //比较版本是否>1.47 jb odbgver dbh //hide od BPHWCALL //clear hardware breakpoint BC //clear software breakpoint BPMC //clear Memory breakpoint mov count,0 ask_jmp: ask "Enter EIP of VM_execute" mov bpVMenginejmp,$RESULT cmp bpVMenginejmp,0 je ask_jmp bp $RESULT log "VM Trace start!" run_to_bp: EOB bp_record ESTO //step to bp(vm_execute) bp_record: log ecx jmp run_to_bp odbgver: msg "本脚本须配合 ODbgscript 1.47 或以上的版本" jmp end end: ret
代码:
Script Log Window Address Message 42F35E VM Trace start! 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042DB4B 42F35E ecx: 0042D6A5 42F35E ecx: 0042D95D 42F35E ecx: 0042D95D 42F35E ecx: 0042D95D 42F35E ecx: 0042D95D 42F35E ecx: 0042D95D 42F35E ecx: 0042D95D 42F35E ecx: 0042D95D 42F35E ecx: 0042D95D 42F35E ecx: 0042D95D 42F35E ecx: 0042D95D 42F35E ecx: 0042D95D 42F35E ecx: 0042D95D 42F35E ecx: 0042D95D 42F35E ecx: 0042F3F0 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 0042D6A5 42F35E ecx: 00401254 42F35E ecx: 00401254 42F35E ecx: 0042E4BC ........
根据以往vmp1.2x的经验,0042D6A5为VM_push_CT,0042D95D为VM_pop_CT,增加标签。
4、寻找两个关键vmopcode(VM_JMP,VM_RETN)
重新运行程序,并重新运行脚本,终止后观察脚本日志窗口
代码:
42F35E VM Trace start! ...... 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042DED5 ////////////////此为VM_retn 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> ........ 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042D95D | offset <vmptest1.VM_pop_CT> 42F35E ecx: 0042F3F0 | ////////////////此为VM_jmp 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> 42F35E ecx: 0042D6A5 | offset <vmptest1.VM_push_CT> ......
在VMM_pop_CT n和VMM_push_CT n通常只隔有1条指令,搜索所有的这样代码段
只有2条指令符合这一要求,他们就是VM_JMP和VM_RETN。
它们的区分方法是,分别设断后,跟进去,很明显其中一个有esi的赋值,此为VM_jmp。另一个则在指令执行结尾处有retn xx。
5、追踪比对原始代码
去除所有断点,然后分别在VM_jmp开始处设断,在VM_retn结尾处设断(也就是retn xx)
代码:
0042F3F0 > 8D3455 4A022F33 lea esi, dword ptr [edx*2+332F024A]
代码:
0042E9A0 C2 3C00 retn 3C
二、加最大保护(vmtest-mp.exe)
类似上面方法。
总结:
vmp1.63很强大,key也很强大。