亲宝软件园·资讯

展开

Getright 5 手动脱壳和重建IAT--第二部分(图)

人气:0

在本参考教程的第一部分我们学习了如何正确地转储(dump)Getright 5. 现在我们将要去找神奇跳转,这样IAT会被正确地转储下来,而不用手工修复了. 要完成这一点,我们需要打败程序中的一些陷阱, 并使它们即使在检测到被执行脱壳时也无所作为.
让我们开始吧!
第一步:如何找到IAT的起始点和终点
在我们开始之前,先在安全的地方保存一份我们在先前第一部分教程中得到的转储文件的副本. 我将它命名为TUTE.exe记住这个名字.
用Ollydbg载入TUTE.exe. 我们将要去寻找IAT.

正如我们在上图中看到的,如果我们稍加跟踪,我们很容易发现一个
CALL [xxxxxx] 或者 JMP [xxxxxx] 间接地带有它们即将跳往IAT中某处的内存地址值.在这张图中,我们可以看到 CALL [5e9d94]. 那意味着这个call将要前往这个内存地址,实际上就是IAT表的入口值.在它的右边我们可以看到"kernel32.Getversion" (黄色). 这就意味着这是一个可以在IMPORT RECONTRUCTOR上被找到的正确的入口值.
让我们看一下转储(DUMP)窗口.在转储(DUMP)窗口右击,选择"前往 表达 5E9D94".

这就是看上去很不错的IAT. 正如你所看到的入口5E9D94指向77E5D142,那个值在我的机器里是API GetVersion.这个入口是正确的,此入口周围的其它一些入口也是指向类似7xxxxxxx的值.
注意在其它的机器上值7xxxxxx可能会有所不同.但是这些值都是相互类似的. 现在我们稍微往上看一看,去找此表的起始点.我们到达这里:

红线标记了此表的起始点.在此线的上面没有其它任何API值了.现在我们知道表从哪儿开始了
TABLE STARTS= 5E99EC
现在到右下角转储区去找IAT表的终止点.正如我们在图中看到的有两个可能的终止点. 如果我们吃不准最好选后面那个.但是我们有更好的方法.

选择任何一个不确认的值,然后将表向上翻页.停在401000.我们必须去找一个CALL [xxxxx]或者一个JMP [xxxxx].右击鼠标并选择SEARCH FOR BINARY STRING

我们将要去寻找那些不确认的入口中的一个,让我们试试5EA25C.记住逆序写这个值如下图所示.

到了这里

稍稍往上翻一下

我们看到那儿有一个使用此值的CALL,所以那些不确认的值也都是属于IAT的.
现在我们知道了
END OF TABLE= 5EA2BC
终点值是用来计算表的长度的.这是很简单的一步:
LENGTH= END-START
LENGTH=5EA2BC-5E99EC
LENGTH= 8D0
在纸上写下OEP,表的起始点和长度,在使用import reconstructor时你将用到这些值.
第二步:找出那些不指向任何API的错值.
当GETRIGHT.exe正在进行转储时,这个问题变的很容易.然后选择VIEW-MEMORY你将看到哪些入口指向了dlls而哪些则指向了错误的地方.
无论如何如果我们尝试用revirgin或import reconstructor则会有很多入口无法解决.这就是我们为什么请出哈里波特带来一些魔法.

正如在表中看到的,还存在一些(黄色标记)没有解决的入口.通常对付这种情况的方法是从我开始转储起就跟踪,然后追踪每一个调用API的call,写下名字最后用Import Reconstructor.
如果自由很少几处没有解决的入口这样做将非常简便,但是如果有很多的话,这项工作将令人难以忍受,所以让我们看看另一种方法.
第三步:如何去找出MAGIC JUMP
这里说明一点:我们已经知道程序的父进程和他的子进程是相同文件,但要用不同的句柄装入两次,所以他们变成两个不同的进程。一方面父进程的OEP是5F90B9,父进程从这里开始运行.

