吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8623|回复: 6
收起左侧

[转贴] 〇〇木一论坛大牛对vmp的讲解,温故知新,别让精华沉默了

[复制链接]
devilmilk 发表于 2016-7-6 22:39
〇〇木一大牛的帖子:http://www.52pojie.cn/thread-476475-1-1.html
昨天在看Sound大牛发的------[分享] 吾爱破解论坛2016年脱壳破解区精华帖索引贴:吾爱破解论坛2016年脱壳破解区精华帖索引贴
http://www.52pojie.cn/thread-454265-1-1.html
(出处: 吾爱破解论坛)
看到这个文章所以提醒大家温故知新,很多答案就藏在论坛的角落里,等待大家寻宝。
我把我个人觉得比较内容再贴出来,大家再看看,如果要看原文件,请回”〇〇木一大牛的帖子:http://www.52pojie.cn/thread-476475-1-1.html”去下载。
好的帖子不要沉下去,精华都在论坛里。
一.VMProtect 脱壳
1. LocalAlloc Anti-Dump
为了防止程序被转储脱壳,VMP会从多个方面来AntiDump,其中第一个就是申请内存并在内存中放入一些关键数据,在VMP运行过程中会读取这些关键数据进行验证,如果脱壳后没有处理这部分数据,运行过程无法读取到相应的数据,程序将无法运行。如下是记录LocalAlloc Anti-Dump的过程:
……
10B091B:VM_CallApi -->11614CD:LocalAlloc  Retn:5656F0
……
1160786:VM_WriteEs32 -->Addr:5656F0
……
1160786:VM_WriteEs32 -->Addr:5656F4
……
1160786:VM_WriteEs32 -->Addr:5656F8
……
1160786:VM_WriteEs32 -->Addr:5656FC
……
1160786:VM_WriteEs32 -->Addr:565700
……
1160786:VM_WriteEs32 -->Addr:565704
……
1160786:VM_WriteEs32 -->Addr:565708
……
1160786:VM_WriteEs32 -->Addr:56570C
……
1160786:VM_WriteEs32 -->Addr:565710
……
1160786:VM_WriteEs32 -->Addr:565714
……
1160786:VM_WriteEs32 -->Addr:565718
……
1160786:VM_WriteEs32 -->Addr:56571C
……
1160786:VM_WriteEs32 -->Addr:565720
……
1160786:VM_WriteEs32 -->Addr:565724
……
1160786:VM_WriteEs32 -->Addr:565728
……
1160786:VM_WriteEs32 -->Addr:56572C
……
1160786:VM_WriteEs32 -->Addr:565730
……
1160786:VM_WriteEs32 -->Addr:565734
……
1160786:VM_WriteEs32 -->Addr:565738
……
1160786:VM_WriteEs32 -->Addr:56573C
……
1160786:VM_WriteEs32 -->Addr:565740
……
1160786:VM_WriteEs32 -->Addr:565744
……
1160786:VM_WriteEs32 -->Addr:565748
……
1160786:VM_WriteEs32 -->Addr:56574C
……
1160786:VM_WriteEs32 -->Addr:565750
……
1160786:VM_WriteEs32 -->Addr:565754
……
10B986E:VM_Cpuid
……
10B9794:VM_WriteDs32 -->Addr:565748
……
其中省略了一些对数据加密的指令,只记录保留一些关键指令和写数据指令,从中可以看出,在LocalAlloc申请到内存后,VMP会向里面写入0x14*4=0x68大小的加密数据,之后会通过CPUID获取CPU的硬件绑定信息加密后存入anti-dump内存。
所以要处理anti-dump不仅要对加密数据进行保存,还需要Patch VM_CPUID使其返回相同的值,这样才能完整的跨平台处理LocalAlloc Anti-Dump
2. Heap Anti-Dump
VMP在壳运行过程中会调用HeapCreate来创建堆内存,用来储存一些关键数据(尚未清楚),以及提供后续解码资源数据的内存空间,所以该堆内存会在程序运行过程中将被申请使用。VMP对这部分代码并没有VM,但对API进行了隐藏,下面是调用HeapCreate部分:
0101C564    8BFF            mov edi,edi
0101C566    55              push ebp
0101C567    8BEC            mov ebp,esp
0101C569    33C0            xor eax,eax
0101C56B    3945 08         cmp dword ptr ss:[ebp+0x8],eax
0101C56E    6A 00           push 0x0
0101C570    0F94C0          sete al
0101C573    68 00100000     push 0x1000
0101C578    50              push eax
0101C579    E8 4F840376     call kernel32.HeapCreate                   ; 调用HeapCreate创建堆
0101C57E    90              nop
0101C57F    A3 D4750201     mov dword ptr ds:[0x10275D4],eax           ; 堆地址存入全局变量中
0101C584    85C0            test eax,eax
0101C586    75 02           jnz short notepad_.0101C58A
0101C588    5D              pop ebp
0101C589    C3              retn
申请的堆会在程序运行时随时被调用,因此要处理Heap Anti-Dump需要dump heap 内存,并提供足够的空间(因为后续会多次申请,heap内存需要足够)。
3. Resource Anti-Dump
VMP对资源的处理个人觉得是难以完全修复的,能用的方法是沿用VMP对资源的处理。VMP在壳运行阶段会对4个资源相关的API进行HOOK,这4API为:LdrFindResource_ULdrAccessResourceLoadStringALoadStringW。具体的HOOK只是跳转到VMP自身模拟的API中,
7762D2A1 ntdll.LdrFindResource_U   jmp notepad_.010118A0                      ; hook到自身代码
7762D2A6                           test byte ptr ds:[0x7FFE0389],0x1
7762D2AD                           jnz ntdll.77676B88
7762D2B3                           push esi
7762D2B4                           push dword ptr ss:[ebp+0x14]
7762D2B7                           push 0x0
7762D2B9                           push dword ptr ss:[ebp+0x10]
7762D2BC                           push dword ptr ss:[ebp+0xC]
7762D2BF                           push dword ptr ss:[ebp+0x8]
7762D2C2                           call ntdll.7762CA4C
7762D2C7                           test byte ptr ds:[0x7FFE0389],0x1
7762D2CE                           mov esi,eax
7762D2D0                           jnz ntdll.77676B9F
7762D2D6                           mov eax,esi
7762D2D8                           pop esi
7762D2D9                           pop ebp
7762D2DA                           retn 0x10
7762D284 ntdll.LdrAccessResource   jmp notepad_.01011930                      ; hook到自身代码
7762D289                           je short ntdll.7762D2AF
7762D28B                           adc bh,bh
7762D28D                           je short ntdll.7762D2B3
7762D28F                           adc bh,bh
7762D291                           je short ntdll.7762D2B7
并且VMP也会申请内存记录被hook的代码,使执行完自身的代码后又去调用真实API执行。在申请这部分内存时,VMP会从两个API相关模块的地址结束开始每次增加0x1000大小来调用VirtualAlloc申请内存,直到这两块内存被申请。
第一块申请hook内存:
77780000           8BFF             mov edi,edi
77780002           55               push ebp
77780003           8BEC             mov ebp,esp
77780005         - E9 9CD2EAFF      jmp ntdll.7762D2A6                         ; 跳回到LdrFindResource_U
7778000A           8BFF             mov edi,edi
7778000C           55               push ebp
7778000D           8BEC             mov ebp,esp
7778000F           FF7424 10        push dword ptr ss:[esp+0x10]
77780013           FF7424 10        push dword ptr ss:[esp+0x10]
77780017         - E9 70D2EAFF      jmp ntdll.7762D28C                         ; 跳回到LdrAccessResource
7778001C           FF7424 10        push dword ptr ss:[esp+0x10]
77780020           FF7424 10        push dword ptr ss:[esp+0x10]
77780024           0000             add byte ptr ds:[eax],al
77780026           0000             add byte ptr ds:[eax],al
77780028           0000             add byte ptr ds:[eax],al
7778002A           0000             add byte ptr ds:[eax],al
第二块申请hook内存:
76A00000            8BFF             mov edi,edi
76A00002            55               push ebp
76A00003            8BEC             mov ebp,esp
76A00005          - E9 4F4BF4FF      jmp KERNELBA.76944B59                      ; 跳回到LoadStringA
76A0000A            8BFF             mov edi,edi
76A0000C            55               push ebp
76A0000D            8BEC             mov ebp,esp
76A0000F            8BFF             mov edi,edi
76A00011            55               push ebp
76A00012            8BEC             mov ebp,esp
76A00014          - E9 AD4BF4FF      jmp KERNELBA.76944BC6                      ; 跳回到LoadStringW
76A00019            8BFF             mov edi,edi
76A0001B            55               push ebp
76A0001C            8BEC             mov ebp,esp
76A0001E            0000             add byte ptr ds:[eax],al
76A00020            0000             add byte ptr ds:[eax],al
76A00022            0000             add byte ptr ds:[eax],al
处理VMP资源的anti-dump,需要添加自己的代码去hook4api到相关地址,并且为实现跨平台需要自己实现hook的调回。
4. OEP定位
关于VMP OEP的查找网上也有很多的方法,其中最简单的就是在最后一次VirtualProtect后,对代码段下内存访问断点。先来看一下壳运行时VirtualProtect调用情况:
第一次调用:
000CF68C   011614CD  /CALL VirtualProtect
000CF690   01001000  |Address = notepad_.01001000
000CF694   00007748  |Size = 7748 (30536.)
000CF698   00000040  |NewProtect = PAGE_EXECUTE_READWRITE
000CF69C   000CFF60  \pOldProtect = 000CFF60
可以看出首次调用VirtualProtect是先将代码段内存的权限变成可写,这是在为数据的解码做准备,从中也可以看出代码段的基址为01001000,大小为 7748
第二次调用:
000CF61C   011614CD  /CALL VirtualProtect
000CF620   01001000  |Address = notepad_.01001000
000CF624   00007748  |Size = 7748 (30536.)
000CF628   00000020  |NewProtect = PAGE_EXECUTE_READ
000CF62C   000CFF60  \pOldProtect = 000CFF60
此次断下后,可以看出壳又将代码段权限改成了不可写,说明数据已经解码完毕,查看代码段数据也已经不为0。数据解码后,当然很快就要将程序控制权交给原程序代码了,OEP也即将到达,此时在代码段下访问断点,断下后就在OEP或者OEP附近了。在这里其实最好是在VM_Retn出口下断,VMP退出虚拟机后就要将eip转给原程序了,在OEPVM的情况下,如果在VM_Retn出口处进行OEP的修补,工作量会小很多。
5. IATAPI)修复
VMPAPI进行了混淆,它将所有IAT相关的API调用都转变成为pushx;call或者call;xxx的形式,这对修复造成了不小的影响,也对程序的理解造成了困难,下面是某个被混淆的API调用指令流程已经相关esp信息:
OoWoodOne Log
Address    Info                                      Note
      -    Print Chain: 010073AD Unoptimize
