• 标 题:Armadillo for Dummies: Getright 5 手动脱壳和重建 IAT
  • 译 者:FTBirthday
  • 时 间:2003年11月30日
  • 链 接:http://bbs.pediy.com

这是一篇Armadillo加壳软件Getright 5.01的脱壳译文,我是参照Ricardo Narvaja的“Getright 5 脱壳和重建IAT”的文章以及Bighead[DFCG][YCG]的译文,一边实践一边再次翻译的。感谢Ricardo Narvaja和Bighead[DFCG][YCG]。

Armadillo for Dummies: Getright 5 手动脱壳和重建 IAT (第一部分)
因为Armadillo实在是太复杂了,以至于人们已经不得不沿用下面这个手动的方法去脱壳armadillo,这不是我的过错,只是因为armadillo实在是太难脱壳了,然后我将会在这个文档中一步一步地去完成脱壳,同时配上很多图片,只是想让它变得尽量容易理解.我并不想写一个10兆大的参考教程,所以我把这个文档分成两部分.第一部分主要讲如何转储(dump)armadillo保护的程序,然后我会在第二部分中提及一些以前在其它任何教程中都未曾见过的东西,那就是输入表(import table)的redirection和rebuilding.

在我们开始之前,我必须声明这篇参考教程只在Windows XP上测试.请不要在 windows98或者2000中尝试.原因是只有Windows xp才拥有必要的API,能够把子进程(child)从它的父进程father那儿脱钩下来.

这次练习,我们找到的自愿者是Getright 5 final. Crusader曾经写过关于这个软件的某一个版本的参考教程,但是这篇参考教程在很多方面都不同于crusader写的那篇.

crusader写的那篇参考教程(原版)----附(1)

crusader写的那篇参考教程(FTBirthday翻译版本)----附(2)

你可以从以下连接下载GetRight 5.不管怎样我在我的ftp中保存了一个副本,所以你可以从那儿下载,就不用去管它在网上被放在哪儿了.

http://www.getright.com/

抓起你的鼠标,安装好Getright然后准备作战.Armadillo将会变得软弱无力.

如何脱壳(UNPACK)以及转储(DUMP)Getright 5

在OLLYBG中载入GETRIGHT.exe然后整装待发.我将按照以下的步骤,很容易记住的,你可以在其它任何armadillo保护软件中按部就班.

第一步:重新设置检测ISDEBUGGERPRESENT API的字节

我将要解释如何在任何电脑中去寻找这个字节.

首先从这个连接下载命令行插件:

http://dd.x-eye.net/file/cmdbar10802.zip

解压它然后复制dll到OLLYDBG文件夹.只要你运行Ollydbg你将会在调试窗口的左下角看到一个白色的输入框,在那儿你可以键入很多命令.让我们键入:
Bp IsDebuggerPresent

我们将要使用BP而不是BPX,因为BP可以直接在API上下断点,那正是我们所需要的.

我们必须准确地键入Bp IsDebuggerPresent.记住我们必须保证大小写的顺序,因为如果我们键入了任何不同于以上形式的命令,那么一个错误提示(UNKNOWN COMMAND)将在命令行的右边产生.所以正确地键入BP IsDebuggerPresent然后按回车. 如果在按回车后没有错误提示出现,那就说明这个BP已经生效.

现在按F9运行程序,它将会中断在API IsDebuggerPresent

在这张图中我们可以看到OllyDBG中断在API IsDebuggerPresent(红色), 然后按F7步进到第三行(白色)

77E52749 0FB640 02 MOVZX EAX,BYTE PTR DS:[EAX+2]

一旦到达那儿,看一下disassembly,在那儿我们可以看到如下图的内容:

这儿我们可以看到在我的机器中偏移是7ffdf002,包含了值01.这个地址在其它机器中可能会不同. 值01意味着调试器被检测到了,所以写下这个地址,在任何时候我们在ollydbg中运行程序,都必须把它修改为00,从而避免被packer检测到.记住在ollydbg中运行或重运行程序的任何时候修改此值为00. 