另一方面我们知道当子进程被转储时其OEP是534E90,但我也要告诉你,子进程也是以相同的父进程的入口点5F80B9开始运行.子进程运行并且解出自己的IAT然后跳到子进程引起错误的OEP,我希望你记住这一点,我在第一部分曾经讲过.
如果你相信以上所说,我告诉你,跟踪父进程直到父进程开始转变为两个进程,父进程和子进程,找到那个地方,那非常有用.我已经找到了那个地方,那是个条件跳转,至于如何找到的,我以后再告诉你.
顺便一提它不是从父进程那复制 IAT 到它的子进程,却是从子进程那解出自己的 IAT。这应该是一大麻烦,因为我们不能在他从父进程脱离前用 OllyDbg 进入子进程。
这是个烦恼,因为我不知道如何进入子进程去观察它如何解出自己的IAT.
首先我尝试着从转储程序TUTE.exe中找一个错误的entry.

我选择了5E9C34但是你可以选择其它任何错误的entry.正如你所看到的,它的值为DF5070.
按老规矩记住要将 IsDebuggerPresent 的值设为零,清除所有以前的 BPX、BP WaitForDebugEvent 然后单击运行.
我们将停在此断点或者某个例外上.那时,打开PUPE选择进程中2个进程中的上面一个(因为他是子进程,是我们需要的).右击进入parcheando窗口选择4字节,如图在地址里填入错误的entry offset 5E9C34.然后点击BUSCAR. 你将会看到没什么变化,因为所有值都是零.

然后不断的在OD中点击运行、在PUPE中点击BUSCAR,并检查字节窗口中的值.
第一次变化可能会很久才发生, 但是记住不断的在OllyDbg运行、在PUPE中点击BUSCAR,并检查字节窗口中的值,直到字节窗口中的值从零开始变化.

如图所示,那个值变化了. 现在再重复几次,你将会看到:


子进程已经完全解出并赋值IAT. 我们必须在那个值第一次变化之后子进程赋值之前进入子进程. 这是个问题,但我耍了一个把戏解决了.
我们将要重复上述过程.关闭Parcheando--PUPE的窗口,因为当重启后进程的句柄会变.
重复上述过程直到你在PUPE的窗口中观察到那个值的第一次变化.你应该看到类似这样的东西:


很难找到一种好方法, 所以我决定在某个API上制造一个死循环.就选GetProcAddress吧.所以现在我们要去父进程中找到它.选择VIEW-EJECUTABLE MODULE 查找KERNEL32.dll 这是GetProcAddress所属的dll.

右击VIEW-NAMES查找api GetProcAddress


写下这个地址(红箭头).现在在PUPE中选择子进程,让它去找search (BUSCAR)这个地址值. 不同的机器这个值会不同. 我的是77E5B332.在字节窗口你会看到前两个字节是55 8B,所以在纸上写下,并在PUPE中将其改为EB FE然后按下PARCHEAR

在那儿现在子进程将会处于死循环中,我们可以说它已经睡着了. 我们现在必须把它从它的父进程那儿unhook下来.
在父进程窗口任意处点击鼠标右键,选择"新建起源"然后写下下面的代码
PUSH (son’shandle)
Call DebugActiveProcessStop
Nop (在这儿BPX并检查是否EAX=1)

在0042F00A 下中断,然后运行一下停在0042F00A 处。看看寄存器窗口的EAX值,如果这个值=01就表示子进程和父进程分离了。如果=00那么可能是子进程的句柄填错了, 你可以在下面重新写入代码再次运行,直到EAX=01时就可以关闭OllyDbg了(杀掉父进程!)然后就可以进入子进程了。
重新运行Ollydbg(不要加载程序)附加上子进程.
再次在PUPE中恢复原来的代码 55 8B并按 "Parchear" 再看看程序中的代码又还原成了77E5B332.

程序将会中断,就像你在下图中看到的.

让我们看一下那个错误的entry,右击转储dump窗口选择GOTO EXPRESSION 5E9C34.

Bp GetProcAddress 然后按RUN. 你可以看到当中断时错误的entry已经被重写了,但是表并不完整. 重新打开一个ollydbg载入tute.exe.如果在5e9c8c下面还有一个错误的entry就看那儿.

以5E9c98处的错值为例,因为那儿将会被写入DF513C.

回到Getright.exe (第一个ollydbg)看一下堆栈. 那儿的API被Df4cB2处的called调用并将会返回到DF4cB8.
在主窗口中选择GOTO EXPRESSION 0Df4cb8.



下图是我的机器中这个call了API的call的地址. 在API返回处BPX(这个call的下一行).

