〇〇木一大牛的帖子: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,这4个API为:LdrFindResource_U、LdrAccessResource、LoadStringA、LoadStringW。具体的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,需要添加自己的代码去hook这4个api到相关地址,并且为实现跨平台需要自己实现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转给原程序了,在OEP被VM的情况下,如果在VM_Retn出口处进行OEP的修补,工作量会小很多。 5. IAT(API)修复 VMP对API进行了混淆,它将所有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地址只要获取const0、const1、const2这3个常数就行了。另外光知道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 pos为4。经过观察分析,调用类型一般可分为以下几类: 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的,这暂时还没找到合理的解决方法。
|