我们将以重启ollydbg (ctrl+F2)来结束第一步,到转储窗口(DUMP)单击右键选择"前往 表达" 7ffdf002或者其它任何先前你记下的地址,然后把它的值从01修改为00.

把它改为00.

第二步:BP WAITFORDEBUGEVENT

这一步并非必要,但却是一个好的选择,当它停止时在转储(DUMP)区看到report.让我们看看该怎样做.

首先我们需要在命令行输入BP WaitForDebugEvent,和我们输入IsDebuggerPresent的方法是相同的.

现在运行它,当它停下时我们可以在堆栈窗口(Stack window)看到关于这个API的所有参数信息.

看一下第二行.那儿我们可以看到它将在哪儿显示report,所以到转储窗口(dump window), 右键选择 "前往 表达"= 12EFF8.注意这个值在一些机器上可能会不同,所以前往你的机器显示的值.

我们在上面的图中看到的是我们将要称之为"API report"的东西.主程序(让我们把它称为Father)通过那里得知secondary program(让我们把它称为son)发生了什么. 

现在我们可以很容易地通过在命令行输入BC WaitForDebugEvent来删除这个断点.

所有的操作都是为了在转储窗口(dump window)看到report,在这个report中我们可以看到入口点OEP(entry point).

第三步: BP WRITEPROCESSMEMORY

像图中一样设置这个断点然后点击"运行".也许你会在一些例外(exceptions)处停下,但是你可以通过按SHIFT + F9很容易地走过.一旦我们停在了WriteProcessMemory API不要做任何事情,看下图:

正如我们在下图中看到的, father将要复制给son的第一个块(block)的所有信息,都可以在堆栈窗口(STACK window)看到.

FTBirthday:由经验,我觉得很难走到这一步,但是我知道是在005F949E处call了WriteProcessMemory如图:

我通过一遍遍试验,得出了最好的方法:等到前两次在别处call了WriteProcessMemory后,左手一直按住shift, 右手连续按F9大约31次,就可以顺利到达.

这些信息被保存在父进程的一个缓冲区从3B8FB0开始,然后它会复制给子进程一个从538000开始的 1000 bytes (BYTES TO WRITE)的块(block),所以如果我们到了这一步,我们可以很容易理解child的第一个section是完全空的,所以当它试图执行到OEP时,它报告一个error因为那儿没有任何东西,所以 father得到了通知正如我们可以在father的report中看到的,所以它停止了运行,复制必要的数据块,然后继续运行直到下一个error.被复制的数据块的大小是1000 bytes,所以当程序试图执行任何超过这个大小的block外的指令时, 另一个error将会发生,然后这个error会被通知给father, father会复制另外1000 bytes的块block然后继续. 

顺便说一下,我们可以假设第一个error发生是因为son (坏小子)call了它的入口点OEP,它的值应该就在 report上.显然OEP的值必定在第一个block,father复制过去来解决son报告的error.

这个块从538000开始知道538fff.OEP值必定在这些值中. 让我们看一下REPORT.看转储窗口(DUMP window) 我们先前已经知道了指向report的指针12EFF8.

太好了!在那儿我们至少看到了三次这个值: 538540.数学课告诉我538540比538000大,比538fff小 (那些是father试图拷贝给son的那个块的起止值)所以我可以确信538540就是child的OEP.

让我们从API WriteProcessMemory来改变断点的性质.知道哪个块被拷贝不是很好吗.可以通过下面的方法做到.首先选择bpx所在的行,按F2移除bpx.

右击选择"断点/条件记录".现在在对话框中准确地填入如图所示的值.这样做的目的是使我们在记录窗口中看到所有被father decrypts并拷贝给son的blocks.

第四步: NOP THE CRIPTER CALL

在先前的参考教程s中我们知道,这儿我们是在decripter call.Father为son decrypts了一个block.但是还有一个cripter call用来encrypts或者destroys已使用的blocks来避免被转储dump.现在的任务是如何找到这个call并把它nop掉. 

我现在在WriteProcessMemory API,所以我打开"呼叫堆栈窗口"(Alt+K)在那儿我可以看到:

