使用OllyDbg从零开始Cracking 第九章-基本概念
第九章-基本概念本章我们将开始逆向。我们先从基本的概念开始一步步的来介绍逆向所需要的步骤。本次实验依然是我们熟悉的Cruehead`a的CrackMe,但是不要把自己局限于逆向这个简单的CrackMe的不同方式-在此过程中,我们将介绍适用于更加复杂的软件的一些标准方法。让我们用调试器开始逆向之旅吧。通过这个CrackMe我们可以掌握一些基本的概念。入口点:程序刚刚被加载第一条指令的地址。为了不和OEP(原始入口点)相混淆,我们稍后再来介绍OEP的概念。当用OD加载应用程序后,调试器就会停在入口点处,分析代码并且等待用户的进一步提示。对于Cruehead`a的CrackMe这个程序来说,入口点为401000。通常情况下,状态栏会显示调试器暂停的原因。现在就提示我们,当前是入口点:大部分(99%)的程序启动的时候都会停在入口点处。还有一些程序通过一些修改方式让其在启动的时候不停在入口点处。这些方法我们将在后面讨论。这些方法就是我们常说的反调试。接下来,让我们来了解一下DLL(动态链接库)的概念以及DLL导出函数的功能。注意一下突出显示的部分,举个例子,比如我们要调用401020或者要跳转到421367,这里会是外部函数的名称替代了这个绝对地址。CALL LoadIconA在最右边的列中显示了一些额外的信息,调用的是LoadIconA。Windows操作系统支持的所谓的动态链接库(扩展名为DLL文件),它们与正常的可执行文件EXE具有相同的格式。动态链接库可导出函数供其他可执行文件(EXE和DLL)调用。不是在多个可执行文件中有相同的静态副本,而是把功能放置在DLL中。如果一个功能的代码量很大,那么这样就可以缩减可执行文件的大小,更重要的是可以节省内存。Windows的基本功能:文件,内存,进程,线程,图形,声音,网络等都是在标准的动态链接库中实现的。LoadIconA是在User32.dll中实现的一个加载位图的应用程序接口。应用程序接口也称之为API。让我们来看看另一个APIMessageBoxA的例子。在命令栏中输入: ?MessageBoxA有个简单的提示,该函数的地址是77D504EA。我们单击鼠标右键选择-Goto-Expression输入这个地址。我机器上面提示的地址可能和你机器上面的地址不一样。如果你是windows 9x的话,就不能这样做了,我们后面会讲到。可以看到该函数属于USER32.DLL,通常在调试器中显示的函数名称前面会有DLL的名称(例如Call USER32.MessageBoxA)。从当前地址开始到函数返回实现了MessageBoxA的功能。MessageBoxA便于我们的识别,但并不是机器码,给逆向者提供了一定的帮助。想要返回到前一条指令处的话,只需要按一下减号键。转到MessageBoxA的函数实现处,也可以直接单击鼠标右键选择Goto-Expression输入MessageBoxA。这里我们又进入了USER32.DLL的MessageBoxA函数中。该函数名称是区分大小写的(这里名称是MessageBoxA而不是messageboxa)。按减号-回到入口点。
单击鼠标右键:选择Search for-Name(label)in currentmodule Ctrl+N菜单项。获取该CrackMe的API的名称列表。查找我们关注的API函数的话,不需要一个个去看,只需要输入API函数的名称即可。这里我们按M键:光标定位到第一个函数名称以M开始的API上。标题栏显示你正在搜索的字母。在函数名称上单击鼠标右键弹出的菜单项有:选择Followimport in Disassembler选项,我们就可以转到API函数的实现处。这也是另一种转到API函数实现代码的方法。在这里新手普遍会犯一个错误,现在单击鼠标右键选择-Search for-Name(label)incurrent module。这个时候我们得到的函数名称是USER32.DLL库中的函数名称,并不是我们的CrackMe主程序的导入表中的函数名。菜单项中明确指出是搜索当前模块,我们现在这种情况,当前模块是USER32,因此MessageBoxA是位于该模块当中的。
为了避免这样的错误,要记得看一眼调试器窗口的标题,尤其是当前模块的名称。尽管我们现在并没有执行这个API函数,但是我们通过这种方式来看看我们感兴趣的API函数。然后按减号键就又可以回到你之前的位置。
Windows NT: 2000, XP或者2003这里我们的教程讨论的是windowsNT/2000 和 XP,2003。对于NT系列的操作系统来说,OD是非常好用的。如果你是95/98系统的话,你也可以参考附录部分(windows 9x的说明).我们在CrackMe的API列表中单击鼠标右键选择-Toggle breakpoint on import菜单项来给当前选中的函数设置断点。当然你也可以在命令栏中给你关注的函数设置断点。bp MessageBoxABP是在指定的函数的第一条指令处设置断点。而不是在函数的调用处。我们可以来到MessageBoxA的第一条指令处看看。现在我们按下F9运行CrackMe。出现了CrackMe的主窗口。在菜单中选择help-Register:随便输入一个名词和序列号,然后单击确定。这个时候MessageBoxA函数就会被调用并且中断下来。右下角的标题显示暂停。左侧标题会显示调试器暂停的原因。提示说:”中断在USER32.MessageBoxA处”,这意味着USER32.MessageBoxA处设置了断点。此时我们断在了这里。
在该函数调用的时候,你可以看到这个函数的参数值。根据stdcall的调用约定,API的参数通常从右至左的在堆栈中排列。堆栈的顶部存放返回地址。当前是-4013C1。回顾一下我们第7章介绍过的CALL和RET,我们就知道调用任何子程序,堆栈顶部存放的都是返回地址。当前调用MessageBoxA,返回地址就是4013C1。接下来(即下图)是函数的参数。MessageBoxA有4个参数(可以参考MSDN):父窗口句柄,消息文本,标题文本和风格。消息文本是:“No luck there,mate!”。由于我们输入的用户名和序列号不正确。所以即将弹出注册失败的消息框。为了证明会弹出消息框,我们在下方的RETN 10处设置断点。我们对应的地址可能不一样。但是无论如何,第一个RET处就是MessageBoxA函数的返回处。按F9键。
消息框弹了出来。标题文本为Noluck!,文本内容为No luckthere,mate!。表明输入的用户名或者序列号不正确。单击”是”-触发RETN 10处的断点。因此,我们得出结论,该消息框是在MessageBoxA函数执行过程中弹出的。这里RETN 10跟通常的RETN有点差别。通常的RET只是返回到4013C1处。返回以后,返回地址从堆栈中删除。堆栈指针向下移动4字节(更确切的说是向高地址一侧)。对于RET 10H来说,除了完成RET的操作之外,ESP还要增加10h。所以,ESP的增量为10h + 4h = 14h = 20,让我们按F7键看看。我们可以看到从API函数中返回到CrackMe主程序代码中。堆栈指针由顶部向下移动了20个字节。和之前预期的一样,即RETN不是简单的返回,并且还会清理按照stdcall调用约定传递给函数参数的堆栈空间。现在CrackMe已经知道了序列号不正确,我们按F9键。断点再次触发。CrackMe会弹出错误提示框。返回地址是40137D。让我们来看看该地址处的代码。单击鼠标右键选择-Follow inDisassembler或者Goto-Expression输入40137D。来到了40137D处,上面是一个CALL指令,调用MessageBox(401378)。可以看到上面还有一个MessageBox,但是提示的是”Great work,mate!Now try thenext CrackMe!”初步分析调试器显示的代码是函数的一部分,一个功能开始于401362弹出No luck的消息框。另一个功能开始于40134D弹出Good work的消息框。如果突出显示该函数的第一行(地址401362),提示框中会显示以下信息:调试器提示调用来至于401245,单击鼠标右键选择-Goto-CALL from 401245。这里是典型的比较代码:一个分支是No luck!,另一个分支是Great work。这里我们可不能错过。我们在条件跳转处设置断点。同时取消MessageBox入口处的断点。这里我们可以单击调试器窗口工具栏中的B按钮,即显示断点窗口:
单击鼠标右键选择-Remove。删除两个调用MessageBox的断点,只留下401243处条件判断语句的断点。我们F9运行起来,弹出No luck消息框,刚才我们已经跟踪过一次了。单击确定关闭消息框,然后继续打开注册窗口,输入名称和序列号。单击OK(确定)。跳转不会发生,因为EAX和EBX的值不相等。因此CALL401362弹出No luck消息框,如果你忘记了。可以单击鼠标右键-Goto-Expression来到401362处看一看。
但是如果我们修改零标志位Z的值呢?将Z标志位的值修改为相反的状态。Z标志位为1表示EAX=EBX。即EAX与EBX的差值为零。跳转将会发生。现在会调用另一个函数。如下:按F9运行起来。因此,比较是验证序列号的一个关键点。如果EAX和EBX的值相等则弹出Great work消息框,否则弹出No luck消息框。我们注意到代码中有两处No luck的消息框提示,只有一个是最后一次的提示。CrackMe当名称中包含数字的时候会提示No luck消息框,当序列号错误的时候还会提示No luck消息框。输入包含数字的名称。单击OK。弹出消息框,单击确定,就会在条件判断处中断下来。F9运行以后,又弹出消息框。返回地址为4013C1。OD的分析显示该子过程开始于40137E,结束于4013C1。调用MessageBox后就返回了。请注意,4013AC的箭头(>),这表示,该处是一个跳转地址。我们顺着红线可以找到跳转来至于哪里。’现在我们面对的是更多的比较和条件跳转指令,可以导致弹出No luck消息框。我们在这里设置断点。再次让程序运行起来,还是输入刚才的用户名和序列号,单击确定。这里的代码会循环检测用户名的所有字母。一旦用户名中包含数字的话,就会跳转到No luck消息框处。每检测一个字母,就会中断在40138B处。按F9键就会接着检测下一个字母。当循环到第7次的时候(也就是用户名ricnar456中的第一个数字-’4’的时候),跳转就会发生,但是我们可以修改进位标志位C的值。JB条件跳转是根据进位标志位C来决定是否跳转的,我们通过双击C标志位修改其值。这样,跳转就不会发生了。针对于其余的数字我们同样可以这么做让跳转不发生。接下来我依然要来比较EAX和EBX的值,向之前一样通过修改零标志位Z来让跳转发生。按F9键。我们不可能通过修改C和Z标志位来实现功能注册吧!我们需要通过别的方式来让CrackMe通过注册。选择第一个条件分支(设置一个断点)。这次我们不改变进位标志位C,而是直接简单粗暴的把该指令填NOP。在该指令上单击鼠标右键选择-Assemble输入NOP。可以将断点去掉了,只需要按下F2键。转到第二个关键跳转处。这里我们需要跳转始终成立,所以我们将JE改成JMP。然后还是按F2删除断点。然后按F9键。输入用户名和序列号。单击确定。当前这些修改是在针对于内存的,并不会影响可执行文件。如果需要保存修改到可执行文件的话,请执行以下操作:在反汇编窗口的任意位置单击鼠标右键选择-Copy to executable-All modifications。然后会显示:选择Copy all确保拷贝我们所做的所有修改。弹出一个新窗口,再次单击鼠标右键选择-Save File。将文件保存一个新名字(原来的可执行文件我们还有用处,其实这个时候覆盖也是行不通的,这个时候可执行文件正在被占用)。新的可执行文件我们取名叫CRACKME2.EXE。这时候,我们可以关闭OD了。我们双击打开我们CRACKME2来看一看修改的效果。选择-help-Register。单击OK。我们成功逆向这个CrackMe!但是这是不够的-以后我们会尝试找到正确的注册码而非该程序打补丁的方式来通过注册。我们还有很多的东西需要学习。(下面的一个点点内容是针对于windows 9x系列系统的,考虑到windows9x已经淘汰很久了。连XP系统前不久都已经退役了。这里就不做翻译了。)
本系列文章汉化版转载看雪论坛
感谢原作者:RicardoNarvaja(西班牙人) 原作者个人主页:http://www.ricardonarvaja.info/
感谢热心翻译的朋友:1~3章译者:BGCoder4~58章译者:安于此生
全集配套程序下载地址:链接: http://pan.baidu.com/s/1eQzTWfo 密码: vytv
现在明白了,新手容易犯的错误:为了避免这样的错误,要记得看一眼调试器窗口的标题,尤其是当前模块的名称 学习学习,,, 在此留名,以后多多交流哦~ [吾爱汇编论坛52HB.COM]-做的不错哦,楼主加油,期待更好的作品!nice,谢谢,给力非常感谢破解思路 楼主辛苦了 链接已生效 学习,收藏, 看下 这必须顶一个!!!
页:
[1]
2