【文章标题】: PEBundle3.2的高级捆缚分析
【文章作者】: iawen
【作者主页】: www.iawen.com
【软件名称】: EdrTest.exe
【下载地址】: 自己搜索下载
【加壳方式】: 加壳
【保护方式】: 加壳
【使用工具】: OD、LoadPE、ImportRec
【操作平台】: Xp Sp3
【软件介绍】: 一个UnpackMe,呵呵
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
先用PEid查一下壳,显示为:PEBundle 2.0b5 - 3.0x -> Jeremy Collake
因为朋友自己加的,所以知道是PEBundle 3.2版的,呵呵!
主要是分2步:
1、Dump出完整的主程序
2、Dump出捆绑的DLL
好了,不多说了,直接用OD载入,先来Dump出主程序吧: 00418000 Ed> 9C pushfd
00418001 60 pushad
00418002 E8 02000000 call EdrTest.00418009
00418007 33C0 xor eax,eax
00418009 8BC4 mov eax,esp
我们单步到00418002时,看一下ESP,下断点:hr 0013FFA0
然后F9运行,就到了这里: 004181A7 9D popfd
004181A8 68 4F154000 push EdrTest.0040154F
004181AD C3 retn
F8单步几下,很快就到了OEP位置了,呵呵,VC写的程序: 0040154F 55 push ebp
00401550 8BEC mov ebp,esp
00401552 6A FF push -1
00401554 68 58714000 push EdrTest.00407158
00401559 68 D0264000 push EdrTest.004026D0
0040155E 64:A1 00000000 mov eax,dword ptr fs:[0]
00401564 50 push eax
00401565 64:8925 0000000>mov dword ptr fs:[0],esp
0040156C 83EC 58 sub esp,58
0040156F 53 push ebx
00401570 56 push esi
00401571 57 push edi
00401572 8965 E8 mov dword ptr ss:[ebp-18],esp
00401575 FF15 A0704000 call dword ptr ds:[4070A0] ; kernel32.GetVersion
我们先看一下IAT,在00401575上,右键--数据窗口跟随--内存地址,发现有IAT加密: 00407098 7C801EF2 kernel32.GetStartupInfoA
0040709C 00419377 EdrTest.00419377================加密了的
004070A0 7C81126A kernel32.GetVersion
004070A4 00419501 EdrTest.00419501================加密了的
004070A8 00418F79 EdrTest.00418F79================加密了的
004070AC 7C801E1A kernel32.TerminateProcess
004070B0 7C80DE85 kernel32.GetCurrentProcess
我们重新载入程序,下断:hr 0040709C,然后F9运行,中断在了: 00418E14 8B19 mov ebx,dword ptr ds:[ecx]
00418E16 83C1 04 add ecx,4
00418E19 85DB test ebx,ebx
00418E1B 74 33 je short EdrTest.00418E50
00418E1D 8BC3 mov eax,ebx
00418E1F F7C3 00000080 test ebx,80000000
00418E25 74 08 je short EdrTest.00418E2F
00418E27 81E3 FFFF0000 and ebx,0FFFF
00418E2D EB 04 jmp short EdrTest.00418E33
00418E2F 43 inc ebx
00418E30 43 inc ebx
00418E31 03DA add ebx,edx
00418E33 51 push ecx
00418E34 52 push edx
00418E35 899D C2214000 mov dword ptr ss:[ebp+4021C2],ebx
00418E3B 53 push ebx
00418E3C FFB5 BA214000 push dword ptr ss:[ebp+4021BA]
00418E42 E8 32010000 call EdrTest.00418F79
00418E47 5A pop edx
00418E48 59 pop ecx
00418E49 85C0 test eax,eax
00418E4B 74 05 je short EdrTest.00418E52
00418E4D AB stos dword ptr es:[edi]
00418E4E ^ EB C4 jmp short EdrTest.00418E14=========中断在这里了,呵呵
这是一个循环,我们耐心的单步,单步这里: 00418E3C FFB5 BA214000 push dword ptr ss:[ebp+4021BA] ; 这里是DLL的基址
00418E42 E8 32010000 call EdrTest.00418F79
我们发现dword ptr ss:[ebp+4021BA]里加载的是DLL的基址,于是我们跟进去看: 00418F79 C8 000000 enter 0,0
00418F7D 55 push ebp
00418F7E 56 push esi
00418F7F E8 00000000 call EdrTest.00418F84
00418F84 5E pop esi
00418F85 81EE 842F4000 sub esi,EdrTest.00402F84
00418F8B 8B86 B2214000 mov eax,dword ptr ds:[esi+4021B2]
00418F91 3945 08 cmp dword ptr ss:[ebp+8],eax
00418F94 75 1B jnz short EdrTest.00418FB1
00418F96 55 push ebp
00418F97 51 push ecx
00418F98 52 push edx
00418F99 57 push edi
00418F9A 53 push ebx
00418F9B FF75 0C push dword ptr ss:[ebp+C]
00418F9E FF75 08 push dword ptr ss:[ebp+8]
00418FA1 E8 AEFEFFFF call EdrTest.00418E54
00418FA6 5B pop ebx
00418FA7 5F pop edi
00418FA8 5A pop edx
00418FA9 59 pop ecx
00418FAA 5D pop ebp
00418FAB 5E pop esi
00418FAC 5D pop ebp
00418FAD C9 leave
00418FAE C2 0800 retn 8
00418FB1 5E pop esi
00418FB2 5D pop ebp
00418FB3 FF75 0C push dword ptr ss:[ebp+C]
00418FB6 FF75 08 push dword ptr ss:[ebp+8] ; 这里加载函数名
00418FB9 FF15 E9884100 call dword ptr ds:[<&KERNEL32.GetProcA>; kernel32.GetProcAddress
00418FBF 85C0 test eax,eax ; 这里开始获取函数的地址了
00418FC1 74 25 je short EdrTest.00418FE8 ; 地址为0则返回
00418FC3 51 push ecx
00418FC4 56 push esi
00418FC5 50 push eax
00418FC6 E8 00000000 call EdrTest.00418FCB
00418FCB 5E pop esi
00418FCC 81EE CB2F4000 sub esi,EdrTest.00402FCB
00418FD2 8D8E 2B234000 lea ecx,dword ptr ds:[esi+40232B]
00418FD8 50 push eax ; EAX里是真实的函数地址
00418FD9 51 push ecx ; ECX里则是一个将要加密的地址
00418FDA E8 97FBFFFF call EdrTest.00418B76
00418FDF 85C0 test eax,eax
00418FE1 74 02 je short EdrTest.00418FE5
00418FE3 59 pop ecx
00418FE4 50 push eax
00418FE5 58 pop eax
00418FE6 5E pop esi
00418FE7 59 pop ecx
00418FE8 C9 leave
00418FE9 C2 0800 retn 8
然后我们继续跟进 call EdrTest.00418B76: 00418B76 C8 040000 enter 4,0
00418B7A 53 push ebx
00418B7B 57 push edi
00418B7C 56 push esi
00418B7D E8 00000000 call EdrTest.00418B82
00418B82 5B pop ebx
00418B83 81EB 822B4000 sub ebx,EdrTest.00402B82
00418B89 C745 FC 0000000>mov dword ptr ss:[ebp-4],0
00418B90 8B75 08 mov esi,dword ptr ss:[ebp+8]
00418B93 833E 00 cmp dword ptr ds:[esi],0
00418B96 74 34 je short EdrTest.00418BCC
00418B98 56 push esi
00418B99 8B7E 08 mov edi,dword ptr ds:[esi+8]
00418B9C 03FB add edi,ebx
00418B9E 8B76 0C mov esi,dword ptr ds:[esi+C]
00418BA1 03F3 add esi,ebx
00418BA3 8B45 0C mov eax,dword ptr ss:[ebp+C]
00418BA6 833F FF cmp dword ptr ds:[edi],-1
00418BA9 74 13 je short EdrTest.00418BBE
00418BAB 8B0F mov ecx,dword ptr ds:[edi]
00418BAD 85C9 test ecx,ecx
00418BAF 74 05 je short EdrTest.00418BB6
00418BB1 390419 cmp dword ptr ds:[ecx+ebx],eax ; kernel32.FreeEnvironmentStringsW
00418BB4 74 0E je short EdrTest.00418BC4 ; 注意这个比较,EAX里真实的IAT地址
00418BB6 83C7 04 add edi,4 ; 而ds:[ecx+ebx]里是什么呢?
00418BB9 83C6 04 add esi,4
00418BBC ^ EB E8 jmp short EdrTest.00418BA6
00418BBE 5E pop esi
00418BBF 83C6 10 add esi,10
00418BC2 ^ EB CF jmp short EdrTest.00418B93
00418BC4 8B06 mov eax,dword ptr ds:[esi]
00418BC6 03C3 add eax,ebx
00418BC8 8945 FC mov dword ptr ss:[ebp-4],eax
00418BCB 5E pop esi
00418BCC 5E pop esi
00418BCD 5F pop edi
00418BCE 5B pop ebx
00418BCF 8B45 FC mov eax,dword ptr ss:[ebp-4]
00418BD2 C9 leave
00418BD3 C2 0800 retn 8
关键在于这个比较了: cmp dword ptr ds:[ecx+ebx],eax
EAX里真实的IAT地址,而ds:[ecx+ebx]里是什么呢?我们右键跟随,在数据窗口里,我们看到了这些: 004188B9 00000000
004188BD >7C809BD7 kernel32.CloseHandle
004188C1 >7C801A28 kernel32.CreateFileA
004188C5 >7C8106C7 kernel32.CreateThread
004188C9 >7C831EC5 kernel32.DeleteFileA
004188CD >7C81CAFA kernel32.ExitProcess
004188D1 >7C80C0E8 kernel32.ExitThread
004188D5 >7C80AC6E kernel32.FreeLibrary
004188D9 >7C80DE85 kernel32.GetCurrentProcess
004188DD >7C8099B0 kernel32.GetCurrentProcessId
004188E1 >7C80B55F kernel32.GetModuleFileNameA
004188E5 >7C80B731 kernel32.GetModuleHandleA
004188E9 >7C80AE30 kernel32.GetProcAddress
004188ED >7C861807 kernel32.GetTempFileNameA
004188F1 >7C835DE2 kernel32.GetTempPathA
004188F5 >7C812B6E kernel32.GetVersionExA
004188F9 >7C801D7B kernel32.LoadLibraryA
004188FD >7C801D53 kernel32.LoadLibraryExA
00418901 >7C8309D1 kernel32.OpenProcess
00418905 >7C802213 kernel32.WriteProcessMemory
00418909 >7C809AE1 kernel32.VirtualAlloc
0041890D >7C809B74 kernel32.VirtualFree
00418911 >7C810E17 kernel32.WriteFile
00418915 00000000
这里都是要加密的函数了!
所以我们只需要把这句:00418B96 74 34 je short EdrTest.00418BCC
改成无条件跳转,跳过就OK了:jmp short EdrTest.00418BCC
好了,我们重新载入程序,直接来到刚刚找到位置,把je改成jmp,然后用ESP快速来到OEP
然后查看一下IAT: 00407098 7C801EF2 kernel32.GetStartupInfoA
0040709C 7C812FAD kernel32.GetCommandLineA
004070A0 7C81126A kernel32.GetVersion
004070A4 7C81CAFA kernel32.ExitProcess
004070A8 7C80AE30 kernel32.GetProcAddress
004070AC 7C801E1A kernel32.TerminateProcess
004070B0 7C80DE85 kernel32.GetCurrentProcess
004070B4 7C863E6A kernel32.UnhandledExceptionFilter
好了,我用LoadPE来Dump出程序,然后修复IAT(有一个无效指针,我们直接CUT掉就行了),一切OK,程序也能跑起来了,呵呵!
难道就此OVER了??也没见什么捆绑的DLL啊!我发朋友一看,XP SP2的,NO,NO,直接出错~!
原来是没有DLL,导致跨平台出错!
所以我们现在就是提取出DLL了,哈,我们再次载入OD,Alt+M打开内存镜像:
如图,大家看到了那些区段了吧,呵呵!这个壳很有意思,把DLL的所有区段,连名称都没变,就直接捆绑到主程序的区段里了!
好了,我们也不先运行了,直接DUMP吧,用LoadPE的部分Dump功能,地址填写:0040B000,大小是C000,大家可以自己累加一下!
然后将DUMP的文件名直接改成DLL名:EdrLib.dll
这个DLL名,我们可以通过下:bp GetModuleHandleA断点来找到,如下: 0013FF7C 00419196 /CALL 到 GetModuleHandleA 来自 EdrTest.00419190
0013FF80 00407954 \pModule = "EdrLib.dll"
我找了一个需要DLL 的主程序来测试,运行,NO,提示EdrLib.dll无效!看来DLL有修改!
好了,我们在.pe区段上双击进去,然后指定用PE头的格式来查看,一个个看下去,发现(如图):
.text区段的PointerToRawData怎么会是1200??应该是1000啊,因为PE头占了1000字节
继续往下看:发现.rdata、data、reloc等区段都多了200字节!而且reloc的虚拟地址也不对,多了1000字节!
先不管了,我们把这些修改过来:
1000、7000、 8000、B000后,再BDUMP一份,测试运行,依然NO!这是为什么??
想到了.reloc区段的虚拟地址多出了1000,这样我们在Dump时,因为大小是C000,所以没有包含上这个地址!
用HexWork打开一个,果然,全部是0!
好了,知道原因了,我们先好着手DUMP出DLL了,将上面的值一一修改过来!
注意.reloc的PointerToRawData是C000,呵呵!不能只简单的改为B000哦!
然后再部分Dump,如图:
大小增加了1000字节!
测试OK了,哈!
--------------------------------------------------------------------------------
【版权声明】: 转载请注明作者并保持文章的完整, 谢谢!
2009年01月30日 20:36:46 |