翻译说明:
1、本教程在 52 破解论坛 及 看雪论坛 全球同步首发!
2、本教程翻译自国外的The Legend of Random的系列教程,英文原文地址: http://thelegendofrandom.com/blog/sample-page。本翻译教程只是为了给不愿看英文教程以及英文水平不好的人提供方便,同时也是自己的学习过程。该教程对英文水平要求不是很高,不过个人水平有限,有些地方翻译不准的请批评指正。
3、本翻译教程请勿用于商业用途。另,转载请注明!!!
4、感谢The Legend of Random!
发帖说明:
根据译者的时间安排,快的话2-3天会上传一章,慢的话就不造了。如果有段时间没更新请勿怪,因为译者的工作性质,有时是接触不到网络的。
其他章节:
第十三章:破解一个真正的程序 一、简介 本章我们打算不训练了,咱们来破解一个真正的程序。这个程序有个时间限制,过了这个时间,这个程序就不能用了。我们准备给它打补丁,让它认为是注册过的。目标文件在下载中有(我没有提及程序的名字,因为教程的目的不是为了拿到一个“破解版”程序,只是为了学习)。与所有的商业软件一样,如果你真的打算用它们,你真的应该考虑购买它。人们在软件中投入了大量的时间,他们应该得到补偿。为了不让这个系列教程成为关于“获得破解版软件”的东东,我试着找了一个没有人真想要的程序,所以我下载了这个软件,它是上周Download.com中拥有最少下载量的软件。作为一个完全诚实的人,在本章中破解了这个程序以后,我很喜欢这个程序,所以我买了一个注册码,现在我心安理得的用它(译者注:作者真是活雷锋,其实咱们都是搞技术的,或多或少都写过代码,尊重软件作者,为他们的劳动付费,其实就是尊重自己。实在不愿意花钱的,就用免费替代软件行了,我一般喜欢用开源免费软件。多说了几句哈)。只是告诉你,你不能通过下载量来判断一个应用。 好,咱们继续...。 二、研究该应用 首先安装软件。安装完成后,会弹出下面这个窗口:(p1) 让“Run the app”保持勾选状态,看看会遇到什么:(p2) 好吧,看起来不是很好啊。我们注意到这里有几个字符串可能会有帮助,“unregistered”、“evaluation”、“registered”等等。点OK,然后弹出主界面:(p3) 注意,标题栏中显示的是“unregistered”。通常,我注意一个程序的另一个地方就是它的关于对话框。它通常都包含有字符串,以及用于逆向的思路。这时候,我们寻找关键字、可识别的方法调用,以及类似的东西。这样的工作你做的越多,就会有更多的线索。(p4) 这里我们又看见了“unregistered”。我通常做的下一件事是,找找看有没有什么地方用来输入注册码。如果“搜索字符串”这招不好用的话,那么对于渗透来说这是一个好入手点:(p5) 下面是输入注册码的地方:(p6)
输入一个试试,看看什么情况:(p7) 点击OK:(p8) 唉!我好像从来就没有输对过。好吧,对于我们当前搜集到的信息来说,我们有一个相当好的方法,Olly载入程序:(p9) 你可能已经注意到了,这看起来和我们已经见过的大部分应用有点不一样。看起来有辣么多的CALL指令,没有那些典型的Windows设置的玩意儿(像RegisterClass...)。这是一个好的标志,说明程序是用Delphi写的。Delphi在程序中会使用大量的CALL。我们可以通过运行一个ID程序来确定,不过我打算在后面的教程中讨论。也有一些专门的工具用来处理Delphi程序,不过幸运星是本章我们不需要用专用工具(虽然我们会接触到它们)。 三、寻找补丁 试试咱们的字符串搜索。右键,选择“Search for” -> “All referenced text strings” ,将会弹出搜索窗口。滚动到顶部然后右键,选择“Search for text”: (p10) 弹出文本搜索对话框。现在我们注意到“registration”和“registered”很早就用到了,所以咱们就搜它们。通常在这种情况下,因为是第一次搜索,我会搜“regist”,因为包含了这两个单词,而且也从来没有让我失望过(我猜没有多少程序会使用单词“register”)。不要勾选“Case sensitive”,选中“Entire scope”,然后点OK:(p11) 第一个命中的看起来没啥前途,按CTRL+L继续搜:(p12) 注意,这次找到的就是我们第一次搜到的。因为第一次命中的是在字符串被压到堆栈的地方,第二次才是字符串“RegisterAutomation”在内存中的真实的数据。因为在第二列中没有指令所以可以分辨出来,反而有个ASCII字样。你遇到的大多数字符串都有两个版本,一个是字符串被访问的地方,另一个就是字符串真正所在的地方:(p13) 如果你再按一次CTRL+L,我们会遇到另一个没前途的字符串。一直按CTRL+L直到来到下面这个地方:(p14)
这回看起来好多了。它将会在程序启动过程中的某个时刻出现,它会检测我们有没有注册,然后根据检测的结果来决定窗口的标题栏显示注册还是没注册。这是我们开始干活的好地方。双击有“registered”的那行,咱们就会跳到相应的代码那:(p15) 首先我们能看到字符串是在9AABA9那,还能看到字符串存储在内存的9AABCC处。第二,要注意到是两个字符串是在同一个方法中,在它们的上面有个一个条件跳转。点击9AABA5处的条件跳转:(p16) 我们能够看到如果结果相等,就跳到“Unregistered”那里。很明显,不能让它跳。咱们在JE指令那设置一个BP,启动应用:(p17)
Olly就会断在那行,你会发现我们就要跳到坏消息那了。咱们得修改下:(p18) 运行程序。Olly会再次断在同一行,并准备跳到坏消息那。咱们再次就0标志位置0,然后运行程序。又来了一遍,清除0标志位后,我们最终得到如下的反馈:(p19)
所以那样做是不起作用的。给那个检测点打补丁不会注册成功,如果你点OK并再一次将标志位置0,你会发现主窗口的“unregistered”没有了:(p20)
那么,我们知道至少没有跟踪错。我们准备做的是步入到下一“层”,做深入的研究。重启应用,然后断在了我们的断点处,咱们再多做些研究:(p21) 在比较的前面并没有CALL,但是在JE指令的前面9AAB9E处有个比较:
CMP BYTE PTR DS:[EAX+15B8],0
所以,这个比较的结果决定了我们是注册还是未注册。EAX+15B8只是一个内存地址,在这里它是一个全局变量,因为是以DS:打头的。我们希望这是程序检测注册与否的唯一一个检测点。如果不是的话,我们就需要找出其他检测点的位置。点选比较指令,就可以看到EAX+15B8i是什么:(p22) 在地址上右键,选择“Follow in dump”:(p23) ***你的地址几乎肯定和我的不一样。这个没事。跟着教程,将我的地址替换成你的地址,一样跑的好好的。*** 这里我们能看到该地址被用于检测注册与否,就是1AC111C处(至少我这里是这样)的第一个00。意思是如果此内存位置的内容是除了0以外的任何数据,那么就假定我们已经注册。这也意味着,程序中可能有别的子程序检查该内存位置,这就是为什么主窗口显示“Registered”,而程序的其他部分知道我们没有注册。因为我们只是在检查了内存内容后绕过了这个子程序的自然流程,其他子程序的检测却没有绕过。 首先,咱们把这个内存地址设置为非0,那么我们知道至少这个子程序将会按照我们想要的方式工作。在比较那行(9AAB9E)设置一个断点,将其他断点删掉。重启应用后Olly就断下来了。在比较那行上右键,选择“Follow in dump” -> “Memory location”,因为Olly会在我们重启应用的时候重置数据窗口。你可能已经注意到了,比较指令检查的内存地址这次变了:(p24) 第一次是1AC111C,现在是01B9111C。你的和我的会不一样,你只需要注意第二次的就行,存储 已注册/未注册 标志的内存地址不同。 点击数据窗口中的“00”(在我的数据窗口中是1B9111C),右键选择“Binary”->“Edit”:(p25) 咱们输入01:(p26) 注意数据窗口中的内容已经更新了:(p27) 继续运行程序直到再一次断下来。你会发现内存中的内容又变回了0,我们将再一次跳到坏消息那。这意味着程序的某个地方,做了第二次检测并将注册与否的标志重置为0。我们需要做的就是找出在哪里重置的,确保不会再被重置。要这样做的话,在该内存位置设置一个硬件断点,当程序向该内存位置写数据时让Olly断下来。之所以选择“写”,是因为某个地方向该内存写了0。 重启应用直到它断下来。右键比较指令,选择“Follow in dump”,因为Olly又重置了数据窗口。用二进制编辑方法将第一个内存位置修改为01。注意它现在的地址又换了:(p28) 右键数据窗口中的第一个值,选择“Breakpoint”->“Hardware, on write”->“byte”:(p29) 在逆向一个程序时,我通常留在硬件断点,因为它们很难被应用检测到。我选择“byte”是因为我们想追踪的就一个字节。 运行程序。Olly会再次断在普通断点,你会发现我们输入的01仍然在那里,所以到目前为止还不错。再运行,Olly会断在一个新地方:(p30) 四、给程序打补丁 现在,咱们来研究研究这块代码。第一条指令是将DL与我们刚才编辑的内存内容进行比较,如果相等就跳到9ADC02,然后就返回了。如果不相等,就将DL的内容存储到我们编辑的内存中。我们已经知道了DL等于0,因为我们看到内存中的值从01变成了00。所以这基本上就是另一个注册检测点,并且如果它检测失败就会将 已注册/未注册 标志置0。如果成功,就什么都不做。现在咱们将硬件断点删掉,选择 “Debug” -> “Hardware breakpoints” (译者注:这里的Debug指的是菜单中的),将硬件断点删除。咱们在9ADBF4处设置另一个硬件断点,这样我们可以在该段代码运行前断下来:(p31) 你或许会纳闷,我为什么不在这里设置一个普通断点。因为我先试过了!Olly根本就不会断下来好嘛!有几个愿意可能会导致该问题的发生:这段是多态代码,所以我们的BP丢了,程序检测到软件断点所以把它删了,断点在一个Olly不会自动追踪的区块...。不管怎样,就是这么个结果。我们需要设置硬件断点而不是软件断点。不保证硬件断点一定管用,因为软件有可能专门对它们进行检测。不过它是一个更可靠的设置断点的方法,所以通常来说还是比较好用的。 ***在后面的章节中我们会更多的学习反调试技巧。*** 重启应用,我们会再次断在新的硬件断点处:(p32) 好,现在咱们来思考思考啊。这个子程序是在咱们原来的断点前面被调用。这个子程序检测我们是否注册,如果没有就将[EAX+15B8]地址处的内容设置为0,如果注册了就置为01(或者任何非0的数据)。然后我们原来的子程序被调用,就是那个在窗口标题中输出“Registered”或“Unregistered”的子程序,它也是根据内存中的数据是0还是1来决定输出。如果我们确保任何时候只要该子程序运行时那个内存位置中都是1,那么任何其他子程序来检测内存中内容时看到的都只能是1,也是就认为我们已经注册了。 如果我们只是将子程序修改成总是在内存中的合适位置放置1的话会怎么样?咱们来试试看。 下一个问题就是怎么做最简单。好,我们已经有了在9ADBFC处被用某些值(DL)填充的内存位置,所以我们只需要在上面的某个地方将DL改成1。问题是将DL改成1需要在当前指令的长度上加一个字节,这样做会覆盖RETN语句。如果我们将比较/跳转指令替换成将DL置为01的指令怎么样。那样的话,在最后一行,DL将被拷贝到我们的内存位置!下面就是我们的做法,选中比较/跳转那两行指令:(p33) 右键选择“Binary” -> “Fill with NOPs”:(p34) 然后就像下面这样:(p35) 这一步不是必须的,不过这能让你更容易的看清自己正在干啥(译者注:这样可以防止不小心多添加或少添加字节)。 现在选中9ADBF4处的NOP,按一下空格键。弹出汇编窗口,输入 MOV DL,1:(p36) 先点Assemble,然后点Cancel。结果就像下面这样:(p37)
现在,无论什么时候调用这个子程序,内存中的那个标志位(译者注:这里的这个标志位和寄存器的标志位不一样啊,读者要能够分辨的了)都会被置1而不是0。因为我们仍然暂停在子程序的第一行,所以你可以单步执行以观察DL被置1的过程,然后1就被存入内存中(你可能需要到数据窗口的合适的地址观察,因为Olly很可能再一次将数据窗口重置过)。现在运行程序,Olly会断在我们原来的断点:(p38) 我们能看到我们将直达正确的字符串。继续运行,我们将断在我们修改过的注册检测子程序,它会如愿的放一个01到我们的内存地址中。这会回头再往前这样来回几次直到最后:(p39) 现在我们注册成功了!!!继续运行程序(打开一个demo文件),Olly会在注册子程序中断下来几次,不过每次它都会走正确的路。不久你就会看到主窗口:(p40) 你会看到我们仍然是已注册状态。点击显示关于对话框:(p41) 恭喜你!你已经成功的完成了第一次破解。 别忘了将它保存到磁盘。打开硬件断点窗口(“Debug” -> “Hardware breakpoints”),点断点边上的Follow按钮。然后我们就来到了我们打补丁的地方。选中所有我们修改过的行,右键选择“Copy to executable”。在弹出的窗口中右键,选择“Save to disk”。以原来的名字保存它。现在退出Olly,运行程序体验它的全部,以及注册成功的骄傲与自豪!!!
本文PDF文件下载(已排版): 本文相关附件下载地址(国外链接,不是一直好用):
|