现在进入第三篇,这一篇我们重点讲解怎样使用OllyDBG中的函数参考(即名称参考)功能。仍然选择crackmes.cjb.net镜像打包中的一个名称为CrackHead的crackme。老规矩,先运行一下这个程序看看:
呵,竟然没找到输入注册码的地方!别急,我们点一下程序上的那个菜单“Shit”(真是Shit啊,呵呵),在下拉菜单中选“TryIt”,会来到如下界面:
我们点一下那个“CheckIt”按钮试一下,哦,竟然没反应!我再输个“78787878”试试,还是没反应。再试试输入字母或其它字符,输不进去。由此判断注册码应该都是数字,只有输入正确的注册码才有动静。用PEiD检测一下,结果为MASM32/TASM32,怪不得程序比较小。信息收集的差不多了,现在关掉这个程序,我们用OllyDBG载入,按F9键直接让它运行起来,依次点击上面图中所说的菜单,使被调试程序显示如上面的第二个图。先不要点那个“CheckIt”按钮,保留上图的状态。现在我们没有什么字串好参考了,我们就在API函数上下断点,来让被调试程序中断在我们希望的地方。我们在OllyDBG的反汇编窗口中右击鼠标,在弹出菜单中选择查找->当前模块中的名称(标签),或者我们通过按CTR N组合键也可以达到同样的效果(注意在进行此操作时要在OllyDBG中保证是在当前被调试程序的领空,我在第一篇中已经介绍了领空的概念,如我这里调试这个程序时OllyDBG的标题栏显示的就是“[CPU-主线程,模块-CrackHea]”,这表明我们当前在被调试程序的领空)。通过上面的操作后会弹出一个对话框,如图:
对于这样的编辑框中输注册码的程序我们要设断点首选的API函数就是GetDlgItemText及GetWindowText。每个函数都有两个版本,一个是ASCII版,在函数后添加一个A表示,如GetDlgItemTextA,另一个是UNICODE版,在函数后添加一个W表示。如GetDlgItemTextW。对于编译为UNCODE版的程序可能在Win98下不能运行,因为Win98并非是完全支持UNICODE的系统。而NT系统则从底层支持UNICODE,它可以在操作系统内对字串进行转换,同时支持ASCII和UNICODE版本函数的调用。一般我们打开的程序看到的调用都是ASCII类型的函数,以“A”结尾。又跑题了,呵呵。现在回到我们调试的程序上来,我们现在就是要找一下我们调试的程序有没有调用GetDlgItemTextA或GetWindowTextA函数。还好,找到一个GetWindowTextA。在这个函数上右击,在弹出菜单上选择“在每个参考上设置断点”,我们会在OllyDBG窗口最下面的那个状态栏里看到“已设置2个断点”。另一种方法就是那个GetWindowTextA函数上右击,在弹出菜单上选择“查找输入函数参考”(或者按回车键),将会出现下面的对话框:
看上图,我们可以把两条都设上断点。这个程序只需在第一条指令设断点就可以了。好,我们现在按前面提到的第一条方法,就是“在每个参考上设置断点”,这样上图中的两条指令都会设上断点。断点设好后我们转到我们调试的程序上来,现在我们在被我们调试的程序上点击那个“CheckIt”按钮,被OllyDBG断下:
00401323|.E84C010000CALL<JMP.&USER32.GetWindowTextA>;GetWindowTextA
00401328|.E8A5000000CALLCrackHea.004013D2;关键,要按F7键跟进去
0040132D|.3BC6CMPEAX,ESI;比较
0040132F|.7542JNZSHORTCrackHea.00401373;不等则完蛋
00401331|.EB2CJMPSHORTCrackHea.0040135F
00401333|.4E6F77207>ASCII"Nowwriteakeyg"
00401343|.656E20616>ASCII"enandtutandy"
00401353|.6F7527726>ASCII"ou'redone.",0
0040135F|>6A00PUSH0;Style=MB_OK|MB_APPLMODAL
00401361|.680F304000PUSHCrackHea.0040300F;Title="Crudd'sCrackHead"
00401366|.6833134000PUSHCrackHea.00401333;Text="Nowwriteakeygenandtutandyou'redone."
0040136B|.FF7508PUSHDWORDPTRSS:[EBP 8];hOwner
0040136E|.E819010000CALL<JMP.&USER32.MessageBoxA>;MessageBoxA
从上面的代码,我们很容易看出00401328地址处的CALLCrackHea.004013D2是关键,必须仔细跟踪。而注册成功则会显示一个对话框,标题是“Crudd'sCrackHead”,对话框显示的内容是“Nowwriteakeygenandtutandyou'redone.”现在我按一下F8,准备步进到00401328地址处的那条CALLCrackHea.004013D2指令后再按F7键跟进去。等等,怎么回事?怎么按一下F8键跑到这来了:
00401474$-FF252C204000JMPDWORDPTRDS:[<&USER32.GetWindowText>;USER32.GetWindowTextA
0040147A$-FF2530204000JMPDWORDPTRDS:[<&USER32.LoadCursorA>];USER32.LoadCursorA
00401480$-FF251C204000JMPDWORDPTRDS:[<&USER32.LoadIconA>];USER32.LoadIconA
00401486$-FF2520204000JMPDWORDPTRDS:[<&USER32.LoadMenuA>];USER32.LoadMenuA
0040148C$-FF2524204000JMPDWORDPTRDS:[<&USER32.MessageBoxA>];USER32.MessageBoxA
原来是跳到另一个断点了。这个断点我们不需要,按一下F2键删掉它吧。删掉00401474地址处的断点后,我再按F8键,呵,完了,跑到User32.dll的领空了。看一下OllyDBG的标题栏:“[CPU-主线程,模块-USER32],跑到系统领空了,OllyDBG反汇编窗口中显示代码是这样:
77D3213C6A0CPUSH0C
77D3213E68A021D377PUSHUSER32.77D321A0
77D32143E87864FEFFCALLUSER32.77D185C0
怎么办?别急,我们按一下ALT F9组合键,呵,回来了:
00401328|.E8A5000000CALLCrackHea.004013D2;关键,要按F7键跟进去
0040132D|.3BC6CMPEAX,ESI;比较
0040132F|.7542JNZSHORTCrackHea.00401373;不等则完蛋
光标停在00401328地址处的那条指令上。现在我们按F7键跟进:
004013D2/$56PUSHESI;ESI入栈
004013D3|.33C0XOREAX,EAX;EAX清零
004013D5|.8D35C4334000LEAESI,DWORDPTRDS:[4033C4];把注册码框中的数值送到ESI
004013DB|.33C9XORECX,ECX;ECX清零
004013DD|.33D2XOREDX,EDX;EDX清零
004013DF|.8A06MOVAL,BYTEPTRDS:[ESI];把注册码中的每个字符送到AL
004013E1|.46INCESI;指针加1,指向下一个字符
004013E2|.3C2DCMPAL,2D;把取得的字符与16进制值为2D的字符(即“-”)比较,这里主要用于判断输入的是不是负数
004013E4|.7508JNZSHORTCrackHea.004013EE;不等则跳
004013E6|.BAFFFFFFFFMOVEDX,-1;如果输入的是负数,则把-1送到EDX,即16进制FFFFFFFF
004013EB|.8A06MOVAL,BYTEPTRDS:[ESI];取“-”号后的第一个字符
004013ED|.46INCESI;指针加1,指向再下一个字符
004013EE|>EB0BJMPSHORTCrackHea.004013FB
004013F0|>2C30SUBAL,30;每位字符减16进制的30,因为这里都是数字,如1的ASCII码是“31H”,减30H后为1,即我们平时看到的数值
004013F2|.8D0C89LEAECX,DWORDPTRDS:[ECX ECX*4];把前面运算后保存在ECX中的结果乘5再送到ECX
004013F5|.8D0C48LEAECX,DWORDPTRDS:[EAX ECX*2];每位字符运算后的值与2倍上一位字符运算后值相加后送ECX
004013F8|.8A06MOVAL,BYTEPTRDS:[ESI];取下一个字符
004013FA|.46INCESI;指针加1,指向再下一个字符
004013FB|>0AC0ORAL,AL
004013FD|.^75F1JNZSHORTCrackHea.004013F0;上面一条和这一条指令主要是用来判断是否已把用户输入的注册码计算完
004013FF|.8D040ALEAEAX,DWORDPTRDS:[EDX ECX];把EDX中的值与经过上面运算后的ECX中值相加送到EAX
00401402|.33C2XOREAX,EDX;把EAX与EDX异或。如果我们输入的是负数,则此处功能就是把EAX中的值取反
00401404|.5EPOPESI;ESI出栈。看到这条和下一条指令,我们要考虑一下这个ESI的值是哪里运算得出的呢?
00401405|.81F653757A79XORESI,797A7553;把ESI中的值与797A7553H异或
0040140B/.C3RETN
这里留下了一个问题:那个ESI寄存器中的值是从哪运算出来的?先不管这里,我们接着按F8键往下走,来到0040140B地址处的那条RETN指令(这里可以通过在调试选项的“命令”标签中勾选“使用RET代替RETN”来更改返回指令的显示方式),再按一下F8,我们就走出00401328地址处的那个CALL了。现在我们回到了这里:
0040132D|.3BC6CMPEAX,ESI;比较
0040132F|.7542JNZSHORTCrackHea.00401373;不等则完蛋
光标停在了0040132D地址处的那条指令上。根据前面的分析,我们知道EAX中存放的是我们输入的注册码经过计算后的值。我们来看一下信息窗口:
ESI=E6B5F2F9
EAX=FF439EBE
左键选择信息窗口中的ESI=E6B5F2F9,再按右键,在弹出菜单上选“修改寄存器”,我们会看到这样一个窗口:
可能你的显示跟我不一样,因为这个crackme中已经说了每个机器的序列号不一样。关掉上面的窗口,再对信息窗口中的EAX=FF439EBE做同样操作:
由上图我们知道了原来前面分析的对我们输入的注册码进行处理后的结果就是把字符格式转为数字格式。我们原来输入的是字串“12345666”,现在转换为了数字12345666。这下就很清楚了,随便在上面那个修改ESI图中显示的有符号或无符号编辑框中复制一个,粘贴到我们调试的程序中的编辑框中试一下:
呵呵,成功了。且慢高兴,这个crackme是要求写出注册机的。我们先不要求写注册机,但注册的算法我们要搞清楚。还记得我在前面说到的那个ESI寄存器值的问题吗?现在看看我们上面的分析,其实对做注册机来说是没有多少帮助的。要搞清注册算法,必须知道上面那个ESI寄存器值是如何产生的,这弄清楚后才能真正清楚这个crackme算法。今天就先说到这里,关于如何追出ESI寄存器的值我就留到下一篇OllyDBG入门系列(四)-内存断点中再讲吧。
OllyDBG 入门系列(三)-函数参考
最新推荐文章于 2019-09-28 01:36:00 发布