在这个"呼叫堆栈窗口"越靠上面的是越最新被执行的.从CALLED FROM 我们可以看到5F949E是father calls API的地方. 如果我们向下看,我们可以看到另一个call.所以我们获得了decripter call的offset. 

DECRIPTER CALL= 5F88D1

通过"前往 表达" 5F88D1跳到那儿 ,或者左键双击它.

这是好的CALL,它用来decrypts.现在去找坏的那个,稍稍向下翻屏或者右击选择"查找参考/呼叫目标" 选择出现的两个参考中的另一个.

它在这儿呢!

重新加密的call--ENCRIPTER CALL = 5F8A24

现在我们把它nop掉.我们选择这个call按空格写入nop.

第五步: 运行API并在OEP处设一个无限循环

现在到了执行WriteProcessMemory一次了. 我们有两条路可走,可以选择执行到返回它将会执行直到RET,然后按一次F7. 第二个选择是去stack第一行在那儿设一个BPX最后运行它.

在stack第一行提示程序会返回到5F94A4, 我们到那儿BPX (F2)然后运行它(F9).

现在该用PUPE了. PUPE是一个西班牙工具.你可以在www.google.com搜索一下. 通过下面几行代码我们将要在son的OEP设置一个无限循环.这会使son休眠直到我们叫醒它. 

这是一张PUPE的图片.我们可以看到有两个进程, the son and the father.上面那个是son. 在右边我们可以看到进程句柄.

选择上面那个getright.exe然后右击选择"parchear".

写下出现在对话框中的original bytes然后用无限循环代码EB FE代替,如下面的对话框中所示.

最后一步就是"PARCHEAR"然后INFINITE LOOP就准备好了.

第六步: BP WAITFORDEBUGEVENT 和 NOP THE API

不要清除先前断点WriteProcessMemory,是时候写入新断点了.键入BP WaitForDebugEvent回车, 然后按F9运行

它停下时

你必须记住NEVER EVER RUN THIS API.在转储窗口"DUMP window"看一下report, 然后去堆栈窗口(STACK window).

正如我们看到的如果我们运行这个API我们应该去5F751D,所以让我们通过"前往 表达" 5F751D到主窗口.

再一次提醒不要RUN THIS API.所以右击5F751D选择"新建起源".这是跳过这个API的方法.

现在我们必须nop the call to the api以及有关的push.

所以在这些行上按空格键然后写入nop.当你完成这项工作时,将会如下图所示. 这一步结束了.

第七步:如何打入补丁

当做这一步时请注意.很多人不知道究竟该如何打补丁,所以努力试着理解我这里所作的, 然后学会在其它情况下运用. 

第一步是改变跳转,

我们将要改变这个跳转为JMP 401000那是我们将要打补丁的地方, 父进程offset 401000 .

跳转设好了,现在去401000 (ctrl+G)我们要开始写补丁了. 在report中我们可以看到三次entry point value.也许OEP在其它情况下会出现更多次.但是我们只需改变这三个.

father unpacks了在report中显示的block.所以我们将要欺骗father则他会相信从第一个section开始的所有blocks都存在错误, 一个接一个所有的blocks都会被unpacked.因为这一点我们需要知道第一个 section的起止点.点击"查看/内存"

这儿我们可以看到getright的sections.忽略Header section看text section,它从401000开始,结束在589000-1=588fff. 

401000-588fff (第一个section的范围)

我们将要在report中用400000代替OEP的值.

正如你在图中看到的我写入了40000,然后patch会在每一个loop前加1000,所以第一个被unpack的block将在401000.

现在我们将要在401000处写下如下补丁.

 

00401000 8105 10F01200 0>picture1/add DWORD PTR DS:[12F010],1000

0040100A 8105 1CF01200 0>picture1/add DWORD PTR DS:[12F01C],1000

00401014 8105 20F01200 0>picture1/add DWORD PTR DS:[12F020],1000

