第十一章:用NOOB技术破解
一、简介
本章我们将再次讨论补丁程序,不过比典型的单个“我们遇到的第一个补丁”要深入一点点。我们将从一个控制台程序开始,找到隐藏在其中的正确密码。教程的相关下载中有。除此之外,你只需要
OllyDbg。
那么,咱们开始吧...。
控制台程序是和其他windows下32位的程序一样。唯一的不同是它们不使用图形界面。除此之外,它们是一样的。此次的crackme叫CrackmeConsole.exe。咱们来运行一下看看情况:(p1)
好,看起来挺简单得。咱们来随便输个密码:(p2)
真失败!按“N”结束程序吧:(p3)
好吧,我觉得至少我们有了足够的信息来开始研究它。GO,Olly载入应用。开始,首先搜索字符串:(p4)
不是很难嘛!双击坏消息“I’m sorry, but that is wrong”,至少来到了正确的地方:(p5)
好,咱们研究研究这个。我们看到一个从4025C6来的跳转,有红色箭头标出来了。我们也注意到,如果4025D5的JE指令没实现的话,我们也会得到坏消息。咱们来看看如果这个跳转实现的话会怎么样。点它:(p6)
滚动到它指向的地方(在下面几页):(p7)
这看起来就是我们想走的路。咱们回到上面看看周围地方:(p8)
4025D5是调到好消息那的,这就是我们想要实现的跳转。咱们点一下另一个跳转看看它将跳到哪去...。说不定前面也有个跳转可以跳到好消息那呢:(p9)
这个是到坏消息的:(p10)
这个也是,如果你接着点那些跳转指令,你会发现4025D5是唯一一个跳到好消息的跳转。所以基本上,我们要阻止所有跳到坏消息的跳转实现,强制跳到好消息的跳转成功跳转。如果我们接着往上滚动,就会在402582找到第一个 call/compare(调用/比较) 指令:(p11)
再往上滚动,就会发现有个跳转跳过了那个CALL,但是仍然进行了比较:(p12)
这个行为不太正常,如果我们再往上滚动一点,就会发现另外一对 调用/比较 指令对。我在这两个CALL上都设置了BP:(p13)
好吧,咱们继续,在Olly中运行程序看看会发生什么。我将输入密码“12121212”:(p14)
Olly断在了第一个CALL:(p15)
单步调试,注意42056F处的跳转跳过了第二个CALL。嗯,这倒给了我们一个提示,第二个跳转可能不是校验密码的,不过有可能是某种验证程序,如果我们的密码不符合某种规则,比如太短或者太长?不管是啥,咱们接着单步运行就行了:(p16)
4025C6这里,咱们看到了罪魁祸首了,就是它跳到了坏消息那:(p17)
咱们设置下0标志位,看看会怎么样:(p18)
继续单步,终于和跳到好消息的跳转碰头了,注意它实现了:(p19)
继续运行程序,我们发现我们已经找到了第一个潜在的补丁:(p20)
现在,给我们刚才设置0标志位的那个跳转打上补丁,这可能有用也可能不起作用。这很难说。如果我们的密码太短会怎样?太长呢?是不同于我们所输入的密码的(译者注:大概这个意思,我没弄明白作者啥意思。原文是A different password than the one entered)。这个补丁不是一个非常好的补丁,因为我们真的不知道我们到底做了什么,我们只知道在这种情况下会起作用。
二、深入挖掘
咱们靠近点看看这段代码,用上一章我学到的级别,试试不那么LAME的方法。向上滚动到我们打过补丁的那个跳转,就是跳到坏消息的那个,咱们来试试看找出为什么我们没打补丁时它会跳转。注意,我已经在跳转那加了一个注释,这样后面比较容易记住(回想下,选中该行,按一下“;”来添加注释):
我们通常在注释前加上“###”以示区别,这样的话在将来,当用其他的工具来向我们显示注释的时候,就更容易找到我自己得注释,因为它们比较突出。当然你也可以按自己喜欢的方式做。
现在,咱们就来看看跳转的上面,看能否找到是什么让它跳转的。我在下面已经标记出了跳转上面的第一个区块:
我们能看到有几个SBB指令和一个比较指令。对于我们来说,这里的这段代码并不真正有什么意义,因为我们不知道它是干啥的,所以咱们网上看下一个区,看看咱们能不能开始对它有所了解:(p23)
好,这里我们将会到达某个地方。可能你注意到第一个问题的是 REPE CMPS 指令。这是逆向工程的一个红色标志(译者注:原文是This is a red flag in reverse engineering! ,我不知道作者是啥意思,就直译了)!咱们查查REPE看看是啥意思:(p24)
这个不是非常的清楚,不过如果你对汇编语言稍有经验的话,就知道REPXX语句像循环一样重复直至ECX=0。REPXX后面的指令,这里是CMPS,就是重复的内容。放在一块的话,这个语句就是“当0标志位保持不变时,重复比较两个内存地址的内存,每循环一次就增加一次地址大小”。简而言之,就是“比较两个字符串”。在逆向工程领域,任何时候我们比较两个字符串,红色标志都应该消失。应用程序不会经常这么做,校验序列号/密码/注册码只是多次比较中的一个。咱们在该区块的第一行也就是4025B5处设置一个BP,并重启应用。输入咱们的密码,然后Olly断了下来:(p25)
现在,注意第一条指令,LEA ESI, DWORD PTR SS:[ESP+34],准备将一个栈中得有效地址载入到ESI中。SS:表示堆栈,[ESP+34]表示的是栈中的位置,本例中是ESP所指向位置前面的第34字节。LEA指令意思是取地址,而不是取内容。如果我们看那个中间区域(就是蓝色箭头指向的地方),可以发现SS:[ESP+34]等于地址0012FE88,在这个地址存储的是我们的ASCII形式的密码。单步步过该行,可以看到ESI被设置成我们的密码(当前是在栈上):(p26)
下一条指令将EAX设置为0,然后就是REPE指令。本例中,是将存储在ESI中的地址的内容与存储在EDI中的地址中的内容进行比较:(p27)
ECX寄存器减一,比较就转到ESI和EDI的下一个内存位置,当ECX=0时循环结束。本例中,如果你往上看,会发现ECX被置为8(就是我们密码的长度),所以该循环会遍历我们密码的8个数字,每一次将一个数字与EDI中相关的数字进行比较。不过,等等...,我们正和谁比较呢?如果我们再看看寄存器窗口,我们会发现EDI指向的是堆栈中的一个地址,其中存储着几个ASCII字符7。咱们到堆栈中看看。点击挨着EDI的那个地址,在其上右键选择“Follow in stack(堆栈中跟随)”:(p28)
堆栈窗口立即就跳转到相关地址处,也就是0012FE6C处。在该地址(我们不能不注意到后面的也是一样)我们看到一串“37”。查查ASCII码表就知道37就是“7”,就是我们在寄存器窗口中看到的EDI寄存器中的内容:(p29)
好吧,不需要像外科医生那样就能够发现我们输入的密码正在和硬编码的全是“7”的字符串进行比较。堆栈中真切的只有8个“7”(很走运,我们输入的密码正好和硬编码密码的长度相同)。这八个“7”与我们输入的密码一个一个的进行比较。如果所有的8个都相等(也就是等于7),我们就会执行下一个跳转。嗯...,我们输入的密码被拿来和8个“7”进行比较。给我的感觉就是密码可能就是八个“7”。咱们来重启应用试试看:(p30)
此处应该有掌声...。(p31)
我们拿到了。所以,在我们通常打补丁的地方的稍远处我们发现了密码,坦白的说这比给一个程序打补丁要好的多,因为我们不知道是真的打上了还是没有。相比LAME级别,这就是NOOB级别补丁的好处。
三、最后一件事
我只是想举个例子,是分析代码及对代码进行注释。不幸的是,在写教程时,你需要在相当深的层次上理解相关应用。下面是核心区块的图片,我在其中加了注释:(p32)
如你所见,很多都是对应用程序工作方式的理解。
本文PDF文件下载(已排版):
本文相关附件下载地址(国外链接,不是一直好用):