【文章标题】: DLB原理实践――(1)PECompact 1.84 aPlib (2)Ezip 1.0 (3)Pepack 1.0 (4)JDPack 1.0 主程序
【测试软件】:对Notepad进行保护
【作者声明】: 初学脱壳,没什么好东西。个人的一些经验,希望与大家分享,错误之处,欢迎指正!
【简要说明】:这篇文章着重于应用于DLB原理的思路。本文对4个弱弱的压缩壳进行简要的分析,这些都是应用DLB原理分析的结果。在这里对壳进行大致的分析,如果对我上一篇文章ASPack 2.12分析熟悉的话,就会明白这些壳的执行流程都是大同小异,没必要分析过多。
――――――――――――――――――――――――――――――――――――――――――――――――――――――――
【本文结构】:
1、PECompact 1.84 aPlib
2、Ezip的分析与总结
3、Pepack 1.0 简要分析
4、JDPack_1.0_主程序脱壳流程
5、总结DLB原理的应用经验
====================================================================================================
【实例一】PECompact 1.84 aPlib
1、OD载入,bp GetModuleHandle, 两次F9,取消断点,ALT+F9返回到用户空间
2、当前的位置在“双重循环体”的内部,往下可以找到内循环的尾部,如下
0040D63C FF95 3D974000 call [ebp+40973D] ; CALL kernel32.VirtualAlloc
0040D642 5A pop edx
0040D643 59 pop ecx
0040D644 0BC0 or eax, eax
0040D646 74 08 je short 0040D650
0040D648 AB stos dword ptr es:[edi]
0040D649 ^ E9 76FFFFFF jmp 0040D5C4 ; 内循环尾部
0040D64E F8 clc ; 到这里表示当前DLL所有函数到已经处理完
0040D64F C3 retn ; 可以直接F4到这里([ESP] = 40D570)
0040D650 F9 stc
0040D651 C3 retn
3、在40D64F处F7或者F8,到这里:
0040D56B E8 0F000000 call 0040D57F ; 函数调用,处理当前DLL中的函数(外循环起点)
0040D570 72 0B jb short 0040D57D
0040D572 83C6 14 add esi, 14 ; 下一IT项
0040D575 837E 0C 00 cmp dword ptr [esi+C], 0; 检测是否所有的DLL都已经处理完
0040D579 ^ 75 F0 jnz short 0040D56B ; 如果没处理完,跳(外循环尾)
0040D57B F8 clc ; 到这里,就表示所有的DLL都已经被处理
0040D57C C3 retn ; 可以直接F4到这里( [ESP] = 40D4B6 )
4、在40D57C 处 F7或者F8,到这里:
0040D4B6 /73 6B jnb short 0040D523 ; 始终跳转
0040D523 80BD 6B9F4000 C>cmp byte ptr [ebp+409F6B], 0C3 ;到这里
0040D52A 74 22 je short 0040D54E ;始终跳转
0040D52C 8D95 6BA14000 lea edx, [ebp+40A16B]
0040D532 6A 40 push 40
0040D534 52 push edx
0040D535 FFB5 3D974000 push dword ptr [ebp+40973D]
0040D53B FFB5 39974000 push dword ptr [ebp+409739]
0040D541 E8 F40A0000 call 0040E03A
0040D546 85C0 test eax, eax
0040D548 ^ 0F85 9DFDFFFF jnz 0040D2EB
0040D54E 61 popad ;到这里
0040D54F 9D popfd
0040D550 50 push eax
0040D551 68 CC104000 push 004010CC ; OEP = 004010CC
0040D556 C2 0400 retn 4
5、DUMP + FIX ==> 搞定
6、 总结:
(1)双重循环体的作用是外壳用来填充IAT的。与ASPack 2.12相比,这里的双重循环并不是那么标准了,经过了一定的变化。第一次看到步骤2中的代码时,还以为到外循环尾部,后来仔细分析才发现是内循环尾部。搞清楚这一点,接下来的步骤就简单了,跟一下就可以OEP了。
(2)PECompact 1.84的最大问题就是把OEP的偏移值放在壳入口处,事实上根本就不需要调试也可以完成脱壳:
直接运行软件 ==> LordPE dump整个进程 ==> 把dump文件的OEP改为10CC ==> ImportREC修复 ==> 搞定
(注:当然OEP不一定就10CC,保险起见,用OD载入看一下就可以了)
====================================================================================================
【实例二】Ezip的分析与总结:
1、OD载入,入口出口如下:
0040D0BE > $ /E9 19320000 jmp 004102DC
2、跳转到这里
004102DC /> \55 push ebp
004102DD |. 8BEC mov ebp, esp
004102DF |. 81EC 28040000 sub esp, 428
004102E5 |. 53 push ebx
004102E6 |. 56 push esi
004102E7 |. 57 push edi
004102E8 |. 8D85 94FCFFFF lea eax, [ebp-36C]
004102EE |. 50 push eax
004102EF |. E8 FCCDFFFF call 0040D0F0
004102F4 |. 59 pop ecx
……省略一大段代码(这部分为区段解压代码)
0041064D |. 83C4 0C add esp, 0C
00410650 |. 8D85 94FCFFFF lea eax, [ebp-36C]
00410656 |. 50 push eax
00410657 |. FFB5 2CFCFFFF push dword ptr [ebp-3D4]
0041065D |. E8 F3C9FFFF call 0040D055 ; 调用填充IAT的代码(子程序中为双重循环体)
00410662 |. 59 pop ecx
00410663 |. 59 pop ecx
00410664 |. 8B85 2CFCFFFF mov eax, [ebp-3D4]
0041066A |. 8B40 10 mov eax, [eax+10]
0041066D |. 8B8D 2CFCFFFF mov ecx, [ebp-3D4]
00410673 |. 0341 1C add eax, [ecx+1C]
00410676 |. 8985 38FCFFFF mov [ebp-3C8], eax
0041067C |. 8B85 38FCFFFF mov eax, [ebp-3C8]
00410682 |. 5F pop edi
00410683 |. 5E pop esi
00410684 |. 5B pop ebx
00410685 |. 8BE5 mov esp, ebp
00410687 |. 5D pop ebp
00410688 |. FFE0 jmp eax ; OEP
0041068A |> 5F pop edi
0041068B |. 5E pop esi
0041068C |. 5B pop ebx
0041068D |. C9 leave
0041068E \. C3 retn
双重循环体代码如下(上面41065D本质就是调用这段代码):
0040FDEA /> \55 push ebp
0040FDEB |. 8BEC mov ebp, esp
0040FDED |. 51 push ecx
0040FDEE |. 51 push ecx
0040FDEF |. 8B45 08 mov eax, [ebp+8]
0040FDF2 |. 53 push ebx
0040FDF3 |. 8B58 68 mov ebx, [eax+68]
0040FDF6 |. 0358 1C add ebx, [eax+1C]
0040FDF9 |. 8B4B 0C mov ecx, [ebx+C]
0040FDFC |. 85C9 test ecx, ecx
0040FDFE |. 0F84 B9000000 je 0040FEBD
0040FE04 |. 56 push esi
0040FE05 |. 57 push edi
0040FE06 |. 8B7D 0C mov edi, [ebp+C]
0040FE09 |. EB 03 jmp short 0040FE0E
0040FE0B |> 8B45 08 /mov eax, [ebp+8] ; 外循环起点
0040FE0E |> 8B40 1C mov eax, [eax+1C]
0040FE11 |. 8D1401 |lea edx, [ecx+eax]
0040FE14 |. 8B0B |mov ecx, [ebx]
0040FE16 |. 8955 F8 |mov [ebp-8], edx
0040FE19 |. 8D3401 |lea esi, [ecx+eax]
0040FE1C |. 8975 0C |mov [ebp+C], esi
0040FE1F |. 8B73 10 |mov esi, [ebx+10]
0040FE22 |. 03F0 |add esi, eax
0040FE24 |. 85C9 |test ecx, ecx
0040FE26 |. 75 03 |jnz short 0040FE2B
0040FE28 |. 8975 0C |mov [ebp+C], esi
0040FE2B |> 52 |push edx
0040FE2C |. FF57 0C |call [edi+C]
0040FE2F |. 85C0 |test eax, eax ; ==> 3次GetModuleHandleA中断后,返回到这里
0040FE31 |. 8945 FC |mov [ebp-4], eax
0040FE34 |. 75 12 |jnz short 0040FE48
0040FE36 |. 50 |push eax
0040FE37 |. 68 C8494100 |push 004149C8 ; ASCII "Can't load module"
0040FE3C |. FF75 F8 |push dword ptr [ebp-8]
0040FE3F |. 50 |push eax
0040FE40 |. FF57 70 |call [edi+70]
0040FE43 |. 6A 00 |push 0
0040FE45 |. FF57 3C |call [edi+3C]
0040FE48 |> 8B45 0C |/mov eax, [ebp+C] ; 内循环起点
0040FE4B |. 8B00 ||mov eax, [eax]
0040FE4D |. 85C0 ||test eax, eax
0040FE4F |. 74 5C ||je short 0040FEAD
0040FE51 |. A9 00000080 ||test eax, 80000000
0040FE56 |. 74 1F ||je short 0040FE77
0040FE58 |. 25 FFFF0000 ||and eax, 0FFFF
0040FE5D |. 50 ||push eax
0040FE5E |. FF75 FC ||push dword ptr [ebp-4]
0040FE61 |. FF57 10 ||call [edi+10]
0040FE64 |. 85C0 ||test eax, eax
0040FE66 |. 8906 ||mov [esi], eax
0040FE68 |. 75 3A ||jnz short 0040FEA4
0040FE6A |. 50 ||push eax
0040FE6B |. 68 B0494100 ||push 004149B0 ; ASCII "Can't find function"
0040FE70 |. 68 9C494100 ||push 0041499C ; ASCII "import by enum"
0040FE75 |. EB 23 ||jmp short 0040FE9A
0040FE77 |> 8B4D 08 ||mov ecx, [ebp+8]
0040FE7A |. 8B49 1C ||mov ecx, [ecx+1C]
0040FE7D |. 8D4401 02 ||lea eax, [ecx+eax+2]
0040FE81 |. 50 ||push eax
0040FE82 |. 8945 F8 ||mov [ebp-8], eax
0040FE85 |. FF75 FC ||push dword ptr [ebp-4]
0040FE88 |. FF57 10 ||call [edi+10] ; kernel32.GetProcAddress
0040FE8B |. 85C0 ||test eax, eax
0040FE8D |. 8906 ||mov [esi], eax
0040FE8F |. 75 13 ||jnz short 0040FEA4
0040FE91 |. 50 ||push eax
0040FE92 |. 68 B0494100 ||push 004149B0 ; ASCII "Can't find function"
0040FE97 |. FF75 F8 ||push dword ptr [ebp-8]
0040FE9A |> 6A 00 ||push 0
0040FE9C |. FF57 70 ||call [edi+70]
0040FE9F |. 6A 00 ||push 0
0040FEA1 |. FF57 3C ||call [edi+3C]
0040FEA4 |> 8345 0C 04 ||add dword ptr [ebp+C], 4
0040FEA8 |. 83C6 04 ||add esi, 4
0040FEAB |.^ EB 9B |\jmp short 0040FE48 ; 内循环尾部
0040FEAD |> 8B4B 20 |mov ecx, [ebx+20]
0040FEB0 |. 83C3 14 |add ebx, 14
0040FEB3 |. 85C9 |test ecx, ecx
0040FEB5 |.^ 0F85 50FFFFFF \jnz 0040FE0B ; 外循环尾部
0040FEBB |. 5F pop edi
0040FEBC |. 5E pop esi
0040FEBD |> 5B pop ebx
0040FEBE |. C9 leave
0040FEBF \. C3 retn
3、总结
(1)结构太过清晰,除了壳入口点的一个JMP外,接下来是一个比较长的代码。外壳的主要功能都在这里直接或者间接实现,连最后的跳转到OEP也是在这段代码的最后。所以只需要直接翻屏,只需要在这段代码的尾部找到“jmp eax”就可以了,从这方面来说与UPX是同等难度的。
(2)可以通过“bp GetModuleHandleA”来达到关键地方: 第三次中断,ALT+F9,返回到的是外壳填充IAT模块(在本例中也就是0041065D处调用的子程序中)
(3)解压区段的代码也是在这个主代码段中(本例中就是中间省略的那部分代码,贴没什么意思,跟一下也很简单)。
大致分析了下,在解压区段时要用到的被压缩过原始数据,而在这个壳中,并不是直接通过内存来读取这些数据,而是通过共享的方式打开这个文件,运用了CreteFile,SetFilePointer,CloseHandle等文件读写函数。但原理上还是不变。
====================================================================================================
【实例三】Pepack 1.0 分析
OD载入,直接bp + GetModuleHandleA ==> F9 ==> ALT+F9返回用户空间,如下:
0040D1B4 8B42 0C mov eax, [edx+C] ; 外循环起点
0040D1B7 0BC0 or eax, eax
0040D1B9 0F84 A0000000 je 0040D25F ; 如果处理完所有的DLL,跳出双重循环体
0040D1BF 894A 0C mov [edx+C], ecx
0040D1C2 0385 47050000 add eax, [ebp+547]
0040D1C8 52 push edx
0040D1C9 51 push ecx
0040D1CA 50 push eax
0040D1CB 50 push eax
0040D1CC 8985 53050000 mov [ebp+553], eax
0040D1D2 8BD8 mov ebx, eax
0040D1D4 E8 4F030000 call 0040D528 ; GetModuleHandleA
0040D1D9 5B pop ebx ;返回到这里
0040D1DA 59 pop ecx
0040D1DB 5A pop edx
0040D1DC 0BC0 or eax, eax
0040D1DE 75 12 jnz short 0040D1F2
0040D1E0 52 push edx
0040D1E1 51 push ecx
0040D1E2 53 push ebx
0040D1E3 E8 46030000 call 0040D52E
0040D1E8 0BC0 or eax, eax
0040D1EA 0F84 82000000 je 0040D272
0040D1F0 59 pop ecx
0040D1F1 5A pop edx
0040D1F2 8985 39020000 mov [ebp+239], eax
0040D1F8 8B32 mov esi, [edx]
0040D1FA 890A mov [edx], ecx
0040D1FC 8B7A 10 mov edi, [edx+10]
0040D1FF 894A 10 mov [edx+10], ecx
0040D202 0BF6 or esi, esi
0040D204 75 02 jnz short 0040D208
0040D206 8BF7 mov esi, edi
0040D208 03B5 47050000 add esi, [ebp+547]
0040D20E 03BD 47050000 add edi, [ebp+547]
0040D214 8B06 mov eax, [esi] ; 内循环起点
0040D216 0BC0 or eax, eax
0040D218 74 3D je short 0040D257
0040D21A 890E mov [esi], ecx
0040D21C 79 05 jns short 0040D223
0040D21E 0FB7C0 movzx eax, ax
0040D221 EB 0D jmp short 0040D230
0040D223 0385 47050000 add eax, [ebp+547]
0040D229 66:C700 0000 mov word ptr [eax], 0
0040D22E 40 inc eax
0040D22F 40 inc eax
0040D230 50 push eax
0040D231 52 push edx
0040D232 56 push esi
0040D233 57 push edi
0040D234 51 push ecx
0040D235 53 push ebx
0040D236 50 push eax
0040D237 50 push eax
0040D238 68 0000DA77 push 77DA0000
0040D23D E8 F2020000 call 0040D534
0040D242 5B pop ebx
0040D243 0BC0 or eax, eax
0040D245 74 4D je short 0040D294
0040D247 5B pop ebx
0040D248 59 pop ecx
0040D249 5F pop edi
0040D24A 8907 mov [edi], eax
0040D24C 5E pop esi
0040D24D 5A pop edx
0040D24E 5B pop ebx
0040D24F 83C6 04 add esi, 4
0040D252 83C7 04 add edi, 4
0040D255 ^ EB BD jmp short 0040D214 ; 内循环尾部
0040D257 83C2 14 add edx, 14 ; IT下一项
0040D25A ^ E9 55FFFFFF jmp 0040D1B4 ; 外循环尾部
0040D25F 8B85 57050000 mov eax, [ebp+557]
0040D265 0385 8B050000 add eax, [ebp+58B]
0040D26B 894424 1C mov [esp+1C], eax
0040D26F 61 popad
0040D270 - FFE0 jmp eax ; OEP = eax = 4010cc
DUMP ==> 手工确定IAT ( RVA = 62E4, SIZE = 220 ),FIX ==> 正常运行
====================================================================================================
【实例四】JDPack_1.0_主程序脱壳流程
(1)OD载入,bp + GetModuleHandleA,3次F9,ALT+F9,返回到用户空间(双重循环体)
0040E284 8B95 41344000 mov edx, [ebp+403441] ; edx 指向基地址
0040E28A 8BB5 D9314 000 mov esi, [ebp+4031D9] ; esi 为 IT 的偏移值
0040E290 03F2 add esi, edx ; esi += edx;指向IT
0040E292 8B46 0C mov eax, [esi+C] ; 外循环起点
0040E295 0BC0 or eax, eax
0040E297 0F84 4D010000 je 0040E3EA
0040E29D 03C2 add eax, edx
0040E29F 8985 A5314000 mov [ebp+4031A5], eax
0040E2A5 8BD8 mov ebx, eax
0040E2A7 50 push eax
0040E2A8 FF95 C0334000 call [ebp+4033C0] ; GetModuleHandleA
0040E2AE 0BC0 or eax, eax ;返回到这里
0040E2B0 75 55 jnz short 0040E307 ; 测试取模块是否成功,若成功则跳转
0040E2B2 53 push ebx
0040E2B3 FF95 C4334000 call [ebp+4033C4]
0040E2B9 0BC0 or eax, eax
0040E2BB 75 4A jnz short 0040E307
0040E2BD FFB5 A5314000 push dword ptr [ebp+4031A5]
0040E2C3 8D9D E1324000 lea ebx, [ebp+4032E1]
0040E2C9 53 push ebx
0040E2CA 8D9D DD344000 lea ebx, [ebp+4034DD]
0040E2D0 53 push ebx
0040E2D1 FF95 C1314000 call [ebp+4031C1]
0040E2D7 83C4 0C add esp, 0C
0040E2DA 6A 30 push 30
0040E2DC 8D9D 83324000 lea ebx, [ebp+403283]
0040E2E2 53 push ebx
0040E2E3 8D9D DD344000 lea ebx, [ebp+4034DD]
0040E2E9 53 push ebx
0040E2EA 6A 00 push 0
0040E2EC FF95 BD314000 call [ebp+4031BD]
0040E2F2 83BD C5314000 01 cmp dword ptr [ebp+4031C5], 1
0040E2F9 74 08 je short 0040E303
0040E2FB 6A 00 push 0
0040E2FD FF95 AD314000 call [ebp+4031AD]
0040E303 61 popad
0040E304 33C0 xor eax, eax
0040E306 C3 retn
0040E307 8985 E1314000 mov [ebp+4031E1], eax
0040E30D C785 E5314000 00000>mov dword ptr [ebp+4031E5], 0 ; offset = 0
0040E317 8B95 41344000 mov edx, [ebp+403441] ; 内循环起点
0040E31D 8B06 mov eax, [esi]
0040E31F 0BC0 or eax, eax
0040E321 75 03 jnz short 0040E326
0040E323 8B46 10 mov eax, [esi+10]
0040E326 03C2 add eax, edx
0040E328 0385 E5314000 add eax, [ebp+4031E5]
0040E32E 8B18 mov ebx, [eax]
0040E330 8B7E 10 mov edi, [esi+10]
0040E333 03FA add edi, edx
0040E335 03BD E5314000 add edi, [ebp+4031E5]
0040E33B 85DB test ebx, ebx
0040E33D 0F84 99000000 je 0040E3DC
0040E343 F7C3 00000080 test ebx, 80000000
0040E349 75 07 jnz short 0040E352
0040E34B 03DA add ebx, edx
0040E34D 83C3 02 add ebx, 2
0040E350 EB 15 jmp short 0040E367
0040E352 81E3 FFFFFF7F and ebx, 7FFFFFFF
0040E358 53 push ebx
0040E359 FFB5 E1314000 push dword ptr [ebp+4031E1]
0040E35F FF95 BC334000 call [ebp+4033BC]
0040E365 EB 18 jmp short 0040E37F
0040E367 53 push ebx
0040E368 FFB5 E1314000 push dword ptr [ebp+4031E1]
0040E36E FF95 BC334000 call [ebp+4033BC] ; GetProcAddress
0040E374 EB 04 jmp short 0040E37A
0040E376 C603 00 mov byte ptr [ebx], 0 ; 把字符串逐字节擦除
0040E379 43 inc ebx ; 所以当壳把控制权交给原程序之后
0040E37A 803B 00 cmp byte ptr [ebx], 0 ; 输入表中不会出现函数名
0040E37D ^ 75 F7 jnz short 0040E376
0040E37F 0BC0 or eax, eax
0040E381 75 4B jnz short 0040E3CE
0040E383 FFB5 A5314000 push dword ptr [ebp+4031A5]
0040E389 53 push ebx
0040E38A 8D9D 90324000 lea ebx, [ebp+403290]
0040E390 53 push ebx
0040E391 8D9D DD344000 lea ebx, [ebp+4034DD]
0040E397 53 push ebx
0040E398 FF95 C1314000 call [ebp+4031C1]
0040E39E 83C4 10 add esp, 10
0040E3A1 6A 30 push 30
0040E3A3 8D9D 83324000 lea ebx, [ebp+403283]
0040E3A9 53 push ebx
0040E3AA 8D9D DD344000 lea ebx, [ebp+4034DD]
0040E3B0 53 push ebx
0040E3B1 6A 00 push 0
0040E3B3 FF95 BD314000 call [ebp+4031BD]
0040E3B9 83BD C5314000 01 cmp dword ptr [ebp+4031C5], 1
0040E3C0 74 08 je short 0040E3CA
0040E3C2 6A 00 push 0
0040E3C4 FF95 AD314000 call [ebp+4031AD]
0040E3CA 61 popad
0040E3CB 33C0 xor eax, eax
0040E3CD C3 retn
0040E3CE 8907 mov [edi], eax
0040E3D0 8385 E5314000 04 add dword ptr [ebp+4031E5], 4
0040E3D7 ^ E9 3BFFFFFF jmp 0040E317 ; 内循环尾部
0040E3DC 83C6 14 add esi, 14
0040E3DF 8B95 41344000 mov edx, [ebp+403441]
0040E3E5 ^ E9 A8FEFFFF jmp 0040E292 ; 外循环尾部
0040E3EA 8B95 41344000 mov edx, [ebp+403441]
0040E3F0 8B85 D5314000 mov eax, [ebp+4031D5]
0040E3F6 03C2 add eax, edx
0040E3F8 894424 1C mov [esp+1C], eax
0040E3FC 61 popad
0040E3FD 50 push eax ; OEP = eax = 4035c9
0040E3FE C3 retn
(2)DUMP + FIX ==> 正常运行
====================================================================================================
【5】总结DLB原理的应用经验
1、当返回到用户空间后,必定处于DLB特征代码中
2、首先确定内层循环,然后确定外层循环(DLB内的代码大都差不多,顶多也只是一些简单的变形。)
3、确定DLB后,OEP就在外循环尾部的不远处(如果只想脱壳的话,就可以直接执行到DLB外)
简要分析这些弱壳,一般都不会超过10分钟。对于想我这样的初级选手来说,还是勉强可以接受的。
刚开始,看到别人动不动就直接bp GetModuleHandleA,返回到用户空间又要怎么跳怎么不跳。后来又梦游般的到了OEP
这样根本就学不到任何东西。
就我自己的感觉,分析的时候,执行到了什么地方,在脑子里有明确的方向感。我脑子就是因为有了DLB这个地图,让我在分析这些的时候不会迷路。希望和我一样的菜鸟朋友也能找到自己的方向
说明:在看上面的4个例子的时候,需要注意的是几个地方:外循环起点、尾部,内循环起点、尾部,返回点这5个地方。
我已经用颜色标记了。对于DLB内部的详细流程,感兴趣的话可以参考我的上一篇对ASPack 2.12的分析
――――――――――――――――――――――――――――――――――――――――――――――――――――――――
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!