下载地址:http://bbs.pediy.com/showthread.php?s=&threadid=33069
使用工具:OllyDbg,Resource Hacker
分析资源的过程只是用Resource Hacker打开.exe文件,然后选择菜单项“操作--->保存所有资源”
就行了,没有什么技术问题,这里就不细说了。
用OllyDbg打开.exe文件,停在入口点处。这是一个典型的VC++程序入口,不用PEID也可看出来,为
什么呢?这种入口往下看会依次出现GetVersion,GetCommandLine,GetStartupInfo和GetModuleHandle
这些API,其中GetModuleHandle返回的值是作为一个过程的实际参数,这个过程肯定就是WinMain。这就
是说,我们刚才看到的部分其实只是VC的初始化代码,而程序员可见的代码只有在WinMain被调用以后才
开始,在这之前的部分是不需要关心的。在这个例子中,WinMain的入口在地址0x401000处,而调用它的
语句在地址0x401319处。
进入WinMain后发现这个过程只包含一个DialogBoxParam调用然后就返回了,典型的一个对话框做界
面的程序。根据DialogBoxParam的倒数第2个参数找到对话框的回调过程在地址0x401020处。
接着进入对话框的回调过程中。一般而言,这个过程只是对派送给对话框的某些消息作出个性化的
响应,我们只需要知道它具体处理的是哪些消息。这个过程的第2个参数就是消息的代号,所以函数体中
肯定会根据第2个参数的值实现分支。不知什么原因,VC生成的机器代码在存取局部变量时不象通常那样
使用ebp而是直接用esp做指针,而switch..case结构也变了形。经过辨认,0x40104D处的指令所引用的
[esp+80]就是消息代码参数,而这个对话框一共处理3种消息:WM_DESTROY、WM_CLOSE和WM_COMMAND。而
WM_DESTROY、WM_CLOSE又是在同一个分支中处理的,其响应动作为用PostMessage在对话框的消息队列中
放入一条WM_QUIT消息。(笔者观点,结束对话框尽量用现成的API――EndDialog,而对话框消息循环是
已经被Windows封装的东西,能不去涉及最好)
WM_COMMAND分支则较复杂些,其中涉及到界面上两个按钮被按下的事件,在地址0x401071处的指令
引用的[esp+84]是消息的(或者说是对话框过程的)wParam参数,通常这个参数的含义就是被按下按钮
的控件ID,这次就要查看刚保存的资源脚本了,经查验知0x3E9是“注册”按钮的ID,0x3ED则是
“关于”按钮的ID。于是很容易看到当“关于”按钮被按下时的动作是用MessageBox显示一条信息:
"first crackme by llydd \^_^/"
而当“注册”按钮被按下时,则调用GetDlgItemText到两个输入框中取得文本,这个函数的第二和
第三个参数分别是控件ID和文本缓冲区,根据第二个参数的数值以及资源脚本中的对应控件,就能知道
第三个参数所指向的缓冲区中是什么内容。在当前的情况下我们知道:
esp+10 == szUserName
esp+44 == szSerial
取完文本后用strlen例程(这在汇编中是一个很常用的求字符串长的例程,但不知道为什么会出现在C语
言编译的代码中,而导入表中实际上也没有strlen这个函数)求它们的长度。如果其中某个字符串的长
度小于4或大于20,则分别用MessageBox显示下面的消息:
"用户名至少4位最多20位"
"注册码至少4位最多20位"
并直接从对话框过程返回。只有这两个长度都在4到20之间才作进一步处理。
首先将szUserName的各字符与szSerial的对应字符相xor。(如果szSerial不及szUserName长,则只
有前strlen(szSerial)个字符做了变换)然后szUserName逐字符与szSerial的中点字符,也就是
szSerial [strlen (szSerial) / 2]
相xor,接下去是szUserName逐字符与这个中点字符减去'A'相xor,szUserName逐字符加上用户名长与序
列号长的总和,最后把szUserName与szSerial逐字符比较,如果相等,就用MessageBox显示下列信息:
"注册成功"
根据以上描述,逆向出来的C语言代码如下:
======================================================
#include <windows.h>
#define DLG_MAIN 101
#define ID_REG 1001 //“注册”按钮的ID
#define EDT_NAME 1003 //用户名输入框的ID
#define EDT_SERIAL 1004 //序列号输入框的ID
#define ID_ABOUT 1005 //“关于”按钮的ID
BOOL CALLBACK ProcDlgMain (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
char loc_szUserName[52], loc_szSerial[52];
int loc_nLenOfName, loc_nLenOfSerial;
int loc_i, loc_k;
switch (uMsg)
{
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_REG: //“注册”按钮被按下
GetDlgItemText (hDlg, EDT_NAME, loc_szUserName, 50);
loc_nLenOfName = strlen (loc_szUserName);
if (loc_nLenOfName < 4 || loc_nLenOfName > 20)
{
MessageBox (NULL, "用户名至少4位最多20位", "", MB_OK);
return FALSE;
}
GetDlgItemText (hDlg, EDT_SERIAL, loc_szSerial, 50);
loc_nLenOfSerial = strlen (loc_szSerial);
if (loc_nLenOfSerial < 4 || loc_nLenOfSerial > 20)
{
MessageBox (NULL, "注册码至少4位最多20位", "", MB_OK);
return FALSE;
}
loc_k = (loc_nLenOfName < loc_nLenOfSerial)? loc_nLenOfName: loc_nLenOfSerial; //取较小者
for (loc_i = 0; loc_i < loc_k; loc_i ++)
{
loc_szUserName [loc_i] = loc_szUserName [loc_i] ^ loc_szSerial [loc_i];
//用户名逐字符与序列号对应字符相xor
}
for (loc_i = 0; loc_i < loc_nLenOfName; loc_i ++)
{
loc_szUserName [loc_i] = loc_szUserName [loc_i] ^ loc_szSerial [loc_nLenOfSerial / 2];
//再逐字符与序列号中点字符相xor
}
for (loc_i = 0; loc_i < loc_nLenOfName; loc_i ++)
{
loc_szUserName [loc_i] = loc_szUserName [loc_i] ^ (loc_szSerial [loc_nLenOfSerial / 2] - 'A');
//再逐字符与序列号中点字符减'A'相xor
}
for (loc_i = 0; loc_i < loc_nLenOfName; loc_i ++)
{
loc_szUserName [loc_i] = loc_szUserName [loc_i] + (char) (loc_nLenOfName + loc_nLenOfSerial);
//再逐字符加上用户名长度与序列号长度的和
}
if (strcmp (loc_szUserName, loc_szSerial) == 0)
{
MessageBox (NULL, "注册成功", "", MB_OK);
}
break;
case ID_ABOUT:
MessageBox (NULL, "first crackme by llydd \^_^/", "", MB_OK);
}
break;
case WM_DESTROY:
case WM_CLOSE:
PostMessage (hDlg, WM_QUIT, 0, 0); //个人觉得这种方法不好
}
return FALSE;
}
int WINAPI WinMain (HINSTANCE hCurrInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow
)
{
DialogBoxParam (hCurrInstance, DLG_MAIN, NULL, &ProcDlgMain, NULL);
return 0;
}
======================================================
资源脚本如下:
======================================================
101 DIALOGEX 0, 0, 162, 91
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "crackme BY llydd"
LANGUAGE LANG_CHINESE, 0x2
FONT 10, "System"
{
CONTROL "crackme", 1001, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 23, 63, 50, 21
CONTROL "用户名", -1, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 14, 14, 25, 16
CONTROL "", 1003, EDIT, ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 46, 11, 92, 17
CONTROL "注册码", -1, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 15, 37, 27, 10
CONTROL "", 1004, EDIT, ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 46, 32, 92, 17
CONTROL "关于", 1005, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 109, 64, 30, 20
}
======================================================
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!