[转载]逆向追踪+模拟跟踪方法寻找暗桩 解除脱UPX后的校验
信息来源:[url]www.asmcc.com[/url]【脱文标题】 逆向追踪+TC(模拟跟踪)方法寻找暗桩--解除脱UPX后的校验
【脱文作者】 Lenus
【作者邮箱】 [email]Lenus_M@163.com[/email]
【作者主页】 [url]www.popbase.net[/url] & 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 //这里是OEP
0040F766 8BEC mov ebp,esp
0040F768 6A FF push -1
0040F76A 68 903A4100 push dumped_.00413A90
在命令行下断BP ExitProcess ,F9运行,断下
7C81CAA2 k> 8BFF mov edi,edi ; ntdll.7C930738 //停在这里
7C81CAA4 55 push ebp
7C81CAA5 8BEC mov ebp,esp
7C81CAA7 6A FF push -1
7C81CAA9 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 ExitProcess
77C09D45 CC int3 //这里是返回地址
这里还是系统的领空,向上面找找是什么地方call到这里来的?
77C09D11 CC int3
77C09D12 CC int3
77C09D13 8BFF mov edi,edi //上面是int 3 估计这个系统函数的入口是这里,F2下断。
77C09D15 55 push ebp
77C09D16 8BEC mov ebp,esp
77C09D18 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.77C09D13
0012FF14 00000000
继续找,到77C09E78处!!
77C09E73 E8 9BFEFFFF call msvcrt.77C09D13 //这里是call向77C09D13的地方
77C09E78 CC int3
再想上找入口
77C09E54 E8 0EFFFFFF call msvcrt._initterm
77C09E59 59 pop ecx
77C09E5A 59 pop ecx
77C09E5B 85DB test ebx,ebx
77C09E5D 74 0B je short msvcrt.77C09E6A //第3处
77C09E5F 6A 08 push 8
77C09E61 E8 B3060000 call msvcrt._unlock
77C09E66 59 pop ecx
77C09E67 5E pop esi
77C09E68 5D pop ebp
77C09E69 C3 retn //第2处
77C09E6A FF75 08 push dword ptr ss:[ebp+8] // 第1处
77C09E6D 8935 541AC377 mov dword ptr ds:[77C31A54],esi
77C09E73 E8 9BFEFFFF call msvcrt.77C09D13
看到前面有个RETN 第一个可能的入口是77C09E6A,可能有一个call从什么地方call过来;然而我们知道有些RETN也可能是变形JMP(不过这个不太可能,但我们对他也不可掉以轻心);第3个地方是77C09E5D他JMP过了RETN。所以为了保险我们3个一起下断!!
好了重来一次!F9,断在了77C09E5D 处,果然是JMP过了RETN
好了继续向上找吧!
77C09DD8 CC int3
77C09DD9 CC int3
77C09DDA CC int3
77C09DDB 8BFF mov edi,edi //找到这里,估计有是一个call吧!下断,重来!
77C09DDD 55 push ebp
重来后断在这里看堆栈:
0012FF20 77C09E90 返回到 msvcrt.77C09E90 来自 msvcrt.77C09DDB //还没回到程序领空(真累,但是一路能断下来说明我们的方向是正确的!!)
0012FF24 00000000
继续
来到77C09E90向上找
77C09E7C CC int3
77C09E7D CC int3
77C09E7E m> 8BFF mov edi,edi //是这里 下断,重来
77C09E80 55 push ebp
F9 后断到这里,看堆栈!
0012FF34 0040F8A3 /CALL 到 exit 来自 dumped_.0040F89D //终于回到程序领空了!庆祝一下吧!
0012FF38 00000000 \status = 0
好了,到程序领空去吧!
0040F884 ^\EB F5 jmp short dumped_.0040F87B
0040F886 6A 0A push 0A
0040F888 58 pop eax
0040F889 50 push eax
0040F88A 56 push esi
0040F88B 53 push ebx
0040F88C 53 push ebx
0040F88D FF15 F4804200 call dword ptr ds:[<&kernel32.#374>] ; kernel32.GetModuleHandleA
0040F893 50 push eax
0040F894 E8 17040000 call dumped_.0040FCB0
0040F899 8945 98 mov dword ptr ss:[ebp-68],eax
0040F89C 50 push eax
0040F89D 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 的call
0040FCB0 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 ebx
73D3CF2E 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,edx
73DC9FF5 74 14 je short mfc42.73DCA00B
再来TC EIP<7000000,回车后到这里
00402300 64:A1 00000000 mov eax,dword ptr fs:[0] //哈哈,终于找到了。小样,别以为用了api我就认不出你了。
00402306 6A FF push -1
00402308 68 38FF4000 push dumped_.0040FF38
0040230D 50 push eax
0040230E 64:8925 00000000 mov dword ptr fs:[0],esp
00402315 81EC 30020000 sub esp,230
0040231B 53 push ebx
0040231C 56 push esi
0040231D 57 push edi
0040231E 68 FC714100 push dumped_.004171FC ; ASCII "OLDWORM_GLUPGRADE_HELPER_APP"
00402323 6A 00 push 0
00402325 8BF1 mov esi,ecx
00402327 68 01001F00 push 1F0001
0040232C FF15 CC804200 call dword ptr ds:[<&kernel32.#627>] ; kernel32.OpenMutexA
00402332 85C0 test eax,eax
00402334 8986 C4000000 mov dword ptr ds:[esi+C4],eax
0040233A 75 56 jnz short dumped_.00402392
0040233C 68 FC714100 push dumped_.004171FC ; ASCII "OLDWORM_GLUPGRADE_HELPER_APP"
00402341 6A 01 push 1
00402343 50 push eax
00402344 FF15 D0804200 call dword ptr ds:[<&kernel32.#93>] ; kernel32.CreateMutexA
上面的0040233A有一个跳转,但是这个不是的哦,看到前面的OpenMutexA了吗?这个估计是判断内存里面还有同样的进程,然后决定新建一个新的进程CreateMutexA(如果哪位兄弟觉得这个功能很多余也可以暴掉,不过我还算是比较尊重原创的^-^)
向下面找
0040236F E8 ACD60000 call dumped_.0040FA20
00402374 83C4 04 add esp,4
00402377 85C0 test eax,eax
00402379 74 17 je short dumped_.00402392 //可疑目标1
0040237B E8 80A40000 call dumped_.0040C800
00402380 85C0 test eax,eax
00402382 74 28 je short dumped_.004023AC //可以目标2
00402384 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.耐性很重要,一定要坚持
----------------------------------------------------------------------
【后话】
关于寻找校验的办法还有很多,有些我也不是很懂。但是,我们可以慢慢的积累经验,以后就会好了。谢谢你能看完。
如果有什么不足欢迎指出,如果要转载请保持文章的完整!
页:
[1]