这些行将我们先前在转储窗口(dump window)写的值增加1000.那些蓝色的值在不同的机器上可能会不同,所以修改为你在转储窗口(dump window)看到的实际值. 再次提醒,证实在转储窗口(dump window)写入了00 00 40 .最容易犯的错误就是在补丁(蓝色的)中的offset和转储窗口(dump window)的offset不匹配.

下一行该这样写:

0040101E 813D 20F01200 0>CMP DWORD PTR DS:[12F020],getright.00589000

比较是为了让我们知道何时所有的块都被unpacked了. 

然后我们必须写下:

00401028 - 0F85 F6341F00 JNZ getright.005F7524

如果比较为假,则返回called loop的地方.然后我们需要在最后写下NOP,在那儿我们将要设下一个BP. 当它完成转储时,它将会停在那儿.

这儿我们可以看到完整的补丁代码

我们可以跟踪第一个循环,来检测report中的each entry是否每次都增加1000.

第八步: UNPACK 

按F9运行程序然后等待它停在我们先前在补丁中设下nop的bpx处.如果没有错误发生,当我们停止时,看view-LOG我们将要看到所有的unpacked blocks. 

这是假设的所有unpacked blocks的LOG.我没有完整的给出因为地方不够但是你可以在你的日志窗口中查看.

所以现在从401000到589000(包括588fff)的所有blocks都被unpacked了,然后dump已经就绪. 

第九步:把CHILD从FATHER那儿脱钩

一旦我们停在NOP,让我们写下下面几行.
PUSH (child的handle )

Call DebugActiveProcessStop

如果handle由字母开始,你需要在它前面键入一个0,因为不这样做的话olly不会认出. 想知道son的handle只需看一下pupe, 记住child的handle是在两个进程中的上面的那个.你一样可以通过opening file attach来获得son的handle.那儿你可以看到两个名称相同的进程. 红的是father,黑的是son.在此情况下我得到的是B78 所以我将要写下0B78.记住handle在每次运行程序时都会改变.

在图示的nop处下断点,然后运行程序.看registers window,如果当程序停止时,EAX=1那意味着son已经从father处脱钩了,我们可以关闭OLLY.如果EAX=0 那么你要检查那几行代码,因为你可能犯了写错误(也许是写错了handle).


现在关闭OLLYDBG,然后再次打开他.不要载入任何东西.前往"文件/附加"然后找son,附加它(我们搞定它的father了:X).

一旦附加成功,程序将在INFINITE LOOP中 RUNNING ,需要按F12 to pause the去暂停程序,但是等一下,在做这个之前,我们先得把 EB FE改回原始值(那些我叫你记下的值,你有记下吗?) 55 8B,然后我们将停在OEP,已经准备好dump了.

我们已经准备好dump了,所以打开LORD PE然后找Getright.exe

选择Getright.exe进程然后选择INTELLIDUMP然后点击DUMP FULL, 保存后启动PEEDITOR (从www.google.com上搜索它)然后写入正确的ENTRY POINT值.
在这儿不要填写538540,而写入文件偏移,就像这样: 538540-400000 = 138540.

那就是full dump.在本参考教程的第二部分我们会学习怎样去找magic jump从而完成import table, 修复IAT,使程序运行,因为它设下了一些陷阱来避免被unpack.

现在我们保存转储(dump)文件(我把它命名为tute.exe),和原始的一样,只是IAT不同.

第二部分再见.

Ricardo Narvaja

翻译(西班牙语翻译为英语)DEGETE和Tenshin,(英语翻译为中文)FTBirthday.自由传播,但是请保留作者的姓名.


第二部分
在本参考教程的第一部分我们学习了如何正确地转储(dump)Getright 5. 现在我们将要去找神奇跳转,这样IAT会被正确地转储下来,而不用手工修复了. 要完成这一点,我们需要打败程序中的一些陷阱, 并使它们即使在检测到被执行脱壳时也无所作为.

让我们开始吧!

第一步:如何找到IAT的起始点和终点

在我们开始之前,先在安全的地方保存一份我们在先前第一部分教程中得到的转储文件的副本. 我将它命名为TUTE.exe记住这个名字.

