所以我们来看看EBP-34那里有什么:
:d ebp-3
你应该能在数据窗中看到你输入的东西。下面我们得来找开始核对输入注册码的
地方。按F10一步一步地单步运行直到你发现与EBP-34有点关系的地方.... 你不
用单步运行多久,就会看到这些代码:
> LEA EAX, [EBP+FFFFFF64] ; EAX = EBP-9C
LEA ECX, [EBP-34] ; ECX = EBP-34
PUSH EAX ; 保存 EAX
PUSH ECX ; 保存 ECX
> CALL 00403DD0 ; Call 一子程序
ADD ESP, 08 ; 删除保存的信息
TEST EAX, EAX ; 检查返回值
JNZ 00402BC0 ; 如果不是零的话跳转
对我来说,马上就可以看出这象是一个字符比较程序。它们工作起来就是这样:
* 输入两个字符串
如果相同就返回零
否则返回非零
那为什么程序要用一个字符串来和你输入的相比较呢?看它是不是合法的!(可
能你已经想到了)。那么是什么东西躲在 [EBP+FFFFFF64] ? SoftICE处理负数
还不是很好,所以得算算:
100000000 - FFFFFF64 = 9C
在 SoftICE 用这样的命令:
:? 0-FFFFFF64
100000000 对SoftICE来说太大了,但它还是给出了相同的结果。
现在是...来看看什么东西躲在EBP-9C那里的时候了...这样输入命令:
:d ebp-9c
数据窗口会显示出一大排数字 ─ 注册码!但是记住我前面说过的...两种注册
方式对应两个注册码....所以你把这些注册码抄下来以后,继续用F10单步运行
....我们会遇到这些代码:
> LEA EAX, [EBP-68] ; EAX = EBP-68
LEA ECX, [EBP-34] ; ECX = EBP-34
PUSH EAX ; 保存 EAX
PUSH ECX ; 保存 ECX
> CALL 00403DD0 ; 再次调用子程序
ADD ESP, 08 ; 删除保存的信息
TEST EAX, EAX ; 检查返回结果
JNZ 00402BFF ; 如果非零跳转
你在EBP-68处找到了什么?不错吧...另一个注册码。
:d ebp-68
我们的练习结束了,希望一切顺利。
3.2 Command Line 95 -容易的用户名/注册码方式的注册、注册器
=================================================================
这是一个非常好的例子,注册码的计算也很简单。
3.1.1 检查程序
===========================
检查程序以后你知道它是32位的应用程序,要求输入名字和注册码。 让我们开始!
3.1.2 捕捉代码
===========================
我们象拆解TaskLock那样 ─ 设置断点。我们可以在两个可能性最大的两个函数
都设上断点:GetWindowTextA 和
GetDlgItemTextA. 按下Ctrl+D 进入SoftICE, 然后:
:bpx getwindowtexta
:bpx getdlgitemtexta
接下来进入注册对话框,输入一个名字和一些数字(多数情况下是一个整数),
我是这么写的,然后按OK....
Name: ED!SON '96
Code: 12345
程序在 GetDlgItemTextA 处停住了,就象TaskLock一样。我们按F11回到调用它
的地方。用 Ctrl+Up卷动窗口直到看到这些:
MOV ESI, [ESP+0C]
PUSH 1E ; 最大长度
PUSH 0040A680 ; 缓冲地址
PUSH 000003ED ; 控制柄
PUSH ESI ; 对话柄
CALL [User32!GetDlgItemTextA]
数字40A680引起了我们的注意,看看那里有什么:
:d 40a680
如果没有我们输入的名字,
数据窗口里有些什么呢?好了,我们来研究下面的一 段代码:
PUSH 00
PUSH 00
PUSH 000003F6 ; 控制柄
MOV EDI, 0040A680 ; 保存缓冲区地址
PUSH ESI ; 对话柄
CALL [User32!GetDlgItemInt]
GetDlgItemInt 和 GetDlgItemText差不多,但它从文字框中返回一个整数。
它出在EAX中返回来的,所以单步运行通过这些代码,再来看看寄存器窗....
对我而言是:
EAX=00003039
十六进制数3039是多少? 输入:
:? 3039
我们得到:
00003039 0000012345 "09"
^ 16进制 ^ 十进制 ^ ASCII
正如你看到(和已经猜到)的那样,它是你输入的注册码。OK,下面怎么办?让
我们来看下面的代码:
MOV [0040A548], EAX ; 返回注册码
MOV EDX, EAX ; 同时保存在DX中
3.1.3 计算注册码
==========================
这样注册码就算出来了
MOV ECX, FFFFFFFF ; 这几行计算字符长度
SUB EAX, EAX ; .
REPNZ SCASB ; .
NOT ECX ; .
DEC ECX ; ECX <-- 长度
MOVSX EAX, BYTE PTR [0040A680] ; 读入40A680处的一字节
IMUL ECX, EAX ; ECX = ECX * EAX
SHL ECX, 0A ; 左移 0A 次
ADD ECX, 0002F8CC ; 结果加上2F8CC
MOV [0040A664], ECX
验证合法性....
CMP ECX, EDX ; 比较
JZ 00402DA6 ; 如果相同就....
当你运行到比较这一步时,就可以得到你真正的注册码:
:? ecx
对我而言它是:
000DC0CC 0000901324
也就是说我的正确的注册码是901324.
按F5或者Ctrl+D让它运行,
然后用正确的注册码(十进制)再来一次。这一次 成功了!
4. 为COMMAND LINE 95制作注册器
=========================================
我们把上面计算注册码的代码翻译成C语言程序。 最明了的计算公式就是:
code=((uppercase_first_char * length_of_string) << 0x0A) + 0x2f8cc;
注(1): 别忘了一件事 就是把所有字符转成大写
(2): "<< 0x0A" 实际上就是 "乘以 2^10"
完整的程序就是:
#include <string.h>
#include <stdio.h>
int main()
{
unsigned long code;
unsigned char buffer[0x1e];
printf("CommandLine95 Keymaker by ED!SON '96\n");
printf("Enter name: ");
gets(buffer);
strupr(buffer);
code = ( ((unsigned long)buffer[0] *
(unsigned long)strlen(buffer))
<< 0x0A) + 0x2f8cc;
printf("Your code is: %lu", code);
return 0;
}
爽吧?
4. 当程序调用一个函数时,PUSH和CALL这些指令是如何工作的
====================================================
我们重新来看看TaskLock中的这段代码:
PUSH 32 ; 字符串最大长度
PUSH EAX ; 字符缓冲区地址
PUSH 000003F4 ; 控制标识
PUSH DWORD PTR [ESI+1C] ; 对话框柄
CALL [USER32!GetDlgItemTextA] ; 获得字符串
如果认为是C语言编译出来的话,这个CALL应该是这样:
GetDlgItemTextA(hwndDlg, 0x3F4, buffer, 0x32);
^ [ESI+1C] ^ EAX
PUSH把数据保存在叫做堆栈的地方。每个PUSH把新的数据放在堆栈的顶部,被调
用的子程序就挨个地检查躺在堆栈中的数据,按照定义来使用它们。
附录
A. 让SoftICE载入符号
==============================
你可以用exp getwindowtext命令来检查SoftICE是否已经为GetWindowText装入
了符号,象这样:
:exp getwindowtext
如果你没有得到所有GetWindowText函数的列表,你就得编辑\SIW95\WINICE.DAT,
在"Examples of export symbols that can be included for chicago"这段文字
以后的那些 'exp='的行首去掉';',为了节省内存,选择最重要的几个就可以了:
kernel32.dll
user32.dll
gdi32.dll
编辑完后,重新起动计算机使其生效。
B. 函数语法
============================
如果你看看下面的函数声明,对我们上面讲到的函数调用就容易明白了:
int GetWindowText(int windowhandle, char *buffer, int maxlen); int
GetDlgItemText(int dialoghandle, int controlid, char *buffer,
int maxlen);
int GetDlgItemInt(int dialoghandle,
int controlid, int *flag, int type);
这些函数的详细描述,可参考Windows/Win32编程手册
=====================================================