黑客风云——风云网络
设为首页 加入收藏 我要投稿 网站地图

您现在的位置: 黑客风云 >> 黑客文章 >> 黑客进阶 >> 软件破解 >> 正文
·没有路由密码权限时的鸽08-23·上网安全 Vista自我防范10-11
·让濒临崩溃的Windows XP10-11·有备无患,快速自制救急10-11
·要你好看!Windows看图工10-11·空间赞助网提供不同类型10-11
·讨论net.exe和net1.exe的10-10·让3389远程桌面传输更通10-10
·巧妙入侵渗透赌博站10-10·Aspx空间扫权限工具10-10
·Windows2003最新提权工具10-10·易淘乐提供100M免费全能10-10
·系统开机密码忘了不着急10-09·中意网络提供免费100M免10-09
·与众不同 Windows XP开始10-08·让桌面图标翻跟斗 在XP上10-08
·上海宽元站长资助计划-提10-08·个性化Windows XP的任务10-07
·趣盘提供3G免费网络硬盘10-07·秀山热线提供200MB免费全10-07
·一次艰辛的提权过程10-06·成功入侵IT大卖场的渗透10-06
·mysqlhack- MYSQL利用工10-06·lanker一句话PHP后门客户10-06
·WIXI提供3G免费多媒体网10-06·新人网络提供100M/ftp免10-06
·如何利用QQ带来高流量10-05·UuShare提供免费网络文件10-05
[组图]破解利器OllyDBG入门系列(四)-内存断点
      ★★★★★

破解利器OllyDBG入门系列(四)-内存断点

文章整理发布:黑客风云 文章来源:www.05112.com 更新时间:2006-10-17 10:36:44


现在我们来按一下 ALT+F9 组合键,我们来到下面的代码:

00401431 |. 8D35 9C334000      LEA ESI,DWORD PTR DS:[40339C]               ; ALT+F9返回后来到的位置
00401437 |. 0FB60D EC334000    MOVZX ECX,BYTE PTR DS:[4033EC]
0040143E |. 33FF               XOR EDI,EDI

我们把反汇编窗口往上翻翻,呵,原来就在我们上一篇分析的代码下面啊?
 
现在我们在 0040140C 地址处那条指令上按 F2 设置一个断点,现在我们按  CTR+F2 组合键重新载入程序,载入后按 F9 键运行,我们将会中断在我们刚才在 0040140C 地址下的那个断点处:

0040140C /$ 60                 PUSHAD
0040140D |. 6A 00              PUSH 0                                      ; /RootPathName = NULL
0040140F |. E8 B4000000        CALL <JMP.&KERNEL32.GetDriveTypeA>          ; \GetDriveTypeA
00401414 |. A2 EC334000        MOV BYTE PTR DS:[4033EC],AL                 ; 磁盘类型参数送内存地址4033EC
00401419 |. 6A 00              PUSH 0                                      ; /pFileSystemNameSize = NULL
0040141B |. 6A 00              PUSH 0                                      ; |pFileSystemNameBuffer = NULL
0040141D |. 6A 00              PUSH 0                                      ; |pFileSystemFlags = NULL
0040141F |. 6A 00              PUSH 0                                      ; |pMaxFilenameLength = NULL
00401421 |. 6A 00              PUSH 0                                      ; |pVolumeSerialNumber = NULL
00401423 |. 6A 0B              PUSH 0B                                     ; |MaxVolumeNameSize = B (11.)
00401425 |. 68 9C334000        PUSH CrackHea.0040339C                      ; |VolumeNameBuffer = CrackHea.0040339C
0040142A |. 6A 00              PUSH 0                                      ; |RootPathName = NULL
0040142C |. E8 A3000000        CALL <JMP.&KERNEL32.GetVolumeInformationA>  ; \GetVolumeInformationA
00401431 |. 8D35 9C334000      LEA ESI,DWORD PTR DS:[40339C]               ; 把crackme程序所在分区的卷标名称送到ESI
00401437 |. 0FB60D EC334000    MOVZX ECX,BYTE PTR DS:[4033EC]              ; 磁盘类型参数送ECX
0040143E |. 33FF               XOR EDI,EDI                                 ; 把EDI清零
00401440 |> 8BC1               MOV EAX,ECX                                 ; 磁盘类型参数送EAX
00401442 |. 8B1E               MOV EBX,DWORD PTR DS:[ESI]                  ; 把卷标名作为数值送到 EBX
00401444 |. F7E3               MUL EBX                                     ; 循环递减取磁盘类型参数值与卷标名值相乘
00401446 |. 03F8               ADD EDI,EAX                                 ; 每次计算结果再加上上次计算结果保存在EDI中
00401448 |. 49                 DEC ECX                                     ; 把磁盘类型参数作为循环次数,依次递减
00401449 |. 83F9 00            CMP ECX,0                                   ; 判断是否计算完
0040144C |.^ 75 F2             JNZ SHORT CrackHea.00401440                 ; 没完继续
0040144E |. 893D 9C334000      MOV DWORD PTR DS:[40339C],EDI               ; 把计算后值送到内存地址40339C,这就是我们后来在ESI中看到的值
00401454 |. 61                 POPAD
00401455 \. C3                 RETN