用Ollydbg载入TUTE.exe. 我们将要去寻找IAT.

Image9.gif

正如我们在上图中看到的,如果我们稍加跟踪,我们很容易发现一个

CALL [xxxxxx] 或者 JMP [xxxxxx] 间接地带有它们即将跳往IAT中某处的内存地址值.在这张图中,我们可以看到 CALL [5e9d94]. 那意味着这个call将要前往这个内存地址,实际上就是IAT表的入口值.在它的右边我们可以看到"kernel32.Getversion" (黄色). 这就意味着这是一个可以在IMPORT RECONTRUCTOR上被找到的正确的入口值.

让我们看一下转储(DUMP)窗口.在转储(DUMP)窗口右击,选择"前往 表达 5E9D94".

Image10.gif

这就是看上去很不错的IAT. 正如你所看到的入口5E9D94指向77E5D142,那个值在我的机器里是API GetVersion.这个入口是正确的,此入口周围的其它一些入口也是指向类似7xxxxxxx的值.

注意在其它的机器上值7xxxxxx可能会有所不同.但是这些值都是相互类似的. 现在我们稍微往上看一看,去找此表的起始点.我们到达这里:

Image11.gif

红线标记了此表的起始点.在此线的上面没有其它任何API值了.现在我们知道表从哪儿开始了

TABLE STARTS= 5E99EC

现在到右下角转储区去找IAT表的终止点.正如我们在图中看到的有两个可能的终止点. 如果我们吃不准最好选后面那个.但是我们有更好的方法.

Image12.gif

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

Image13.gif

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

Image14.gif

到了这里

Image15.gif

稍稍往上翻一下

Image16.gif

我们看到那儿有一个使用此值的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则会有很多入口无法解决.这就是我们为什么请出哈里波特带来一些魔法.

Image17.gif

正如在表中看到的,还存在一些(黄色标记)没有解决的入口.通常对付这种情况的方法是从我开始转储起就跟踪,然后追踪每一个调用API的call,写下名字最后用Import Reconstructor.

如果自由很少几处没有解决的入口这样做将非常简便,但是如果有很多的话,这项工作将令人难以忍受,所以让我们看看另一种方法.

第三步:如何去找出MAGIC JUMP 

这里说明一点:我们已经知道程序的父进程和他的子进程是相同文件,但要用不同的句柄装入两次,所以他们变成两个不同的进程。一方面父进程的OEP是5F90B9,父进程从这里开始运行.

Image18.gif

另一方面我们知道当子进程被转储时其OEP是534E90,但我也要告诉你,子进程也是以相同的父进程的入口点5F80B9开始运行.子进程运行并且解出自己的IAT然后跳到子进程引起错误的OEP,我希望你记住这一点,我在第一部分曾经讲过.

如果你相信以上所说,我告诉你,跟踪父进程直到父进程开始转变为两个进程,父进程和子进程,找到那个地方,那非常有用.我已经找到了那个地方,那是个条件跳转,至于如何找到的,我以后再告诉你.

顺便一提它不是从父进程那复制 IAT 到它的子进程,却是从子进程那解出自己的 IAT。这应该是一大麻烦,因为我们不能在他从父进程脱离前用 OllyDbg 进入子进程。

这是个烦恼,因为我不知道如何进入子进程去观察它如何解出自己的IAT.

首先我尝试着从转储程序TUTE.exe中找一个错误的entry.

Image19.gif

我选择了5E9C34但是你可以选择其它任何错误的entry.正如你所看到的,它的值为DF5070.

按老规矩记住要将 IsDebuggerPresent 的值设为零,清除所有以前的 BPX、BP WaitForDebugEvent 然后单击运行.

我们将停在此断点或者某个例外上.那时,打开PUPE选择进程中2个进程中的上面一个(因为他是子进程,是我们需要的).右击进入parcheando窗口选择4字节,如图在地址里填入错误的entry offset 5E9C34.然后点击BUSCAR. 你将会看到没什么变化,因为所有值都是零.

Image20.gif

然后不断的在OD中点击运行、在PUPE中点击BUSCAR,并检查字节窗口中的值.

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