点击RUN
现在在错误entry 5E9c98上下HARDWARE BPX ON WRITE然后点击RUN.当在那儿写入时将被断下.

你可以看到在前面一行保存着错误值.后面指出这个错值从[ebp-394]处来.所以在转储窗口中选择GOTO EXPRESSION EBP-394. 那儿你将会看到这个值.下BREKPOINT HARDWARE ON ACCESS.

如果我按下运行键,我将看到无论值是好是坏都在这一行被保存.看附近有一个call BPX它.

你可以看到这是关键call.因为当它结束时eax带着稍后将写入的值. 我测试了这个call里的每一个跳转(只有5到6个,通常都是这样):
00DF4B68 /75 03 JNZ SHORT 00DF4B6D
00DF4B79 /75 07 JNZ SHORT 00DF4B82
00DF4B8D /74 0C JE SHORT 00DF4B9B
00DF4B92 /74 1B JE SHORT 00DF4BAF
00DF4B99 ^75 F4 JNZ SHORT 00DF4B8F
胜利者是DF4B8D处的跳转. 正如我先前所说的在所有的armadillos里都是一样的,所以当我们在脱其它armadillo时,通过跟踪这些代码我们可以很容易发现这个好跳转.
00DF4B8D /74 0C JE SHORT 00DF4B9B
它必须永远跳转,所以我们做如图修改:

清除BPX点击run. (记住清除hardware bpx). 当程序走到我们修改为死循环的指令那儿时输入表已经修复了,再也没有坏值了. 然后它在子进程的OEP处停下.在纸上写下magic jump的值(EB 0C),记住.
我们将要重复那些步骤.当我们走到上次我们unhook 子进程那一步时,我们将要在magic jump处设置一个 infinite loop代替在API上设置.

在那儿从父进程处unhook子进程,然后它将在magic jump处死循环.
附加子进程,按 RUN, 然后停止(F12). 它将停在magic jump.

现在还没有任何改变,因为magic jump还没起作用.把它改回先前的值(EB 0C).

如果我们运行程序,它将停在OEP,而且输入表也很完美和完整.现在我们可以利用这个进程用Import Reconstructor去修复在第一部分中转储的程序了.

所有的值都是好的.最后那几个是指向dll的.

那些值指向Exxxxx,如果我们看看VIEW-MEMORY我们可以得知那是dll.

现在我们要用一下Import Reconstructor.
打开Import Reconstructor在"Attach to an Active Process"对话框选择这个进程.

装载这些dll需要很长一段时间.当完成后写下OEP的值,表的起始地址值和表的长度,不要按IAT AutoSearch.

如图在IMPORT RECONSTRUCTOR中那些值是这样计算的:你在纸上写下的值-Image Base. 比如: OEP=534E90, Image Base=400000, 534E90- 400000= 134E90. RVA也一样 (表的起始地址值).
现在按GET IMPORTS. 所有的entries都修复了.



现在按FIX DUMP 载入转储文件.我把它叫做TUTE.exe

它将被保存为TUTE_.exe.原先的文件保持原样.
到此armadillo保护已经被击败了.
第四步:如何击败BOSS
如果我们尝试运行TUTE_.exe它会无故终止.上面我刚说已经击败了armadillo,但是脱壳文件不能正常运行.见鬼了?
程序员(bad guys)不想让getright 5在我们的机器中自由运行、解压,然后他们做了手脚.
在OLLYDBG中载入tute_.exe.
运行,你会发现它突然终止.
BPX GetEnvironmentVariableA 当它中断时我们需要改变条件跳转,当从API返回程序时.
T看下图.

这是中断点BPX.现在返回程序用EXECUTE TILL RETURN 然后按一下F7.

这一流程将会重复多次,所以要熟练掌握.
修改跳转.

依次类推.

把JNZ改为JMP 你可以找到所有calls API然后修改临近的跳转.我把那些跳转罗列出来.

把JNZ改为JMP 你可以找到所有calls API然后修改临近的跳转.我把那些跳转罗列出来.

我想这个armadillo已经被搞定了. Ricardo Narvaja 英文翻译Tenshin. 中文翻译:FTBirthday 完成了

加载全部内容

相关教程
猜你喜欢
用户评论