通过上面的分析,我们知道基本算法是这样的:先用 GetDriveTypeA 函数获取磁盘类型参数,再用 GetVolumeInformationA 函数获取这个 crackme 程序所在分区的卷标。如我把这个 Crackme 程序放在 F:\OD教程\crackhead\ 目录下,而我 F 盘设置的卷标是 GAME,则这里获取的就是 GAME,ASCII 码为“47414D45”。但我们发现一个问题:假如原来我们在数据窗口中看到的地址 40339C 处的 16 进制代码是“47414D45”,即“GAME”,但经过地址 00401442 处的那条 MOV EBX,DWORD PTR DS:[ESI] 指令后,我们却发现 EBX 中的值是“454D4147”,正好把我们上面那个“47414D45”反过来了。为什么会这样呢?如果大家对 x86系列 CPU 的存储方式了解的话,这里就容易理解了。我们知道“GAME”有四个字节,即 ASCII 码为“47414D45”。我们看一下数据窗口中的情况:

0040339C     47 41 4D 45 00 00 00 00 00 00 00 00 00 00 00 00     GAME............

大家可以看出来内存地址 40339CH 到 40339FH 分别按顺序存放的是 47 41 4D 45。
如下图:
  
系统存储的原则为“高高低低”,即低字节存放在地址较低的字节单元中,高字节存放在地址较高的字节单元中。比如一个字由两个字节组成,像这样:12 34 ,这里的高字节就是 12 ,低字节就是 34。上面的那条指令 MOV EBX,DWORD PTR DS:[ESI] 等同于 MOV EBX,DWORD PTR DS:[40339C]。注意这里是 DWORD,即“双字”,由 4 个连续的字节构成。而取地址为 40339C 的双字单元中的内容时,我们应该得到的是“454D4147”,即由高字节到低字节顺序的值。因此经过 MOV EBX,DWORD PTR DS:[ESI] 这条指令,就是把从地址 40339C 开始处的值送到 EBX,所以我们得到了“454D4147”。好了,这里弄清楚了,我们再接着谈这个程序的算法。前面我们已经说了取磁盘类型参数做循环次数,再取卷标值 ASCII 码的逆序作为数值,有了这两个值就开始计算了。现在我们把磁盘类型值作为 n,卷标值 ASCII 码的逆序数值作为 a,最后得出的结果作为 b,有这样的计算过程:
第一次:b = a * n
第二次:b = a * (n - 1) + b
第三次:b = a * (n - 2) + b

第 n 次:b = a * 1 + b
可得出公式为 b = a * [n + (n - 1) + (n - 2) + … + 1] = a * [n * (n + 1) / 2]
还记得上一篇我们的分析吗?看这一句:

00401405 |. 81F6 53757A79     XOR ESI,797A7553                            ; 把ESI中的值与797A7553H异或

这里算出来的 b 最后还要和 797A7553H 异或一下才是真正的注册码。只要你对编程有所了解,这个注册机就很好写了。如果用汇编来写这个注册机的话就更简单了,很多内容可以直接照抄。
到此已经差不多了,最后还有几个东西也说一下吧:
1、上面用到了两个 API 函数,一个是 GetDriveTypeA,还有一个是 GetVolumeInformationA,关于这两个函数的具体用法我就不多说了,大家可以查一下 MSDN。这里只要大家注意函数参数传递的次序,即调用约定。先看一下这里:

00401419 |. 6A 00              PUSH 0                                      ; /pFileSystemNameSize = NULL
0040141B |. 6A 00              PUSH 0                                      ; |pFileSystemNameBuffer = NULL
0040141D |. 6A 00              PUSH 0                                      ; |pFileSystemFlags = NULL
0040141F |. 6A 00              PUSH 0                                      ; |pMaxFilenameLength = NULL
00401421 |. 6A 00              PUSH 0                                      ; |pVolumeSerialNumber = NULL
00401423 |. 6A 0B              PUSH 0B                                     ; |MaxVolumeNameSize = B (11.)
00401425 |. 68 9C334000        PUSH CrackHea.0040339C                      ; |VolumeNameBuffer = CrackHea.0040339C
0040142A |. 6A 00              PUSH 0                                      ; |RootPathName = NULL
0040142C |. E8 A3000000        CALL <JMP.&KERNEL32.GetVolumeInformationA>  ; \GetVolumeInformationA

把上面代码后的 OllyDBG 自动添加的注释与 MSDN 中的函数原型比较一下:
BOOL GetVolumeInformation(
LPCTSTR lpRootPathName,             // address of root directory of the file system
LPTSTR lpVolumeNameBuffer,          // address of name of the volume
DWORD nVolumeNameSize,              // length of lpVolumeNameBuffer
LPDWORD lpVolumeSerialNumber,       // address of volume serial number
LPDWORD lpMaximumComponentLength,   // address of system's maximum filename length
LPDWORD lpFileSystemFlags,          // address of file system flags
LPTSTR lpFileSystemNameBuffer,      // address of name of file system
DWORD nFileSystemNameSize           // length of lpFileSystemNameBuffer
);

大家应该看出来点什么了吧?函数调用是先把最后一个参数压栈,参数压栈顺序是从后往前。这就是一般比较常见的 stdcall 调用约定。
2、我在前面的 00401414 地址处的那条 MOV BYTE PTR DS:[4033EC],AL 指令后加的注释是“磁盘类型参数送内存地址4033EC”。为什么这样写?大家把前一句和这一句合起来看一下:

0040140F |. E8 B4000000        CALL <JMP.&KERNEL32.GetDriveTypeA>          ; \GetDriveTypeA
00401414 |. A2 EC334000        MOV BYTE PTR DS:[4033EC],AL                 ; 磁盘类型参数送内存地址4033EC

地址 0040140F 处的那条指令是调用 GetDriveTypeA 函数,一般函数调用后的返回值都保存在 EAX 中,所以地址 00401414 处的那一句 MOV BYTE PTR DS:[4033EC],AL 就是传递返回值。查一下 MSDN 可以知道 GetDriveTypeA 函数的返回值有这几个:

Value                     Meaning                                        返回在EAX中的值
DRIVE_UNKNOWN             The drive type cannot be determined.               0
DRIVE_NO_ROOT_DIR         The root directory does not exist.                 1
DRIVE_REMOVABLE           The disk can be removed from the drive.            2
DRIVE_FIXED               The disk cannot be removed from the drive.         3
DRIVE_REMOTE              The drive is a remote (network) drive.             4
DRIVE_CDROM               The drive is a CD-ROM drive.                       5
DRIVE_RAMDISK             The drive is a RAM disk.                           6

上面那个“返回在EAX中的值”是我加的,我这里返回的是 3,即磁盘不可从驱动器上删除。
3、通过分析这个程序的算法,我们发现这个注册算法是有漏洞的。如果我的分区没有卷标的话,则卷标值为 0,最后的注册码就是 797A7553H,即十进制 2038068563。而如果你的卷标和我一样,且磁盘类型一样的话,注册码也会一样,并不能真正做到一机一码。

上一页  [1] [2] [3] 

文章录入:cainiaowang    责任编辑:cainiaowang 
【字体:
Copyright @2006 黑客风云 ●业务联系:QQ 联系怪人 联系奇人 Email:给怪人发邮件 给奇人发邮件
ICP备案:冀06009886