Image21.gif

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

Image22.gif

 

子进程已经完全解出并赋值IAT. 我们必须在那个值第一次变化之后子进程赋值之前进入子进程. 这是个问题,但我耍了一个把戏解决了.

我们将要重复上述过程.关闭Parcheando--PUPE的窗口,因为当重启后进程的句柄会变.

重复上述过程直到你在PUPE的窗口中观察到那个值的第一次变化.你应该看到类似这样的东西:

Image21.gif

 

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

Image23.gif

右击VIEW-NAMES查找api GetProcAddress

Image24.gif

 

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

Image25.gif

在那儿现在子进程将会处于死循环中,我们可以说它已经睡着了. 我们现在必须把它从它的父进程那儿unhook下来.

在父进程窗口任意处点击鼠标右键,选择"新建起源"然后写下下面的代码

PUSH (son’shandle)

Call DebugActiveProcessStop

Nop (在这儿BPX并检查是否EAX=1)

Image26.gif

在0042F00A 下中断,然后运行一下停在0042F00A 处。看看寄存器窗口的EAX值,如果这个值=01就表示子进程和父进程分离了。如果=00那么可能是子进程的句柄填错了, 你可以在下面重新写入代码再次运行,直到EAX=01时就可以关闭OllyDbg了(杀掉父进程!)然后就可以进入子进程了。

重新运行Ollydbg(不要加载程序)附加上子进程.

再次在PUPE中恢复原来的代码 55 8B并按 "Parchear" 再看看程序中的代码又还原成了77E5B332.

Image27.gif

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

Image28.gif

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

Image29.gif

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

Image30.gif

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

Image31.gif

回到Getright.exe (第一个ollydbg)看一下堆栈. 那儿的API被Df4cB2处的called调用并将会返回到DF4cB8. 

在主窗口中选择GOTO EXPRESSION 0Df4cb8.

Image32.gif

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

Image33.gif

点击RUN

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

Image34.gif

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

Image35.gif

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

Image36.gif

你可以看到这是关键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

它必须永远跳转,所以我们做如图修改:

Image37.gif

清除BPX点击run. (记住清除hardware bpx). 当程序走到我们修改为死循环的指令那儿时输入表已经修复了,再也没有坏值了. 然后它在子进程的OEP处停下.在纸上写下magic jump的值(EB 0C),记住.

我们将要重复那些步骤.当我们走到上次我们unhook 子进程那一步时,我们将要在magic jump处设置一个 infinite loop代替在API上设置.

Image38.gif

在那儿从父进程处unhook子进程,然后它将在magic jump处死循环.

附加子进程,按 RUN, 然后停止(F12). 它将停在magic jump.

Image39.gif

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

Image40.gif

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

Image41.gif

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

Image42.gif

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

Image43.gif

现在我们要用一下Import Reconstructor.

打开Import Reconstructor在"Attach to an Active Process"对话框选择这个进程.

Image44.gif

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

Image45.gif

如图在IMPORT RECONSTRUCTOR中那些值是这样计算的:你在纸上写下的值-Image Base. 比如: OEP=534E90, Image Base=400000, 534E90- 400000= 134E90. RVA也一样 (表的起始地址值).

现在按GET IMPORTS. 所有的entries都修复了.

Image46.gif

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

Image47.gif

它将被保存为TUTE_.exe.原先的文件保持原样.

到此armadillo保护已经被击败了.

第四步:如何击败BOSS

如果我们尝试运行TUTE_.exe它会无故终止.上面我刚说已经击败了armadillo,但是脱壳文件不能正常运行.见鬼了?

程序员(bad guys)不想让getright 5在我们的机器中自由运行、解压,然后他们做了手脚.

在OLLYDBG中载入tute_.exe. 

运行,你会发现它突然终止.

BPX GetEnvironmentVariableA 当它中断时我们需要改变条件跳转,当从API返回程序时.

T看下图.

Image48.gif

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

Image49.gif

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

修改跳转.

Image50.gif

依次类推.

Image51.gif

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

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

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