本帖最后由 ps520 于 2011-8-3 21:15 编辑
废话开头:本来发了个娱乐贴,说老毛子的乱序和VP的乱序的事,文中只分析了VMP的乱序部分。
后来在群里聊起两个的乱序框架,发现确实有必要再发一个VP的乱序框架分析,供大家比较。于是有了UpK的两篇帖子。
发LCG的时候进行了一些整理,对文中一些娱乐文字进行了修改,对附件进行了整合,供大家参考。
VP的附件中,我说Vp的例子是作者用Demo No Nag加的,然后大家都懂的是吧……呵呵。
Opinion:只有乱序,其他全部取消。附件有3个,原名是原文件,其他的按后缀对应保护壳。
vp的实例采用了VProtect 2.0.7.0 正版乱序引擎
VMP的实例采用了VMProtect 2.0.8.0正版乱序引擎
向以上两位作者致敬!
——ps520[LCG]
在分析两个附件以后,我小小地总结了一下。
VMP的乱序特色在于,尽管增加了10多kb的体积,但真正执行的代码量其实比较轻松,所以运行效率不错。
执行的时候基本无延迟感;比较有顺序,按照其流程依次执行,几乎没有大跳转绕圈的全套,花指令较多。
VP的特色在于,增加了30kb左右的体积,执行的垃圾代码较多。框架特色是花指令多,在花指令附近基本都会插有原来的命令,
除入口外,其他地方基本对应的是一个花指令对应一行指令。在各个花指令之间,用大跳转连接,并且有多次跳转的迷惑手段。
花指令也有比较悲剧的,在后面文中贴出的代码里你可以找到。
两个的特点不同,评价很难公正地给出,就交由各位自己跟踪时判断了。
我的理解是,乱序是一种高效率的代码保护手段,它的优势在于代码变形,花指令的引入,这样让整个代码的可读性降低,从而迷惑破解者。
而这种高效率是相对于VM而言的,所以其应有一定的强度,作为普通代码的保护手段,而算法等部分还应采用VM重点保护。
乱序,个人觉得LTT的扭曲变形是最接近于这个的要求的,代码变形强度之大让人汗颜,ChinaProtect作为当年的外挂壳名不虚传。
VMProtect分析笔记:0040109D $ /E9 00000000 jmp ddd_vmp.004010A2 ; 原来判断事件的入口,push ebp
004010A2 > \9C pushfd ; 保存现场,下面有几行简单的处理,我的理解,跟原代码无关的都是花啊~~~
004010A3 . 66:0FBAE5 0D bt bp,0xD
004010A8 . F5 cmc
004010A9 . 53 push ebx
004010AA . 876C24 04 xchg dword ptr ss:[esp+0x4],ebp
004010AE . 66:0FA4DD 0A shld bp,bx,0xA
004010B3 . 8D6C24 04 lea ebp,dword ptr ss:[esp+0x4]
004010B7 . 66:0FA3DF bt di,bx
004010BB . 83EC 04 sub esp,0x4
004010BE . 60 pushad
004010BF . 60 pushad
004010C0 . E8 00000000 call ddd_vmp.004010C5 ; 步入看看?
004010C5 [code]0040F276 6A 00 push 0x0
0040F278 89FB mov ebx,edi
0040F27A 66:0FB6D9 movzx bx,cl
0040F27E 60 pushad
0040F27F C74424 1C 00000>mov dword ptr ss:[esp+0x1C],0x0
0040F287 E8 17000000 call ddd_vmp.0040F2A3
0040F28C 8D6424 38 lea esp,dword ptr ss:[esp+0x38]
0040F290 E8 DB20FFFF call ddd_vmp.00401370 ; 执行原来的不成立call
0040F295 2F das
0040F296 E8 2BFFFFFF call ddd_vmp.0040F1C6
0040F29B 0F9DC4 setge ah
0040F29E E8 43FFFFFF call ddd_vmp.0040F1E6
884424 0C mov byte ptr ss:[esp+0xC],al ; 上面不步入到这里。。你懂的,这还算花。
004010C9 . C745 FC 00000>mov dword ptr ss:[ebp-0x4],0x0
004010D0 . FF7424 0C push dword ptr ss:[esp+0xC]
004010D4 . C74424 44 30A>mov dword ptr ss:[esp+0x44],ddd_vmp.0040>; 原始代码压栈,我怎么感觉VMP只复制了代码,没变形?
004010DC . 50 push eax
004010DD . FF75 FC push dword ptr ss:[ebp-0x4]
004010E0 . 8F4424 44 pop dword ptr ss:[esp+0x44]
004010E4 . E9 00000000 jmp ddd_vmp.004010E9 ; 这个也叫花指令,跳转的花指令。。增加美观度
004010E9 > 9C pushfd
004010EA . 8D6424 48 lea esp,dword ptr ss:[esp+0x48]
004010EE . E8 0DFFFFFF call ddd_vmp.00401000 ; 貌似是文本的处理?
004010F3 . 60 pushad
004010F4 . F5 cmc
004010F5 . 60 pushad
004010F6 . 83C4 48 add esp,0x48
004010F9 . F8 clc
004010FA . 83F8 00 cmp eax,0x0
004010FD . E9 00000000 jmp ddd_vmp.00401102
00401102 - 0F85 EFE00000 jnz ddd_vmp.0040F1F7 ; 你们猜这个跳转干嘛的。其实这里就是原来的跳转。
这里有跳转分支,分开讨论。
若不成立:
先0040F1F7 /E9 7A000000 jmp ddd_vmp.0040F276 ; 假设没成立,就到这里了。这只是个跳板,执行变异部分在后面
然后,继续:00401108 . 9C pushfd
00401109 . 60 pushad
0040110A . 60 pushad
0040110B . 66:0FCB bswap bx
0040110E . C74424 40 000>mov dword ptr ss:[esp+0x40],0x0
00401116 . 66:0FBED8 movsx bx,al
0040111A . F7D3 not ebx
0040111C . 66:0FB6D8 movzx bx,al
00401120 . C74424 3C 000>mov dword ptr ss:[esp+0x3C],0x0
00401128 . E8 85E10000 call ddd_vmp.0040F2B2 ; 这是一个神奇的call,除了花指令还有。。。
你们懂的,现在是秒了那个jnz。
00401102 90 nop ; 你们猜这个跳转干嘛的。其实这里就是原来的跳转。
00401103 90 nop
00401104 90 nop
00401105 90 nop
00401106 90 nop
00401107 90 nop
NOP掉。[ DISCUZ_CODE_1 ]步入刚才那call。0040F2B2 66:0FBEDA movsx bx,dl ; 我说这些无聊代码能否无视?
0040F2B6 66:0FB6DB movzx bx,bl
0040F2BA C74424 3C 00000>mov dword ptr ss:[esp+0x3C],0x0
0040F2C2 88CB mov bl,cl
0040F2C4 ^ E9 33FFFFFF jmp ddd_vmp.0040F1FC ; 飞去执行成功代码
跟去看看0040F1F7 /E9 7A000000 jmp ddd_vmp.0040F276 ; 假设没成立,就到这里了
0040F1FC |C6C7 F1 mov bh,0xF1 ; 成功以后是飞到这里。对比一下,上面就是飞去错误的jmp
0040F1FF |C74424 38 01030>mov dword ptr ss:[esp+0x38],0x80000301
0040F207 |5B pop ebx
0040F208 |C74424 30 00000>mov dword ptr ss:[esp+0x30],0x0
0040F210 |66:0FBEDA movsx bx,dl
0040F214 |C74424 2C 00000>mov dword ptr ss:[esp+0x2C],0x0
0040F21C |F6D7 not bh
0040F21E |5B pop ebx
0040F21F |C74424 24 04000>mov dword ptr ss:[esp+0x24],0x80000004
0040F227 |66:0FCB bswap bx
0040F22A |66:0FCB bswap bx
0040F22D |C74424 20 00000>mov dword ptr ss:[esp+0x20],0x0
0040F235 |66:0FBEDA movsx bx,dl
0040F239 |5B pop ebx
0040F23A |5B pop ebx
0040F23B |0FB6DA movzx ebx,dl
0040F23E |C74424 14 34A24>mov dword ptr ss:[esp+0x14],ddd_vmp.0040>
0040F246 |66:0FCB bswap bx
0040F249 |C74424 10 03000>mov dword ptr ss:[esp+0x10],0x3
0040F251 |5B pop ebx
0040F252 |BB 00030000 mov ebx,0x300
0040F257 |52 push edx
0040F258 |8D6424 10 lea esp,dword ptr ss:[esp+0x10]
0040F25C |E8 0F21FFFF call ddd_vmp.00401370 ; 注定又是一个神奇的call
到这里00401370 . 8B4424 10 mov eax,dword ptr ss:[esp+0x10]
F7下去就可以执行到成功的msgbox调用。
附件是这个的例程,包括原程序和VMP乱序保护后的程序。
总结一下:
貌似VMP对单一判断的乱序保护强度不是很高?(自大了。。。。其实是猜的)
乱序貌似只是对原来的代码插入了大量的花和无效执行,而并未对原有执行进行一些变形,比如把jnz变为结果取反后je
貌似为了执行效率起见,乱序并没有将所有垃圾代码跑完,而是用jmp很有技巧地跳转,减少了垃圾代码执行过程,但也降低了乱序的强度。(个人认为吧)
调试VMP乱序的小技巧:
爆破而言,你需要做的其实很简单,F7。一般而言,乱序开始部分的call还有有法跟的,不要漏了,对于jnz\je\ja这些远跳转做好F2断点。
这样就可以进行断点的猜解。比较不错。
这个方法的好处是,只要你进入了关键代码区,一般爆破都是可以成功的。。。。。
VProtect笔记:
调试需要的是心境,不要急躁,要认真地去对待每一个F7,当然也不是死磕,也要用好F2和F4、F8(以下省略N个OD热键)。
以下是OD调试记录,VP的代码增加到了32K,执行时较VMP乱序保护有明显延迟感,那么它是如何构建整个乱序框架的呢?
你可以用VP DEMO只M一条线,比如我这个例子就是。
跟VMP的那个例子是一样的,所以很多细节不会赘述。
原始的入口在0040109D,所以在这里先断点,然后F9跑起来,执行到这里以后,发现同样也是个jmp(群众:不是JMP那该是神马!!!)
F7一下,就到这里了……00410DD0 68 8D8C22F5 push 0xF5228C8D
00410DD5 9C pushfd
00410DD6 814424 04 C37C1>add dword ptr ss:[esp+0x4],0xB1E7CC3
00410DDE 9D popfd
00410DDF C3 retn
童鞋,你觉得这个好看么。。。。好看?那你等着吧,你会碰见很多的……
ctrl+F8,到这里0041094D 696420 68 DCCD0>imul esp,dword ptr ds:[eax+0x68],0xFA04C>
00410955 9C pushfd
00410956 814424 04 446E3>add dword ptr ss:[esp+0x4],0x63C6E44
0041095E 9D popfd
0041095F C3 retn
继续……Ctrl+F8,跑啊跑~00413C20 68 6A0D30AE push 0xAE300D6A
00413C25 9C pushfd
00413C26 814424 04 D63A1>add dword ptr ss:[esp+0x4],0x52113AD6
00413C2E 9D popfd
00413C2F C3 retn
貌似有4个这样的push|pushfd|add dword|popfd|retn
就可以到这里了:0040F800 60 pushad
0040F801 9C pushfd
0040F802 03C3 add eax,ebx
0040F804 9D popfd
0040F805 61 popad
0040F806 ^ E9 65FBFFFF jmp ddd_VP.0040F370
VP的特点在于,貌似是这样的。你会在2块地方被jmp远跳到处乱引,然后在一堆花中执行原代码。那么继续:0040F800 60 pushad
0040F801 9C pushfd
0040F802 03C3 add eax,ebx
0040F804 9D popfd
0040F805 61 popad
0040F806 ^ E9 65FBFFFF jmp ddd_VP.0040F370 ; 这个飞跃只是个意外。
0040F367 ^\E9 94FEFFFF jmp ddd_VP.0040F200
0040F36C 697F 71 3E55E91>imul edi,dword ptr ds:[edi+0x71],0x1AE95>
0040F373 0000 add byte ptr ds:[eax],al
0040F375 004E 43 add byte ptr ds:[esi+0x43],cl
0040F378 386D 3E cmp byte ptr ss:[ebp+0x3E],ch
识别的问题,原来是个 jmp、。0040F390 68 4CC940C4 push 0xC440C94C
0040F395 9C pushfd
0040F396 814424 04 04290>add dword ptr ss:[esp+0x4],0x3C002904
0040F39E 9D popfd
0040F39F C3 retn
0040F250 50 push eax ; 这里貌似执行了点原代码
0040F251 B8 04041818 mov eax,0x18180404
0040F256 58 pop eax
0040F257 ^ E9 34FFFFFF jmp ddd_VP.0040F190
0040F18C EA 21B17F9C 535>jmp far 5B53:9C7FB121
0040F193 9D popfd
0040F194 8BEC mov ebp,esp
0040F191 53 push ebx
0040F192 5B pop ebx
0040F193 9D popfd
0040F194 8BEC mov ebp,esp
0040F196 E9 15010000 jmp ddd_VP.0040F2B0
貌似又在悄悄地原代码了0040F2B6 ^\E9 75FEFFFF jmp ddd_VP.0040F130 飞跃无处不在
0040F130 /E9 CB010000 jmp ddd_VP.0040F300
但你又跳开就是不对了。0040F2FD 6B4D 24 60 imul ecx,dword ptr ss:[ebp+0x24],0x60
0040F301 90 nop
0040F302 61 popad
0040F303 C745 FC 0000000>mov dword ptr ss:[ebp-0x4],0x0
0040F30A ^ E9 21FDFFFF jmp ddd_VP.0040F030
0040F210 9C pushfd
0040F211 53 push ebx
0040F212 5B pop ebx
0040F213 9D popfd
0040F214 68 30A24000 push ddd_VP.0040A230 ; ASCII "123"
0040F219 ^ E9 E2FDFFFF jmp ddd_VP.0040F000
很明显又在偷偷执行原代码0040F000 50 push eax ; ddd_VP.0040114C
0040F001 8D0418 lea eax,dword ptr ds:[eax+ebx]
0040F004 58 pop eax
0040F005 FF75 FC push dword ptr ss:[ebp-0x4]
0040F008 E9 73030000 jmp ddd_VP.0040F380
少贴点代码比较好。
在VMP中我们常见到的是call ,retn,在VP这里,漫天都是jmp啊……0040F380 50 push eax ; ddd_VP.0040114C
0040F381 8D0418 lea eax,dword ptr ds:[eax+ebx]
0040F384 58 pop eax
0040F385 ^ E9 F6FEFFFF jmp ddd_VP.0040F280
0040F280 60 pushad
0040F281 90 nop
0040F282 61 popad
0040F283 E8 781DFFFF call ddd_VP.00401000
半天遇到个call,进去看看。F700401000 . 8B5424 04 mov edx,dword ptr ss:[esp+0x4]
00401004 . 8B4C24 08 mov ecx,dword ptr ss:[esp+0x8]
00401008 . 85D2 test edx,edx
0040100A . 75 0D jnz Xddd_VP.00401019
0040100C . 33C0 xor eax,eax
0040100E . 85C9 test ecx,ecx
00401010 . 74 06 je Xddd_VP.00401018
00401012 . 8039 00 cmp byte ptr ds:[ecx],0x0
00401015 . 74 01 je Xddd_VP.00401018
00401017 . 48 dec eax
00401018 > C3 retn
貌似是黑月的文本处理函数。。。忘鸟……
这里省略一些F7。。。。0040F090 51 push ecx ; ddd_VP.0040A230
0040F091 59 pop ecx
0040F092 0F85 38000000 jnz ddd_VP.0040F0D0 ; 关键跳。。
飞了半天,终于到关键跳了,先让成立,我们去看看其他代码。
初步看了下,貌似是很乱的说。。但跟一会就会有个call,进去就是switch.那是黑月的一个特点。。。
nop掉jnz。继续:0040F2E1 8D0418 lea eax,dword ptr ds:[eax+ebx]
0040F2E4 58 pop eax
0040F2E5 68 01030080 push 0x80000301 ; 悄悄压栈参数。你懂的
0040F2EA ^ E9 B1FFFFFF jmp ddd_VP.0040F2A0
0040F3E2 03C3 add eax,ebx
0040F3E4 9D popfd
0040F3E5 61 popad
0040F3E6 6A 00 push 0x0 ; 又一个参数被悄悄压栈了
0040F3E8 ^ E9 B3FFFFFF jmp ddd_VP.0040F3A0
0040F3B0 51 push ecx ; ddd_VP.0040A230
0040F3B1 59 pop ecx
0040F3B2 68 00000000 push 0x0
0040F3B7 ^ E9 A4FCFFFF jmp ddd_VP.0040F060
0040F060 68 04000080 push 0x80000004
0040F065 E9 46010000 jmp ddd_VP.0040F1B0
参数4也到位了。
然后就是call鸟。。。
本文只是对整体的VP进行了简单的介绍,大家可以看到VP的一些特色。大家都懂的……
结尾说一句:该回复的回复,该加分的加分,七夕节礼物,希望送我个妹子。。
|