练习了MoleBox(2.0.0-2.3.0)的脱壳, 记录一下脱壳流程点.
找到OEP后, 去看IAT项, 系统DLL地址被换成了壳函数的地址(API redirection).
如果不使IAT项为系统API地址, 运行时闪退.
Detect It Easy 1.01 => MoleBox(2.0.0-2.3.0)[-]
---------------------------
Compressed code?
---------------------------
Quick statistical test of module 'API_Redi' reports that its code section is either compressed, encrypted, or contains large amount of embedded data. Results of code analysis can be very unreliable or simply wrong. Do you want to continue analysis?
---------------------------
是(Y) 否(N)
---------------------------
选No, 不分析代码.
0044CB58 > 60 pushad ; EP
0044CB59 E8 4F000000 call 0044CBAD ; ESP定律, 对ESP对应的内存地址下硬件访问断点(DWORD)
0044C731 58 pop eax ; F9后, ESP定律第一次被触发时, 到达这里
0044C732 58 pop eax
0044C733 FFD0 call eax ; F7去OEP
004331B8 55 push ebp ; OEP
004331B9 8BEC mov ebp, esp
004331BB 6A FF push -1
004331BD 68 00A34300 push 0043A300
004331C2 68 1C334300 push 0043331C ; jmp to msvcrt._except_handler3
从OEP往下翻, 随便找一个系统API.
004332BC FF15 84814300 call dword ptr [438184] ; kernel32.GetStartupInfoA
去438184去看IAT表项是否正常, 如果被API地址重定向, 表项值就不是系统API原始地址, 而是壳地址.
全部翻看一遍, 看看有没有IAT表项被API重定向
00438184 7C801EF2 kernel32.GetStartupInfoA
00438188 7C835EA7 kernel32.MoveFileA
0043818C 7C813841 kernel32.GetFileAttributesExA
00438190 7C80E87C kernel32.FileTimeToSystemTime
00438194 7C80A164 kernel32.WideCharToMultiByte
00438198 00459254 API_Redi.00459254 // 发现了API重定向
0043819C 7C80E8F6 kernel32.FileTimeToLocalFileTime
在Memory中, 看API实现在壳里.
Memory map
Address Size Owner Section Contains Type Access Initial Mapped as
00400000 00001000 API_Redi PE header Imag R RWE
00401000 00037000 API_Redi 0 code Imag R RWE
00438000 0000A000 API_Redi 1 data Imag R RWE
00442000 00005000 API_Redi 2 Imag R RWE
00447000 00004000 API_Redi 3 resources Imag R RWE
0044B000 00011000 API_Redi 4 SFX Imag R RWE // API重定向的实现在壳里
0045C000 00001000 API_Redi 5 imports Imag R RWE
0045D000 00008000 API_Redi 6 Imag R RWE
00470000 00003000 Map R E R E
00530000 00002000 Map R E R E
目的:找出壳中API重定向的代码实现.
00438198 00459254 API_Redi.00459254
挑一个被API重定向的表项地址, 下硬件写断点(DWORD).
重新跑程序.
转到Memory窗口看00438198啥时候写入系统API地址, 按下F9观察.
00451B35 66:8901 mov word ptr [ecx], ax ; 再写00438198
00451B38 8B45 F8 mov eax, dword ptr [ebp-8] ; API_Redi.00438198
可以取消此处的硬件写断点.
在写00438198, F8跟出去, 看函数调用点
00451BAA /EB 07 jmp short 00451BB3 ; do-while
00451BAC |8B45 F8 mov eax, dword ptr [ebp-8] ; do
00451BAF |40 inc eax
00451BB0 |8945 F8 mov dword ptr [ebp-8], eax
00451BB3 \8B45 F8 mov eax, dword ptr [ebp-8]
00451BB6 3B45 0C cmp eax, dword ptr [ebp+C] ; 下硬件执行断点
00451BB9 7D 20 jge short 00451BDB ; 判断:是否要API重定向
00451BBB 8B45 F4 mov eax, dword ptr [ebp-C]
00451BBE 83C0 68 add eax, 68
00451BC1 50 push eax
00451BC2 FF75 FC push dword ptr [ebp-4]
00451BC5 FF75 FC push dword ptr [ebp-4]
00451BC8 8B4D F4 mov ecx, dword ptr [ebp-C]
00451BCB E8 9CF9FFFF call 0045156C ; API重定向
00451BD0 8B45 FC mov eax, dword ptr [ebp-4] ; API_Redi.00438198
00451BD3 83C0 08 add eax, 8
00451BD6 8945 FC mov dword ptr [ebp-4], eax
00451BD9 ^ EB D1 jmp short 00451BAC ; continue
00451BDB C9 leave
00451BDC C2 0800 retn 8
在00451BB6下硬件执行断点, 重新跑程序.
断点命中在00451BB6, 取消硬件执行断点.
此时00438198处还不是系统API断点
00451BCB E8 9CF9FFFF call 0045156C ; 写IAT项, 还不是API地址或壳内的函数地址
在00451BDB处F4, 让壳写完.
00451BDB C9 leave
F8跟出去, 看看哪里写系统API地址.
004537E2 8B45 F0 mov eax, dword ptr [ebp-10] ; 循环开始
004537E5 83C0 01 add eax, 1
004537E8 8945 F0 mov dword ptr [ebp-10], eax
004537EB 8B4D F0 mov ecx, dword ptr [ebp-10]
004537EE 3B4D FC cmp ecx, dword ptr [ebp-4]
004537F1 0F83 9E000000 jnb 00453895
004537F7 8B55 F0 mov edx, dword ptr [ebp-10]
004537FA 6BD2 28 imul edx, edx, 28
004537FD A1 1CF64500 mov eax, dword ptr [45F61C]
00453802 03C2 add eax, edx
00453804 8945 E4 mov dword ptr [ebp-1C], eax
00453807 8B4D E4 mov ecx, dword ptr [ebp-1C]
0045380A 8379 0C 00 cmp dword ptr [ecx+C], 0
0045380E 75 02 jnz short 00453812
00453810 ^ EB D0 jmp short 004537E2
00453812 837D F0 20 cmp dword ptr [ebp-10], 20
00453816 73 21 jnb short 00453839
00453818 BA 01000000 mov edx, 1
0045381D 8B4D F0 mov ecx, dword ptr [ebp-10]
00453820 D3E2 shl edx, cl
00453822 A1 04F64500 mov eax, dword ptr [45F604]
00453827 8B48 10 mov ecx, dword ptr [eax+10]
0045382A 23CA and ecx, edx
0045382C 85C9 test ecx, ecx
0045382E 74 09 je short 00453839
00453830 C745 D8 0100000>mov dword ptr [ebp-28], 1
00453837 EB 07 jmp short 00453840
00453839 C745 D8 0000000>mov dword ptr [ebp-28], 0
00453840 8B55 D8 mov edx, dword ptr [ebp-28]
00453843 8955 E0 mov dword ptr [ebp-20], edx
00453846 837D F0 20 cmp dword ptr [ebp-10], 20
0045384A 73 22 jnb short 0045386E
0045384C B8 01000000 mov eax, 1
00453851 8B4D F0 mov ecx, dword ptr [ebp-10]
00453854 D3E0 shl eax, cl
00453856 8B0D 04F64500 mov ecx, dword ptr [45F604]
0045385C 8B51 14 mov edx, dword ptr [ecx+14]
0045385F 23D0 and edx, eax
00453861 85D2 test edx, edx
00453863 74 09 je short 0045386E
00453865 C745 D4 0100000>mov dword ptr [ebp-2C], 1
0045386C EB 07 jmp short 00453875
0045386E C745 D4 0000000>mov dword ptr [ebp-2C], 0
00453875 8B45 D4 mov eax, dword ptr [ebp-2C]
00453878 8945 DC mov dword ptr [ebp-24], eax
0045387B 8B4D EC mov ecx, dword ptr [ebp-14]
0045387E 51 push ecx
0045387F 8B55 E4 mov edx, dword ptr [ebp-1C]
00453882 52 push edx
00453883 8B45 DC mov eax, dword ptr [ebp-24]
00453886 50 push eax
00453887 8B4D E0 mov ecx, dword ptr [ebp-20]
0045388A 51 push ecx
0045388B E8 B0000000 call 00453940
00453890 ^ E9 4DFFFFFF jmp 004537E2 ; 一个循环写IAT项,但还不是API地址
00453895 8B15 04F64500 mov edx, dword ptr [45F604] ; 写完了会到这里, F4等着来
F8往下走, 可以看到经过一个CALL时, IAT表项中有系统API地址, 也有壳函数地址.
在这个函数调用点下硬件执行断点, 重新跑程序.
004538C3 50 push eax ; 下硬件执行断点
004538C4 E8 D7FBFFFF call 004534A0 ; IAT表项 API重定向
F7跟进004534A0去看API重定向的判断, 观察00438198内容
004536E5 8B0D C0EB4500 mov ecx, dword ptr [45EBC0] ; API_Redi.0045EBC4
004536EB 51 push ecx
004536EC 8B55 E0 mov edx, dword ptr [ebp-20]
004536EF 52 push edx ; 下硬件执行断点等着
004536F0 E8 9B070000 call 00453E90 ; 这个CALL写IAT表项
004536F5 83C4 0C add esp, 0C
004536F8 ^ E9 3EFFFFFF jmp 0045363B ; 循环
004536FD 8B45 E8 mov eax, dword ptr [ebp-18]
00453700 A3 C0EB4500 mov dword ptr [45EBC0], eax
00453705 8B4D F8 mov ecx, dword ptr [ebp-8]
00453708 6BC9 14 imul ecx, ecx, 14
0045370B 8B55 08 mov edx, dword ptr [ebp+8]
0045370E C7440A 04 FEFFF>mov dword ptr [edx+ecx+4], -2
00453716 ^ E9 98FDFFFF jmp 004534B3 ; 循环
0045371B 8A45 FC mov al, byte ptr [ebp-4]
0045371E 8BE5 mov esp, ebp
00453720 5D pop ebp
00453721 C3 retn ; IAT写完了
现在除了硬件断点(ESP定律地址 +004536EF )没有其他断点.
F7进入00453E90, 看写IAT实现.
有点跟蒙了, 对00438198下硬件写入断点, 再看看谁在写IAT.
写IAT的代码就在这个函数中.
0045369B FF15 A8F64500 call dword ptr [45F6A8] ; kernel32.GetProcAddress
004536A1 8B4D E0 mov ecx, dword ptr [ebp-20]
004536A4 8901 mov dword ptr [ecx], eax ; 这里在写IAT, 第一次写的是系统API地址
00438198 7C80B731 kernel32.GetModuleHandleA
重新跑程序, 2个硬件断点都在(ESP定律地址, 写00438198).
翻IAT表项, 发现前面已经将IAT API重定向了
00438040 00458C35 API_Redi.00458C35
最上面被改的内存地址是00438040.
对00438040做内存写入硬件断点, 取消对00438198的硬件写入断点.
重新跑程序, 2个硬件断点都在(ESP定律地址, 写00438040).
在Memory窗口定位00438040, 看着写的内容.
F9继续, 当第三次F9时, 发现写入了系统API地址
004536A4 8901 mov dword ptr [ecx], eax ; 这里再写IAT, 第一次写的是系统API地址
004536A6 EB 2C jmp short 004536D4
00438040 7C80EE67 ASCII "j$h"
7C80EE67 kernel32.FindClose 6A 24 push 24
现在F7往下走, 看看谁在API重定向.
004536DD 85D2 test edx, edx
004536DF 74 17 je short 004536F8 ; 要跳,否则API重定向
004536E1 8B45 DC mov eax, dword ptr [ebp-24] ; API重定向
004536E4 50 push eax
004536E5 8B0D C0EB4500 mov ecx, dword ptr [45EBC0] ; API_Redi.0045EBC4
004536EB 51 push ecx
004536EC 8B55 E0 mov edx, dword ptr [ebp-20]
004536EF 52 push edx ; 下硬件执行断点等着
004536F0 E8 9B070000 call 00453E90 ; 这个CALL写IAT表项
004536F5 83C4 0C add esp, 0C
004536F8 ^ E9 3EFFFFFF jmp 0045363B ; 循环
修改代码, 不让API重定向
004536DF /EB 17 jmp short 004536F8 ; 要跳,否则API重定向
F9, 走到OEP
0044C731 58 pop eax ; F9后, ESP定律第一次被触发时, 到达这里
0044C732 58 pop eax
0044C733 FFD0 call eax ; F7去OEP
到达OEP
004331B8 55 push ebp ; OEP
004331B9 8BEC mov ebp, esp
004331BB 6A FF push -1
004331BD 68 00A34300 push 0043A300
004331C2 68 1C334300 push 0043331C ; jmp to msvcrt._except_handler3
00438000 77DAE9E4 ADVAPI32.RegCreateKeyExA
00438004 77DA6C17 ADVAPI32.RegCloseKey
00438008 77DB54A4 ADVAPI32.GetUserNameA
0043800C 77DA7AAB ADVAPI32.RegQueryValueExA
00438010 77DA7842 ADVAPI32.RegOpenKeyExA
00438014 77DAEAD7 ADVAPI32.RegSetValueExA
00438018 00000000
0043801C 77EFBA3F GDI32.TextOutA
00438020 77EF5B70 GDI32.SelectObject
00438024 77F0C63D GDI32.GetTextExtentPoint32A
00438028 77EF8D16 GDI32.GetObjectA
0043802C 77EF9410 GDI32.SetMapMode
00438030 77EF5D77 GDI32.SetTextColor
00438034 77F1BC60 GDI32.CreateFontA
00438038 00000000
0043803C 7C80BCF9 kernel32.SizeofResource
00438040 7C80EE67 ASCII "j$h"
00438044 7C80BB31 kernel32.lstrcmpiA
00438048 7C834DB9 kernel32.GlobalSize
0043804C 7C80BE46 kernel32.lstrlenA
00438050 7C80236B kernel32.CreateProcessA
00438054 7C82134B kernel32.GetWindowsDirectoryA
00438058 7C814F7A kernel32.GetSystemDirectoryA
将IAT表项从上到下扫了一遍, IAT表项地址全部是系统API地址.
可以脱壳了.
在OEP处, 用ollyDump脱壳(选择默认的导入表修复选项).
运行脱壳后的程序, 已经可以正常跑了.