010073AD   call 01074896                             Def:[00000000], Ref:[00000000], Sk:[-4], EspDef:[-4], EspRef:[0], EspRRef:[0]
01074896   pushfd                                    Def:[00030000], Ref:[00030000], Sk:[-4], EspDef:[-8], EspRef:[0], EspRRef:[0]
01074897   mov bp,0xE81E                             Def:[00100000], Ref:[00000000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
0107489B   not edi                                   Def:[30000000], Ref:[30000000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
0107489D   not di                                    Def:[10000000], Ref:[10000000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
010748A0   nop                                       Def:[00000000], Ref:[00000000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
010748A1   not di                                    Def:[10000000], Ref:[10000000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
010748A4   bswap di                                  Def:[10000000], Ref:[10000000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
010748A7   movsx di,al                               Def:[10000000], Ref:[00000001], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
010748AB   xchg edi,ebp                              Def:[30000000], Ref:[00300000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
010748AD   mov ebp,dword ptr ss:[esp+0x4]            Def:[00300000], Ref:[00030000], Sk:[0], EspDef:[0], EspRef:[-4], EspRRef:[0]
010748B1   movzx di,cl                               Def:[10000000], Ref:[00000010], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
010748B5   xchg dword ptr ss:[esp+0x8],ebp           Def:[00000000], Ref:[00330000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
010748B9   push dword ptr ss:[esp+0x4]               Def:[00030000], Ref:[00030000], Sk:[-4], EspDef:[-12], EspRef:[-4], EspRRef:[0]
010748BD   xchg dword ptr ss:[esp+0x8],ebp           Def:[00000000], Ref:[00330000], Sk:[0], EspDef:[-4], EspRef:[0], EspRRef:[0]
010748C1   bswap ebp                                 Def:[00300000], Ref:[00300000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
010748C3   xchg bp,di                                Def:[00100000], Ref:[10000000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
010748C6   movzx bp,al                               Def:[00100000], Ref:[00000001], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
010748CA   pushad                                    Def:[00000000], Ref:[33337777], Sk:[-32], EspDef:[-44], EspRef:[0], EspRRef:[0]
010748CB   mov ebp,0x1005480                         Def:[00300000], Ref:[00000000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
010748D0   call 01016AB0                             Def:[00000000], Ref:[00000000], Sk:[-4], EspDef:[-48], EspRef:[0], EspRRef:[0]
01016AB0   mov ebp,dword ptr ss:[ebp+0x37FBE]        Def:[00300000], Ref:[00300000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
01016AB6   pushfd                                    Def:[00030000], Ref:[00030000], Sk:[-4], EspDef:[-52], EspRef:[0], EspRRef:[0]
01016AB7   lea ebp,dword ptr ss:[ebp+0x19534205]     Def:[00300000], Ref:[00300000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
01016ABD   movsx di,al                               Def:[10000000], Ref:[00000001], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
01016AC1   bswap di                                  Def:[10000000], Ref:[10000000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
01016AC4   mov edi,ebp                               Def:[30000000], Ref:[00300000], Sk:[0], EspDef:[0], EspRef:[0], EspRRef:[0]
01016AC6   push dword ptr ss:[esp+0x4]               Def:[00030000], Ref:[00030000], Sk:[-4], EspDef:[-56], EspRef:[-48], EspRRef:[0]
01016ACA   mov ebp,dword ptr ss:[esp+0x34]           Def:[00300000], Ref:[00030000], Sk:[0], EspDef:[0], EspRef:[-4], EspRRef:[0]
01016ACE   pushfd                                    Def:[00030000], Ref:[00030000], Sk:[-4], EspDef:[-60], EspRef:[0], EspRRef:[0]
01016ACF   push 0x600A1D36                           Def:[00030000], Ref:[00030000], Sk:[-4], EspDef:[-64], EspRef:[0], EspRRef:[0]
01016AD4   push 0x4E94F530                           Def:[00030000], Ref:[00030000], Sk:[-4], EspDef:[-68], EspRef:[0], EspRRef:[0]
01016AD9   push dword ptr ss:[esp+0x44]              Def:[00030000], Ref:[00030000], Sk:[-4], EspDef:[-72], EspRef:[0], EspRRef:[0]
01016ADD   retn 0x48                                 Def:[00030000], Ref:[00030000], Sk:[76], EspDef:[0], EspRef:[0], EspRRef:[0]
           ESP Pos                                   4
从中获取最主要的解密指令:
mov ebp,0x1005480
mov ebp,dword ptr ss:[ebp+0x37FBE]
lea ebp,dword ptr ss:[ebp+0x19534205]
mov edi,ebp
观察几个API的混淆可以发现,所有的API就是参照如下规则解密的:
Mov reg,const0
Mov reg,[reg+const1]
Lea reg,[reg+const2]
Mov reg0,reg//如果有这条指令说明是mov reg,[apiaddr]类型
所以要获取API地址只要获取const0const1const23个常数就行了。另外光知道API地址还不行,还需要知道它的调用类型。这里需要观察指令链最后的ESP位置,以及最后入栈的指令所引用的esp地址。
在上述指令链中:
01016AD9   push dword ptr ss:[esp+0x44]              Def:[00030000], Ref:[00030000], Sk:[-4], EspDef:[-72], EspRef:[0], EspRRef:[0]
最后入栈的指令所引用的esp地址为0,最后Esp pos4。经过观察分析,调用类型一般可分为以下几类:
EspPos        EspRef        HaveReg1         type
4                -4                                        x;jmp[]
0                -8                                        jmp[];x
0                -4                yes                        mov[];x
0                -4                no                        x;call[]
-4                -8                yes                        pop;mov[]
-4                -8                no                        call[];x
4                0                                        push;mov[]
按照这样大部分的api可以完全修复,最后不能修复的几个是与资源相关的api,只要资源anti-dump修复正确,这几个api可以不去管。
6. 脱壳总结
VMProtect保护的脱壳修复的繁琐是免不了的,对于anti-dump可以采用补区段的方法,为了dump区段的位置紧凑,可以在系统断点时就在模块后面申请相关内存区段,然后再脱壳是patch一些anti-dump位置到该区段。
Api的修复可能不仅仅只对代码段,在壳区段中可是需要修复一些api的,这暂时还没找到合理的解决方法。

免费评分

参与人数 3热心值 +3 收起 理由
Sound + 1 鼓励转贴优秀软件安全工具和文档!
材鸟 + 1 楼主研究这么透彻了,不如来个实战教程
FraMeQ + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

一曦过往 发表于 2016-7-7 18:28
学习啦,支持一下,感谢分享
bujieyuan 发表于 2016-7-8 20:07
yyjjww67 发表于 2016-8-19 17:25
evilived 发表于 2016-11-13 14:49
楼主辛苦,学习了
Anonyomus 发表于 2016-11-13 16:22
net壳又该怎么办呢
z236293824 发表于 2016-11-14 07:08 来自手机
学习到了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-17 10:38

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表