当前位置:首页 > 加密解密技术 > 调试工具使用 > OllyDbg调试器 > 详细内容
OllyDBG 入门系列(三)-函数参考
发布时间:2009/8/9  阅读次数:7578  字体大小: 【】 【】【
OllyDBG  入门系列(三)-函数参考

作者:CCDebuger

现在进入第三篇,这一篇我们重点讲解怎样使用  OllyDBG  中的函数参考(即名称参考)功能。仍然选择  crackmes.cjb.net  镜像打包中的一个名称为  CrackHead  的crackme。老规矩,先运行一下这个程序看看:

呵,竟然没找到输入注册码的地方!别急,我们点一下程序上的那个菜单“Shit”(真是  Shit  啊,呵呵),在下拉菜单中选“Try  It”,会来到如下界面:
  
我们点一下那个“Check  It”按钮试一下,哦,竟然没反应!我再输个“78787878”试试,还是没反应。再试试输入字母或其它字符,输不进去。由此判断注册码应该都是数字,只有输入正确的注册码才有动静。用  PEiD  检测一下,结果为  MASM32  /  TASM32,怪不得程序比较小。信息收集的差不多了,现在关掉这个程序,我们用  OllyDBG  载入,按  F9  键直接让它运行起来,依次点击上面图中所说的菜单,使被调试程序显示如上面的第二个图。先不要点那个“Check  It”按钮,保留上图的状态。现在我们没有什么字串好参考了,我们就在  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  函数上右击,在弹出菜单上选择“查找输入函数参考”(或者按回车键),将会出现下面的对话框:
  
看上图,我们可以把两条都设上断点。这个程序只需在第一条指令设断点就可以了。好,我们现在按前面提到的第一条方法,就是“在每个参考上设置断点”,这样上图中的两条指令都会设上断点。断点设好后我们转到我们调试的程序上来,现在我们在被我们调试的程序上点击那个“Check  It”按钮,被  OllyDBG  断下:

00401323  |.  E8  4C010000                  CALL                        ;  GetWindowTextA
00401328  |.  E8  A5000000                  CALL  CrackHea.004013D2                                            ;  关键,要按F7键跟进去
0040132D  |.  3BC6                                CMP  EAX,ESI                                                                  ;  比较
0040132F  |.  75  42                              JNZ  SHORT  CrackHea.00401373                                  ;  不等则完蛋
00401331  |.  EB  2C                              JMP  SHORT  CrackHea.0040135F
00401333  |.  4E  6F  77  20  7>            ASCII  "Now  write  a  keyg"
00401343  |.  65  6E  20  61  6>            ASCII  "en  and  tut  and  y"
00401353  |.  6F  75  27  72  6>            ASCII  "oure  done.",0
0040135F  |>  6A  00                              PUSH  0                                                                            ;  Style  =  MB_OK|MB_APPLMODAL
00401361  |.  68  0F304000                  PUSH  CrackHea.0040300F                                            ;  Title  =  "Crudds  Crack  Head"
00401366  |.  68  33134000                  PUSH  CrackHea.00401333                                            ;  Text  =  "Now  write  a  keygen  and  tut  and  youre  done."
0040136B  |.  FF75  08                          PUSH  DWORD  PTR  SS:[EBP+8]                                      ;  hOwner
0040136E  |.  E8  19010000                  CALL                              ;  MessageBoxA

从上面的代码,我们很容易看出  00401328  地址处的  CALL  CrackHea.004013D2  是关键,必须仔细跟踪。而注册成功则会显示一个对话框,标题是“Crudds  Crack  Head”,对话框显示的内容是“Now  write  a  keygen  and  tut  and  youre  done.”现在我按一下  F8,准备步进到  00401328  地址处的那条  CALL  CrackHea.004013D2  指令后再按  F7  键跟进去。等等,怎么回事?怎么按一下  F8  键跑到这来了:

00401474  $-  FF25  2C204000            JMP  DWORD  PTR  DS:[<&USER32.GetWindowText>        ;  USER32.GetWindowTextA
0040147A  $-  FF25  30204000            JMP  DWORD  PTR  DS:[<&USER32.LoadCursorA>]          ;  USER32.LoadCursorA
00401480  $-  FF25  1C204000            JMP  DWORD  PTR  DS:[<&USER32.LoadIconA>]              ;  USER32.LoadIconA
00401486  $-  FF25  20204000            JMP  DWORD  PTR  DS:[<&USER32.LoadMenuA>]              ;  USER32.LoadMenuA
0040148C  $-  FF25  24204000            JMP  DWORD  PTR  DS:[<&USER32.MessageBoxA>]          ;  USER32.MessageBoxA

原来是跳到另一个断点了。这个断点我们不需要,按一下  F2  键删掉它吧。删掉  00401474  地址处的断点后,我再按  F8  键,呵,完了,跑到  User32.dll  的领空了。看一下  OllyDBG  的标题栏:“[CPU  -  主线程,  模块  -  USER32],跑到系统领空了,OllyDBG  反汇编窗口中显示代码是这样:

77D3213C  6A  0C                                  PUSH  0C
77D3213E  68  A021D377                      PUSH  USER32.77D321A0
77D32143  E8  7864FEFF                      CALL  USER32.77D185C0

怎么办?别急,我们按一下  ALT+F9  组合键,呵,回来了:

00401328  |.  E8  A5000000                CALL  CrackHea.004013D2                                          ;  关键,要按F7键跟进去
0040132D  |.  3BC6                              CMP  EAX,ESI                                                                ;  比较
0040132F  |.  75  42                            JNZ  SHORT  CrackHea.00401373                                ;  不等则完蛋

光标停在  00401328  地址处的那条指令上。现在我们按  F7  键跟进:

004013D2  /$  56                                PUSH  ESI                                                                        ;  ESI入栈
004013D3  |.  33C0                            XOR  EAX,EAX                                                                  ;  EAX清零
004013D5  |.  8D35  C4334000          LEA  ESI,DWORD  PTR  DS:[4033C4]                              ;  把注册码框中的数值送到ESI
004013DB  |.  33C9                            XOR  ECX,ECX                                                                  ;  ECX清零
004013DD  |.  33D2                            XOR  EDX,EDX                                                                  ;  EDX清零
004013DF  |.  8A06                            MOV  AL,BYTE  PTR  DS:[ESI]                                        ;  把注册码中的每个字符送到AL
004013E1  |.  46                                INC  ESI                                                                          ;  指针加1,指向下一个字符
004013E2  |.  3C  2D                          CMP  AL,2D                                                                      ;  把取得的字符与16进制值为2D的字符(即“-”)比较,这里主要用于判断输入的是不是负数
004013E4  |.  75  08                          JNZ  SHORT  CrackHea.004013EE                                  ;  不等则跳
004013E6  |.  BA  FFFFFFFF              MOV  EDX,-1                                                                    ;  如果输入的是负数,则把-1送到EDX,即16进制FFFFFFFF
004013EB  |.  8A06                            MOV  AL,BYTE  PTR  DS:[ESI]                                        ;  取“-”号后的第一个字符
004013ED  |.  46                                INC  ESI                                                                          ;  指针加1,指向再下一个字符
004013EE  |>  EB  0B                          JMP  SHORT  CrackHea.004013FB
004013F0  |>  2C  30                          SUB  AL,30                                                                      ;  每位字符减16进制的30,因为这里都是数字,如1的ASCII码是“31H”,减30H后为1,即我们平时看到的数值
004013F2  |.  8D0C89                        LEA  ECX,DWORD  PTR  DS:[ECX+ECX*4]                        ;  把前面运算后保存在ECX中的结果乘5再送到ECX
004013F5  |.  8D0C48                        LEA  ECX,DWORD  PTR  DS:[EAX+ECX*2]                        ;  每位字符运算后的值与2倍上一位字符运算后值相加后送ECX
004013F8  |.  8A06                            MOV  AL,BYTE  PTR  DS:[ESI]                                        ;  取下一个字符
004013FA  |.  46                                INC  ESI                                                                          ;  指针加1,指向再下一个字符
004013FB  |>  0AC0                            OR  AL,AL
004013FD  |.^  75  F1                        JNZ  SHORT  CrackHea.004013F0                                  ;  上面一条和这一条指令主要是用来判断是否已把用户输入的注册码计算完
004013FF  |.  8D040A                        LEA  EAX,DWORD  PTR  DS:[EDX+ECX]                            ;  把EDX中的值与经过上面运算后的ECX中值相加送到EAX
00401402  |.  33C2                            XOR  EAX,EDX                                                                  ;  把EAX与EDX异或。如果我们输入的是负数,则此处功能就是把EAX中的值取反
00401404  |.  5E                                POP  ESI                                                                          ;  ESI出栈。看到这条和下一条指令,我们要考虑一下这个ESI的值是哪里运算得出的呢?
00401405  |.  81F6  53757A79          XOR  ESI,797A7553                                                        ;  把ESI中的值与797A7553H异或
0040140B  \.  C3                                RETN


这里留下了一个问题:那个  ESI  寄存器中的值是从哪运算出来的?先不管这里,我们接着按  F8  键往下走,来到  0040140B  地址处的那条  RETN  指令(这里可以通过在调试选项的“命令”标签中勾选“使用  RET  代替  RETN”来更改返回指令的显示方式),再按一下  F8,我们就走出  00401328  地址处的那个  CALL  了。现在我们回到了这里:

0040132D  |.  3BC6                          CMP  EAX,ESI                                                                    ;  比较
0040132F  |.  75  42                        JNZ  SHORT  CrackHea.00401373                                    ;  不等则完蛋

光标停在了  0040132D  地址处的那条指令上。根据前面的分析,我们知道  EAX  中存放的是我们输入的注册码经过计算后的值。我们来看一下信息窗口:

ESI=E6B5F2F9
EAX=FF439EBE

左键选择信息窗口中的  ESI=E6B5F2F9,再按右键,在弹出菜单上选“修改寄存器”,我们会看到这样一个窗口:
  
可能你的显示跟我不一样,因为这个  crackme  中已经说了每个机器的序列号不一样。关掉上面的窗口,再对信息窗口中的  EAX=FF439EBE  做同样操作:
  
由上图我们知道了原来前面分析的对我们输入的注册码进行处理后的结果就是把字符格式转为数字格式。我们原来输入的是字串“12345666”,现在转换为了数字  12345666。这下就很清楚了,随便在上面那个修改  ESI  图中显示的有符号或无符号编辑框中复制一个,粘贴到我们调试的程序中的编辑框中试一下:
  
呵呵,成功了。且慢高兴,这个  crackme  是要求写出注册机的。我们先不要求写注册机,但注册的算法我们要搞清楚。还记得我在前面说到的那个  ESI  寄存器值的问题吗?现在看看我们上面的分析,其实对做注册机来说是没有多少帮助的。要搞清注册算法,必须知道上面那个  ESI  寄存器值是如何产生的,这弄清楚后才能真正清楚这个  crackme  算法。今天就先说到这里,关于如何追出  ESI  寄存器的值我就留到下一篇  OllyDBG  入门系列(四)-内存断点  中再讲吧。

--------------------------------------------------------------------------------
【版权声明】  本文纯属技术交流,  转载请注明作者并保持文章的完整,  谢谢!  
我要评论
  • 匿名发表
  • [添加到收藏夹]
  • 发表评论:(匿名发表无需登录,已登录用户可直接发表。) 登录状态:未登录
最新评论
所有评论[0]
    暂无已审核评论!
 


设为首页 | 加入收藏 | 意见建议 | 友情链接 | 版权声明 | 管理登陆 | 编程论坛 | 给我留言

声明:本网站部分稿件来源的所有文字、图片和音视频稿件,来自互联网,若侵犯您的权利,请来信告知,我们将在第一时间内删除!


Copyright 2009-2022 redrose ( wgbcw.com ) All rights reserved ICP备案编号:滇ICP备09007156号 Dict.cn


点击这里给我发消息
点击这里给我发消息