本帖最后由 yysniper 于 2015-4-26 21:49 编辑
翻译说明:
1、本教程在 52 破解论坛 及 看雪论坛 全球同步首发!
2、本教程翻译自国外的The Legend of Random的系列教程,英文原文地址: http://thelegendofrandom.com/blog/sample-page。本翻译教程只是为了给不愿看英文教程以及英文水平不好的人提供方便,同时也是自己的学习过程。该教程对英文水平要求不是很高,不过个人水平有限,有些地方翻译不准的请批评指正。
3、本翻译教程请勿用于商业用途。另,转载请注明!!!
4、感谢The Legend of Random!
发帖说明:
根据译者的时间安排,快的话2-3天会上传一章,慢的话就不造了。如果有段时间没更新请勿怪,因为译者的工作性质,有时是接触不到网络的。
其他章节:
第八章:参考引用框架 一、简介 我们现在要研究的crackme,相比来说更具挑战性。它就是Crackme3.exe。咱们也会学习几个新技巧。 你可以在 教程页下载相关文件以及本文的PDF格式版本。 二、探究二进制文件 启动Olly并载入crackme。它会载入、分析并暂停在第一行:(p1)
1
运行下程序看看什么样:(p2)
2
好吧,没啥东西。选择 “Help”->”Register”:(p3)
3
现在咱们来到了某个地方。奇怪了,怎么和我们的FAKE那么像。试着输入用户名和序列号看看程序有什么反应:(p4)
4
嗯。这回弹出了一个显示坏消息的对话框:(p5)
5
有时候,对于一个比较小的程序,我喜欢向下多翻几页看看有没有什么有意思的东西。我向下翻了大概6页,然后我看到了一些相当有趣的东西:(p6)
6
看看在MessageBoxA函数前面的文本。如果你往MessageBoxA函数上面的文本的左边看的话,你会看到一条黑线将函数的参数框起来:(p7)
7
Olly给你显示的是准备传递给函数的参数,就是被调用的那个函数的。本例中个,参数1是窗口的类型,参数2是窗口的标题(“Good work!”),参数3是窗口显示的文本(“Great work...”),参数4是窗口所有者的句柄。最后,MessageBoxA函数被调用。你可以在MessageBoxA上右键,选择“Help on symbolic names”来查看传递给函数的参数以及返回值。 现在,我们对比着看下紧随其后的那部分:(p8)
8
对这两个函数的调用有很大的不同。一个看起来真的不错,而另一个却不是。我想我们大家都承认,我们宁愿要第一个调用。现在咱们要记住 R4ndom's Essential Truths About Reversing Data #2: R4ndom关于逆向数据的必备真言2:
2.大部分的保护机制都可以被绕过,通过修改一个简单的跳转指令来跳转到“好的”代码处,而不是“坏的”代码处(或者避免跳转跳过“好的”代码)。 如果你看两个函数的上面几行,你会看到几个jmp语句,它们决定了你将走哪条路,好的路或者坏的路。99%的应用里的99%的时间都是这样。窍门就是找到这个跳转。(当然剩下的1%要难得多,不过我们不会接触。)我们的例子中,在401344和40134B有跳转。现在,作为一个已经训练过的逆向工程师,这些跳转很快就被略过(如果你想知道为啥,是因为它们和我们的消息框在不同的函数中,所以它们不会跳过我们的坏消息或跳转至好消息处,后面会讨论这个)。咱们来研究研究它们:(p9)
9
首先,点一下40134B处的JMP指令。会看到一个红线指示该JMP将跳到哪,我们看到它走的是一条错误的路!(p10)
10
它没有跳到我们的好消息那,也没有跳过坏消息,反而往上跳到前面的代码了。我们试试401344那个JMP。这个事实上和那个指向的一样(仍然是错误的路),所以看起来我们的第一个猜测是错的。 顺便说一下,就像我早些说的,老鸟忽视这些跳转的原因是因为Olly显示函数的方式。如果你注意看第一列(地址)和第二列(操作码)之间的话,会看到一些粗黑线。这些线是Olly放进去的,用来区分函数(有时候Olly无法指出函数的起始点和结束点,所以就不会有这些线):(p11)
11
本例中,你可以看到那两个JMP是在我们的好消息和坏消息的上面的函数中。因为它不会跳转到好消息或者坏消息处,它们真的对我们没有任何帮助。这也告诉你另一件事,第一个消息框(好消息那个)和坏消息框不是在同一个函数中。这些都告诉我们,这些函数都是在别的地方被调用的,并且在它们被调用之前的某处决定了哪个被调用,是好的还是坏的。咱们看看怎么才能绕过这些干扰...... 三、查找参考 在好消息函数的第一行,也就是40134D那行上右键。选择“Find References To”->”Selected Command”(或者按Ctrl+R):(p12)
12
弹出“References(参考)”窗口:(p13)
13
该窗口显示的是Olly能够找到的CALL或JMP到*这个*地址的所有参考(CALL和JMP)。现在,双击列表中的第一个(就是那个不是红色的),然后你就会来到调用这个(好的)消息的那行:(p14)
14
在40124C那行你可以看到指令CALL CRACKME.0040134D。40134D就是好消息对话框的第一行。咱们在这里设置一个断点:(p15)
15
现在,咱们对另一个函数做相同的操作,也就是坏消息那个。转到401362那行,就是坏消息函数的第一行,右键选择“Find References To”->“Selection (or ctrl-R)”。这会再一次调出参考窗口。双击第一条,我们就会来到调用坏消息的地方:(p16)
16
有意思的是,它就在我们刚才设置的断点的上面2行!咱们在这里也设置一个断点:(p17)
17
***注意,有时候你选中一行然后查找参考,但是一个都没有。导致这个结果的原因有两种:1)你选择了错误的函数“入口点”,也就是调用这个函数应该call或jump其他的地方,但是它们却调用了别的行,有可能就是你选择行的前面或后面那行。选择正确的行来查找参考需要花时间和技巧,不过要坚持下去。2)代码中没有明显指向这一行的指令。记住,程序运行时有许多数字被动态的操纵,call或jump指向的地址也不例外。所以,如果call的地址是动态创建的话,所以Olly就没有办法提前知道这行会被调用,所以Olly也就不会将这个参考列出来。关于这个也是有方法的,不过这会我不打算讨论。 现在,如果我们看看这两个CALL的附件的话,会看到几个jmp指令。第一个,401243的 JE SHORT CRACKME.0040124C。当然,你知道JE是啥意思,因为你已经读过汇编语言的书(参见R.E.T.A.R.D. 规则#1),不过为了证实,我们假定你不知道这个特别的助记符(指令)是啥意思。这就是插件MnemonicHelp存在的原因。右键JE指令,在上下文菜单中选择“? JE”:(p18)
18
这个窗口比较长,因为有大量的跳转指令。如果我们向下看那个“JE”的话,会看到它是“Jump if Equal (ZF = 1)”。意思是如果0标志位被置1就跳转(或者被比较的两个项目相等)。前面的教程中我们复习过标志位,所以你应该知道,如果被比较的两个对象相等,JE就会跳转。我们也能够发现,这个JE跳过了对坏消息的调用,并且紧随跳转的那条指令是对好消息的调用。如果JE没有跳,我们就会调用坏消息。所以,我们想要这个跳转实现,以便我们能够调用好消息。咱们操作下看看。在JE指令上设置一个断点,重启(或运行)应用。点击crackme中的“Help”->”Register”,输入用户名和序列号,然后点OK:(p19)
19
哇!等等!显示了坏消息,并且Olly也没有断下来?也就是说Olly永远也不会运行到我们的断点!这是咋回事呢。 事实上,这个在逆向工程领域里是可以用得着的。我向你保证,每一个专家级逆向工程师/破解者这时候都会想“我错过什么了吗?一个0xcc中断?IsDebuggerPresent(译者注:一个Windows API)?NTFlags?TLS回调?”,然后白费力气去寻找一些过于复杂的解决方案。但是我们只是初学者,我们只有几个工具可以使用,其中一个就是搜索字符串,那就试试这个吧:(p20)
20
现在,你可以看到一些相当有趣的东西...。有两个“No luck!”坏消息,但是只有一个好消息。也就是说,代码中的某个地方做了检查,如果没有通过就会显示坏消息。这是一个在反逆向工程中非常流行的技术:找一个非常明显的地方放好的/坏的消息,然后添加一个不是那么明显的检测。如果你看看代码窗口我们的好消息和坏消息所在的位置,你会发现字符串“No luck!”是在40136B,所以我们知道那不是我们要找的字符串。所以咱们双击下4013AF那个:(p21)
21
这个坏消息是在程序内存中完全不同的区!我认为这个crackme是在太简单了!好,咱们深呼吸然后想想RETARD 规则 #2,找找 比较/跳转。本例中在4013AA处有个JMP,当你点击它的时候,Olly会显示一个箭头刚好跳过了坏消息。看起来前途光明啊...。那就试试吧!在那个jmp指令处设置一个断点,重启应用并运行。 ***你有可能得到错误的消息,就像我们上一章中断点被破坏那样。如果发生了,像上一次那样做就行了。打开BP窗口,在你运行程序前重新启用所有的断点:)(p22)
22
操蛋!!!好吧,不起作用,所以我猜我们得深入挖掘了。咱们看看代码,试试理解到底是什么个情况(该是你组合阅读大放异彩的时候了):(p23)
23
好,我们知道了一件事,因为教程的前面我们学过,就是函数的开始和结束点。图片中你能通过蓝色箭头看到。所以,从函数的起始点开始,有一个循环首先检查AL是不是0( TEXT AL, AL),然后循环将AL和一组数值(41,5a)进行比较。期间,有一些依赖于AL值得跳转。首先,咱们看看到底哪个跳转会调用我们的坏消息(有一个JMP指令刚好在坏消息前面,没有什么可以“空降”直达它。所以,必须有什么东西跳过那个跳转来运行坏消息代码。最有可能的跳转是在4013AC)。 点一下4013AC,也就是坏消息框的第一条指令,右键该行选择 “Find References To” -> “Selected Address”。(我知道一旦你点了这行,就会显示一个红色箭头,显示了哪条指令调用了它,但是我们怎么才能知道就没有别的指令调用坏消息呢。找到所有的参考可以帮助我们确定,有可能只有一个。)然后我们就再次看到了参考窗口:(p24)
25
现在,双击第一个,咱们来看看哪一行正在调用坏消息:(p25) 噢,原来是循环中的一个。注意参考窗口中旁边的那个红色的那行(我们现在可以忽略它),只有一个到该地址的参考,所以我们可以保证40138B这行是调用坏消息的唯一代码。所以40138B的JB SHORT 4013AC就是那个罪魁祸首。咱们试着在它上面设置一个BP,临时修改下看看能否绕过这个坏消息。在40138B设断点,重新运行程序:(p26)
26
嗯。箭头是灰色的,我们知道在这次的循环迭代中我们没有跳到坏消息那。按下F9执行循环体:(p27)
27
啊!第二次循环时它就要调用坏消息了。好,就让它那么干,看看我们跟踪的对不对。你可能注意到了,如果修改了0标志位,跳转仍然实现了。这是因为JB指令是跳转指令集中略有不同的那部分,它用进位标志位而不是0标志位(别担心,这些你的汇编语言书籍中全都有file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wpsE5CE.tmp.jpg)。所以双击那个进位标志位(“C”):(p28)
28
然后那个箭头就会变为灰色:(p29)
29
现在我们再次运行循环,看看循环中还有没有调用坏消息的。按5次F9,没有一次调用坏消息。事实上,在第五次F9之后,我断在了一个旧断点处,我们首先想到的是补丁:(p30)
30
那么,这就意味着我们已经成功的绕过了对坏消息的第一个检测,并且回到了原来的检测点。咱们给第一个检测打个补丁,这样就再也不用操心它了,就可以将注意力集中在主要的检测点。回到40138B的断点处,我们得想想怎么给它打补丁而不让它跳转到坏消息那。记住,跳转是在第二次循环时实现的,只有在AL的值小于41时才成立(相关指令是CMP AL, 31, JB SHORT 4013AC)。如果我们只NOP掉这个跳转会怎么样?它就再也不会跳了,我们一点也不用担心它会跳到坏消息那:(p31)
31
32
右键,选择“Copy to executable” -> “All modifications”。弹出内存窗口,右键该窗口,选择“Save File”,将其另存为crackme_patch1.exe:(p33)
33
现在,在重新载入刚刚打过补丁版本前,我们要明白所有的补丁、注释和(尤其是)断点都会被删除,因为所有的信息都存储在Crackme3.udd这个UDD文件中。我们将要打开的Crackme3_Patch1,并没有和它相关的UDD文件。不过还是有几个好消息的。本文的相关下载中包含有断点管理插件。如果你没有准备好,那就将其拷贝到你的插件目录下,然后重启Olly。如果你一开始就安装好了,那你就已经载入了它。现在打开断点窗口,右键并选择“Breakpoint manager”->”Export Breakpoints”:(p34)
34
保存文件,因为我们将会将其导入到新文件。现在,将新文件(打过补丁的)载入Olly。它很可能会弹出一个消息框,告诉你断点被破坏了:(p35)
35
点OK就行了。在我们的打过补丁的程序中打开断点窗口,很可能所有的(或大部分)断点消息了。现在,右键并选择“Breakpoint Manager” -> “Import breakpoints”:(p36)
36
现在你会看到我们原来的断点又回来了:(p37)
37
运行程序,Olly会断在我们的第一个断点也就是401243的JE指令处(如果你没有在该行设置断点,那就设一个。译者注:吐一下槽,原作者太操蛋,从来都只打圆括号的左半个,右半个就不管了,我还得自己琢磨把右边的圆括号放哪)。重启应用并运行,你会断在这里:(p38)
38
现在,看那个灰色的箭头,就是从当前暂停行往下到40124C。因为箭头是灰色的,所以跳转不会实现。你也可以看反汇编窗口与数据窗口中间的那块,它告诉你跳转没有实现:(p39)
39
这意味着,什么都不用做,程序不会跳到第二个调用,会直达第一个调用。第一个调用会跳到坏消息那,所以我们真心不想让它发生。按一下F8单步步过。就像Olly告诉我们的,没有跳转,我们当前的位置就在调用坏消息的地方。按一下F7单步步入那个CALL,我们来到了坏消息所在函数的第一条指令处。现在,如果我们按F9让程序运行,我们看到的正是意料之中的:(p40)
40
咱们来看看能不能解决这个。重启应用,按F9运行之,选择“Help”->”Register”,然后输入用户名和序列号。现在,当你按下OK按钮时,Olly会再次停在我们的第一个断点:(p41)
41
这回,咱们来帮助Olly走正确的路。浏览下寄存器窗口,注意到Z标志位是红色的。嗯,你知道该怎么做了:(p42)
42
注意,箭头是灰色的,显示跳转不会发生,不过现在变红了,在反汇编窗口和数据窗口之间的那个区域已经变成了“Jump will be taken(跳转将会发生)”。我们所做的就是告诉Olly去修改标志位,该标志位用来判定两个东西是否相同,为了让它认为它们是相同的。所以现在,我们会跳过对坏消息的调用,转而去调用好消息!!! 咱们试试。按F8执行跳转,再按F7单步步入到那个CALL。现在我们将跳转到好消息的起始处:(p43)
43
现在,按几次F8,每按一次就观察一下堆栈窗口。你会看到MessageBoxA的参数被压入堆栈,本例中确实是好消息被压入堆栈。只要你单步步过40135C处的函数,新的消息对话框就会显示出来。我们已经破解了我们的第一个程序!!!(p44)
44
现在的问题是,我们只是临时性的修改了标志寄存器,当程序再次运行时,它却不会再次的修改标志位,所以我们还是会得到坏消息。我们需要做的是通过某种方式将修改保存起来,以便于每一次程序运行的时候,我们都能强制它做跳转。这时候补丁要派上用场了。和我们以前做的一样:选中所有已修改的行,右键并选择 “Copy to executable”。在弹出的窗口中右键,点击“Save file”。选一个名字,这就是你的打过补丁的版本:(p45)
45
现在可以关掉数据窗口和Olly了。打开你保存的打补丁版本文件夹,运行打过补丁的程序。输入你的信息并验证:(p46)
46
干的漂亮!你已经真正的破解了一个有些挑战的crackme。
本文PDF文件下载(已排版): 本文相关附件下载地址(国外链接,不是一直好用): |