| 关键词: nbsp 脱壳 程序 0040 push CALL 09 77 dword 一个 |
来源:http://www.asmcc.com【脱文标题】 逆向追踪+TC(模拟跟踪)方法寻找暗桩--解除脱UPX后的校验【脱文作者】 Lenus 【作者邮箱】 [email protected] 【作者主页】 www.popbase.net & bbs.asmcc.com【使用工具】 Ollydbg,Loadpe,PEID,Import_Rec 1.6【破解平台】 WinXp(SP2)【软件名称】 在DFCG上逛的时候找到的一个小外挂,好象是玩升级游戏可以看其他人的牌的工具。 【加壳方式】 UPX 0.89.6 - 1.02 / 1.05 - 1.24 -> Markus & Laszlo [Overlay]【脱壳声明】 我是一个考古人,在软件的土地上,发掘未知的宝藏!---------------------------------------------------------------------- 【脱壳内容】 1.寻找OEP+dump+修复IAT。UPX的壳,脱壳过程我就不再重复。用ESP定律可以马上找到,或者直接往下找到0000000前面的JMP。用LORDPE dump下来,用Import_Rec修复。注意到这个壳有OVERLAY,所以脱壳修复后,不求他马上能运行(如果马上就能运行那这篇文章就太垃圾了^^)本来这个壳不想写什么的,但是看到论坛上很多人在问,对于UPX脱壳后的修复问题。所以在这里我打算整理一次一些基本的方法。供大家参考,当然还有很多修复的方法,在这里不可能全讲完。而且随着自己的脱壳经验的增长慢慢的也会懂一些技巧!2.修复暗桩I.逆向跟踪法 运行修复好了IAT的程序,发现什么反应都没有! 从这里我们可以有两个结论: 1.壳的不能正常运行不是关于overlay的。因为,如果是因为没读到附加的数据应该有非法操作和警告的对话窗弹出。 2.壳应该是有校验的地方发现壳被脱掉了就直接退出了程序。 得到上面的结论我们不难得出我们的下一步是什么:找校验的地方。 但是代码茫茫我们该如何去寻找呢?我用我的逆向追踪法! 因为程序的退出一般用的是这个API:ExitProcess 于是我们从最后的函数开始向前追踪程序! 用OD载入脱壳后的代码我的是DUMP_.EXE,来到这里0040F765 d> 55 push ebp //这里是OEP0040F766 8BEC mov ebp,esp0040F768 6A FF push -10040F76A 68 903A4100 push dumped_.00413A90 在命令行下断BP ExitProcess ,F9运行,断下7C81CAA2 k> 8BFF mov edi,edi ; ntdll.7C930738 //停在这里7C81CAA4 55 push ebp7C81CAA5 8BEC mov ebp,esp7C81CAA7 6A FF push -17C81CAA9 68 B0F3E877 push 77E8F3B0这里是系统领空,看看堆栈。0012FF04 77C09D45 /CALL 到 ExitProcess 来自 msvcrt.77C09D3F //还记得我写的ESP定律的预备知识吗?(没看过的去补补课^^)0012FF08 00000000 \ExitCode = 0很明显77C09D45是call ExitProcess的时候,压入堆栈的地址。好了我们到77C09D45处去看看,ctrl+g 输入77C09D45,来到77C09D45处。77C09D3F FF15 1C12BE77 call dword ptr ds:[<&KERNEL32.ExitProcess>; kernel32.ExitProcess //这里call ExitProcess77C09D45 CC int3 //这里是返回地址 这里还是系统的领空,向上面找找是什么地方call到这里来的?77C09D11 CC int377C09D12 CC int377C09D13 8BFF mov edi,edi //上面是int 3 估计这个系统函数的入口是这里,F2下断。77C09D15 55 push ebp77C09D16 8BEC mov ebp,esp77C09D18 68 A440BE77 push msvcrt.77BE40A4 ; ASCII "mscoree.dll"77C09D1D FF15 E810BE77 call dword ptr ds:[<&KERNEL32.GetModuleHa>; kernel32.GetModuleHandleA找到了新的入口,那就从来一遍ctrl+F2 ,F9 , 断下!看堆栈!0012FF10 77C09E78 返回到 msvcrt.77C09E78 来自 msvcrt.77C09D130012FF14 00000000继续找,到77C09E78处!!77C09E73 E8 9BFEFFFF call msvcrt.77C09D13 //这里是call向77C09D13的地方77C09E78 CC int3 再想上找入口77C09E54 E8 0EFFFFFF call msvcrt._initterm77C09E59 59 pop ecx77C09E5A 59 pop ecx77C09E5B 85DB test ebx,ebx77C09E5D 74 0B je short msvcrt.77C09E6A //第3处77C09E5F 6A 08 push 877C09E61 E8 B3060000 call msvcrt._unlock77C09E66 59 pop ecx77C09E67 5E pop esi77C09E68 5D pop ebp77C09E69 C3 retn //第2处77C09E6A FF75 08 push dword ptr ss:[ebp+8] // 第1处77C09E6D 8935 541AC377 mov dword ptr ds:[77C31A54],esi77C09E73 E8 9BFEFFFF call msvcrt.77C09D13看到前面有个RETN 第一个可能的入口是77C09E6A,可能有一个call从什么地方call过来;然而我们知道有些RETN也可能是变形JMP(不过这个不太可能,但我们对他也不可掉以轻心);第3个地方是77C09E5D他JMP过了RETN。所以为了保险我们3个一起下断!!好了重来一次!F9,断在了77C09E5D 处,果然是JMP过了RETN好了继续向上找吧!77C09DD8 CC int377C09DD9 CC int377C09DDA CC int377C09DDB 8BFF mov edi,edi //找到这里,估计有是一个call吧!下断,重来!77C09DDD 55 push ebp重来后断在这里看堆栈:0012FF20 77C09E90 返回到 msvcrt.77C09E90 来自 msvcrt.77C09DDB //还没回到程序领空(真累,但是一路能断下来说明我们的方向是正确的!!)0012FF24 00000000继续来到77C09E90向上找77C09E7C CC int377C09E7D CC int377C09E7E m> 8BFF mov edi,edi //是这里 下断,重来77C09E80 55 push ebpF9 后断到这里,看堆栈!0012FF34 0040F8A3 /CALL 到 exit 来自 dumped_.0040F89D //终于回到程序领空了!庆祝一下吧!0012FF38 00000000 \status = 0好了,到程序领空去吧!0040F884 ^\EB F5 jmp short dumped_.0040F87B0040F886 6A 0A push 0A0040F888 58 pop eax0040F889 50 push eax0040F88A 56 push esi0040F88B 53 push ebx0040F88C 53 push ebx0040F88D FF15 F4804200 call dword ptr ds:[<&kernel32.#374>] ; kernel32.GetModuleHandleA0040F893 50 push eax0040F894 E8 17040000 call dumped_.0040FCB00040F899 8945 98 mov dword ptr ss:[ebp-68],eax0040F89C 50 push eax0040F89D FF15 BC864200 call dword ptr ds:[<&msvcrt.#657>] ; msvcrt.exit //从这里开始call向ExitProcess逆向追踪现在全部完毕了,呵呵,很累是吗?其实做一个好的crack,想不累是不可能的,关键是这一路下来我们在系统的领空玩得挺高兴的,而且多学会了一种方法,呵呵,值了!!II.TC模拟跟踪法下面我们的工作就是什么地方是校验了(一般的程序在exitprocess的前面不远就会有一个关键比较可以跳过,但是这个程序却没有。)于是我们在这里先停一停。来看看原未脱壳的程序它在这里是怎么运行的。呵呵我们发现原来这个地址离OEP还不是很远!于是,我们用OD载入未脱壳的程序,用ESP定律来到OEP处!(这里请格外注意一下,一定要关掉原来的程序,因为这个程序会检查在内存中是否还有另一个同样的进程,如果有就直接退出,这个也是我后来才知道的,但是这个东西却不是校验,所以,曾经影响了我的判断!)向下找到0040F89D地址,在他的前面的JMP和CALL全部下断(不要太多了^^)我们会发现程序在(用F8)步过0040F894这里的时候,程序就运行了。所以我们又得到了以下的结论: 1.程序的校验一定在0040F894里面的某个地方。因为我们的脱壳后的程序也步过了这一句但是什么都没反映就出来了!2.程序0040F89D是正个程序的终点。上面的call里面一定有个校验比较。如果发现程序被脱壳就直接跳出来结束。(难怪我们运行程序的时候什么反应都没有)知道上面的结论我们就再来一次。F7进入0040F894 的call0040FCB0 FF7424 10 push dword ptr ss:[esp+10] //到这里0040FCB4 FF7424 10 push dword ptr ss:[esp+10] 0040FCB8 FF7424 10 push dword ptr ss:[esp+10]0040FCBC FF7424 10 push dword ptr ss:[esp+10]0040FCC0 E8 43000000 call //这个call没什么东西啊,F7继续跟进!0040FCC5 C2 1000 retn 10 //返回就挂了0040FD08 - FF25 28864200 jmp dword ptr ds:[<&mfc42.#1576>] ; mfc42.#1576 //到这里,啊跳转表,原来这个是一个api啊!0040FD0E CC int3跳到这里:73D3CF2B m> 8BFF mov edi,edi ; ntdll.7C930738 //到这里了73D3CF2D 53 push ebx73D3CF2E 56 push esi哎,又是系统领空了!真是累,如果各位还想在里面玩玩我可不想在奉陪了,这里祭出OD强大的模拟跟踪命令TC,在命令行键入 TC EIP<7000000(只要不在系统领空的时候就断下来!)回车后,马上到了这里:0040F062 - FF25 98814200 jmp dword ptr ds:[<&mfc42.#3922>] ; mfc42.#3922 //又是一个api -_-!0040F068 - FF25 9C814200 jmp dword ptr ds:[<&mfc42.#1089>] ; mfc42.#1089到这里73DC9FED m> 8B15 9404E173 mov edx,dword ptr ds:[73E10494] //又来到系统领空。73DC9FF3 85D2 test edx,edx73DC9FF5 74 14 je short mfc42.73DCA00B再来TC EIP<7000000,回车后到这里00402300 64:A1 00000000 mov eax,dword ptr fs:[0] //哈哈,终于找到了。小样,别以为用了api我就认不出你了。00402306 6A FF push -100402308 68 38FF4000 push dumped_.0040FF380040230D 50 push eax0040230E 64:8925 00000000 mov dword ptr fs:[0],esp00402315 81EC 30020000 sub esp,2300040231B 53 push ebx0040231C 56 push esi0040231D 57 push edi0040231E 68 FC714100 push dumped_.004171FC ; ASCII "OLDWORM_GLUPGRADE_HELPER_APP"00402323 6A 00 push 000402325 8BF1 mov esi,ecx00402327 68 01001F00 push 1F00010040232C FF15 CC804200 call dword ptr ds:[<&kernel32.#627>] ; kernel32.OpenMutexA00402332 85C0 test eax,eax00402334 8986 C4000000 mov dword ptr ds:[esi+C4],eax0040233A 75 56 jnz short dumped_.004023920040233C 68 FC714100 push dumped_.004171FC ; ASCII "OLDWORM_GLUPGRADE_HELPER_APP"00402341 6A 01 push 100402343 50 push eax00402344 FF15 D0804200 call dword ptr ds:[<&kernel32.#93>] ; kernel32.CreateMutexA上面的0040233A有一个跳转,但是这个不是的哦,看到前面的OpenMutexA了吗?这个估计是判断内存里面还有同样的进程,然后决定新建一个新的进程CreateMutexA(如果哪位兄弟觉得这个功能很多余也可以暴掉,不过我还算是比较尊重原创的^-^)向下面找0040236F E8 ACD60000 call dumped_.0040FA2000402374 83C4 04 add esp,400402377 85C0 test eax,eax00402379 74 17 je short dumped_.00402392 //可疑目标10040237B E8 80A40000 call dumped_.0040C80000402380 85C0 test eax,eax00402382 74 28 je short dumped_.004023AC //可以目标200402384 6A FF push -1上面的两个都是可能关键跳转,为了确定我们在这里分别下断,考察未脱壳程序在这里是否跳转。考察结果:未脱壳程序:目标1没跳,目标2跳了。脱壳后的程序:目标1跳了,直接跳过了目标2。所以只要把00402379 nop掉就可以了。再次友情提醒,如果那位兄弟没有修改上面的判断进程的跳转的话,那么在考察两个程序关键跳的时候,一定要一个一个的看,不能开两个OD!!OK,暴掉后程序正常运行。---------------------------------------------------------------------- 【脱壳总结】 1.如果说逆向跟踪是大概确定校验地址的办法,那么正向跟踪(TC法)就是进一步确定精确校验的地址的办法。 2.找校验的时候,多次用到CTRL+F2&F9的办法,这是一个cracker的必修课,你如果觉得我上面写的CTRL+F2&F9已经很多了,那么我可以告诉你,我在调试的时候,比这里用到的CTRL+F2&F9多了10倍! 3.耐性很重要,一定要坚持----------------------------------------------------------------------【后话】 关于寻找校验的办法还有很多,有些我也不是很懂。但是,我们可以慢慢的积累经验,以后就会好了。谢谢你能看完。 如果有什么不足欢迎指出,如果要转载请保持文章的完整! |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|