下载地址:http://www.crackmes.de/users/cobrasniper555/crackme_2/
使用工具:OllyDbg,IDA,Resource Hacker 这是个用Mingw GCC编译的程序,而且很可能是在Dev C++里开发的。本来下载的目的只是为了破解算法,
但长久以来逆向SDK养成的习惯还是让我禁不住从头至尾地观察程序的运行,结果,不小心就彻底解剖了……
首先试运行,弹出一个消息框,提示“这是试用版本……”云云,确定之后才出现主窗口,主窗口的
“About”菜单有一个“Register”项,但是是灰化的(这本身在逻辑上就有矛盾,正因为是试用版本才需要
注册,但“注册”功能在试用版本里却又是受限的)。
用OllyDbg载入程序。下一步干什么呢?查找提示字符串?必须承认,如果是在刚学破解的时候,我肯定
会这么做,事实上,大部分人也确实会这么做。但现在我们采用另外一种思路:首先寻找WinMain()启动点,
在这个函数里一般都会进行注册窗口类、创建窗口、显示窗口、消息循环……之类的工作,通过窗口类的成
员lpfnWndProc的值就能找到窗口过程的入口,关注一下窗口过程对WM_COMMAND消息的处理,程序的关键点,
包括注册算法都十有八九就在里面了。
入口点在地址401240处,遇到401253处的call 401100处跟入,里边的代码虽然长,但绝大多数都是系统
初始化和退出扫尾的代码。只有4011E2处的call 401990看上去比较不寻常,需要跟踪进入。进入以后一眼就
能看到GetCommandLine和GetStartupInfo两个API,但不要急,这还不是程序真正开始的地方,必须等看到再
下面的GetModuleHandle才算,理由很简单,WinMain()的第一个参数就是hInstance,如果没有取模块句柄的
调用,这个参数的值从何而来。再往下看,只剩一个call了,很明显,WinMain()只可能是这个call 4014D4
了。跟踪进入,果然是注册窗口类到建立消息循环的窗口程序四部曲(具体代码略)。
跟踪到CreateWindowEx一句时按F8步过,这时上述“试用版本……”那个消息框出现在屏幕上。然而自
从入口点以来都没有发现MessageBox函数调用,那么这是怎么一回事呢?只有一种可能,就是在窗口过程的
WM_CREATE分支中进行了相关的检测和调用。往上找到填写窗口类时给的窗口过程地址是4017EE,跟到窗口过
程里,找到[ebp-0C]等于1的分支(符号WM_CREATE的值为1),发现它是先调用401290处的过程,如果这个过
程的返回值为0就跳过上述消息框,反之则显示消息框。地址401290处的过程其实就是一句API调用:
RegOpenKey (HKEY_CURRENT_USER, "Software\\Cobra", &hkResult);
这提示我们为了去掉启动消息框,必须在注册表中创建如上所示的一个项。
为了进一步分析窗口程序,包括分析窗口类结构体、操作数符号化等等方面的内容,改用IDA加载程序,
毕竟这些工作在OllyDbg里要么是不能做,要么是不方便做。进一步的分析过程这里从略了,直接贴上逆向出
来的源代码应该更能说明问题。界面部分的代码如下:(这其中自然已包含用Resource Hacker提取资源并且
定义资源ID的过程)
===================================================================
#include <windows.h>
#define WS_EX_USEDEF WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR
#define DLG_REG 100
#define ID_CLOSE 101 /* “注册”对话框上的“Exit”按钮 */
#define ICO_MAIN 102 /* 主图标 */
#define IDM_LIMITED 103 /* 试用版菜单 */
#define IDM_EXIT 104
#define IDM_REG 105
#define EDT_NAME 106
#define EDT_SERIAL 107
#define IDM_MAIN 108
#define IDM_RESTART 109
#define ID_CHECK 110 /* “注册”对话框上的“Submit”按钮 */ LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK ProcDlgReg (HWND, UINT, WPARAM, LPARAM);
void KeyGen (char *, char *);
/* Make the class name into a global variable */
char szClassName[ ] = "WindowsApp";
HKEY hkResult;
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow)
{
HWND hWinMain; /* This is the handle for our window */
MSG stMsg; /* Here messages to the application are saved */
WNDCLASSEX stWndClass; /* Data structure for the windowclass */
/* The Window structure */
stWndClass.hInstance = hThisInstance;
stWndClass.lpszClassName = szClassName;
stWndClass.lpfnWndProc = WindowProcedure;
stWndClass.style = CS_DBLCLKS;
stWndClass.cbSize = sizeof (WNDCLASSEX);
stWndClass.hIcon = LoadIcon (hThisInstance, ICO_MAIN);
stWndClass.hIconSm = NULL;
stWndClass.hCursor = LoadCursor (NULL, IDC_ARROW);
stWndClass.lpszMenuName = (RegOpenKey (HKEY_CURRENT_USER,
"Software\\Cobra",
&hkResult
) == ERROR_SUCCESS
)? IDM_MAIN : IDM_LIMITED;
stWndClass.cbClsExtra = 0;
stWndClass.cbWndExtra = 0;
stWndClass.hbrBackground = CreateSolidBrush (0xE9E9E9);
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&stWndClass))
return 0;
/* The class is registered, let's create the program */
hWinMain = CreateWindowEx (
WS_EX_USEDEF, /* Extended possibilites for variation */
szClassName, /* Classname */
"Windows App", /* Title Text */
WS_CAPTION | WS_SYSMENU,
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
300, /* The programs width */
200, /* and height in pixels */
NULL,
NULL, /* Use class menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
/* Make the window visible on the screen */
ShowWindow (hWinMain, nCmdShow);
UpdateWindow (hWinMain);
while (TRUE)
{
register int i = GetMessage (&stMsg, NULL, 0, 0);
if ((i == 0) || (i == -1)) break;
TranslateMessage (&stMsg);
DispatchMessage (&stMsg);
}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return stMsg.wParam;
} /* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) /* handle the messages */
{
case WM_CREATE:
if (RegOpenKey (HKEY_CURRENT_USER, "Software\\Cobra", &hkResult) != ERROR_SUCCESS)
{
if (MessageBox (hWnd,
"This is a trial version of...umm...nothing.\n \
Please go to www.dontbuyme.com\nand register for your copy today!",
"Information",
MB_ICONINFORMATION | MB_OKCANCEL
) != IDOK
) PostQuitMessage (0);
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_EXIT:
PostQuitMessage (0);
break;
case IDM_REG: /* “注册”菜单项 */
DialogBoxParam (GetModuleHandle (NULL),
DLG_REG,
hWnd,
ProcDlgReg,
NULL
);
break;
case IDM_RESTART:
RegDeleteKey (HKEY_CURRENT_USER, "Software\\Cobra");
MessageBox (NULL,
"This program will now close.\nRestart this \
program at any time to start over the tutorial.\nThank you for doing this!\nPlease \
contact me by email at cobrasniper555@gmail.com when\nyou've completed this.\n \
Thank you!",
"Restart",
MB_OK
);
PostQuitMessage (0);
}
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
default:
return DefWindowProc (hWnd, uMsg, wParam, lParam);
}
return 0;
}
BOOL CALLBACK ProcDlgReg (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
/* “注册”对话框过程 */
{
char loc_szUserName[20], loc_szSerial[80], loc_szRealSerial[80];
switch (uMsg)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_CLOSE:
EndDialog (hDlg, 0);
break;
case ID_CHECK:
GetDlgItemText (hDlg, EDT_NAME, loc_szUserName, 20);
GetDlgItemText (hDlg, EDT_SERIAL, loc_szSerial, 80);
KeyGen (loc_szUserName, loc_szRealSerial); /* 生成明码 */
SetDlgItemText (hDlg, EDT_SERIAL, loc_szRealSerial);
if (strcmp(loc_szSerial, loc_szRealSerial) == 0)
{
MessageBox (hDlg,
"You are now Registered!",
"Alert",
MB_OK
);
RegCreateKey (HKEY_CURRENT_USER,
"Software\\cobra",
&hkResult
);
}
else
MessageBox (hDlg,
"You're information is wrong.\n \
Please try again!",
"Alert",
MB_OK
);
}
return TRUE;
}
return FALSE;
}
===================================================================
资源脚本如下:
===================================================================
103 MENU
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
{
POPUP "&File"
{
MENUITEM "E&xit", 104
}
POPUP "&About"
{
MENUITEM "&Register", 65535, GRAYED
}
}
108 MENU
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
{
POPUP "&File"
{
MENUITEM "&Restart", 109
MENUITEM SEPARATOR
MENUITEM "E&xit", 104
}
POPUP "&About"
{
MENUITEM "&Register", 105
}
}
100 DIALOG 0, 0, 150, 100
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
CAPTION "Register!"
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
FONT 10, "Comic Sans MS"
{
CONTROL "Register here:", -1, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 5, 5, 50, 10
CONTROL "Name:", -1, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 5, 25, 20, 10
CONTROL "", 106, EDIT, ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 45, 25, 60, 10
CONTROL "Registration Key:", -1, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 5, 40, 45, 20
CONTROL "", 107, EDIT, ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 45, 50, 60, 10
CONTROL "&Submit", 110, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 20, 80, 45, 15
CONTROL "E&xit", 101, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 70, 80, 45, 15
}
A ICON "Icon_3.ico"
102 ICON "Icon_4.ico"
===================================================================
由上可见,在注册窗口类和创建窗口时会试图打开注册表指定项HKEY_CURRENT_USER\Software\Cobra,如果
失败的话,就会显示消息框;同时主窗口的菜单也分为两套,这时加载的是受限的一套。如果在程序中注册
成功,则会创建该注册表项;注册成功后的程序,主窗口的“File”菜单中多了一项“Restart”,如果选
择该菜单项,则退出程序并同时删除相应注册表项,如此下次启动的时候,程序又变成了“未注册”版本。
注册算法不仅是明码比较,而且从源代码来看(注意那个SetDlgItemText调用),随便输入一组注册信
息,程序在关键比较前竟然会将真序列号显示在序列号输入框中!(无法理解。猜测可能是打算搞注册机的
条件编译,而忘了写条件编译指令所致)也就是说,随便输一组数据,只要连点两下“注册”按钮,必定注
册成功。KeyGen过程的反汇编内容如下:
===================================================================
.text:004012FC ; int __cdecl KeyGen(LPSTR szUserName,LPSTR szSerial)
.text:004012FC _KeyGen proc near ; CODE XREF: ProcDlgReg+BFp
.text:004012FC
.text:004012FC var_D8 = dword ptr -0D8h
.text:004012FC var_D4 = dword ptr -0D4h
.text:004012FC var_D0 = dword ptr -0D0h
.text:004012FC var_CC = dword ptr -0CCh
.text:004012FC var_C8 = dword ptr -0C8h
.text:004012FC var_AC = dword ptr -0ACh
.text:004012FC var_A8 = dword ptr -0A8h
.text:004012FC var_A4 = dword ptr -0A4h
.text:004012FC var_A0 = dword ptr -0A0h
.text:004012FC Counter1 = dword ptr -9Ch
.text:004012FC var_14 = dword ptr -14h
.text:004012FC var_10 = dword ptr -10h
.text:004012FC uNameLen = dword ptr -0Ch
.text:004012FC szUserName = dword ptr 8
.text:004012FC szSerial = dword ptr 0Ch
.text:004012FC
.text:004012FC push ebp
.text:004012FD mov ebp, esp
.text:004012FF sub esp, 0D8h ; LPSTR
.text:00401305 mov eax, [ebp+szUserName]
.text:00401308 mov [esp+0D8h+var_D8], eax
.text:0040130B call strlen
.text:00401310 mov [ebp+uNameLen], eax
.text:00401313 mov [ebp+Counter1], 0
.text:0040131D
.text:0040131D loc_40131D: ; CODE XREF: _KeyGen+CBj
.text:0040131D mov eax, [ebp+Counter1]
.text:00401323 cmp eax, [ebp+uNameLen]
.text:00401326 jge loc_4013CC
.text:0040132C mov eax, [ebp+szUserName]
.text:0040132F add eax, [ebp+Counter1]
.text:00401335 movsx eax, byte ptr [eax]
.text:00401338 mov [ebp+var_10], eax
.text:0040133B cmp [ebp+Counter1], 0
.text:00401342 jz short loc_401362
.text:00401344 mov eax, [ebp+Counter1]
.text:0040134A add eax, [ebp+szUserName]
.text:0040134D dec eax
.text:0040134E movsx eax, byte ptr [eax]
.text:00401351 mov [ebp+var_A0], eax
.text:00401357 mov eax, [ebp+var_10]
.text:0040135A add [ebp+var_A0], eax
.text:00401360 jmp short loc_40136E
.text:00401362
.text:00401362 loc_401362: ; CODE XREF: _KeyGen+46j
.text:00401362 mov eax, [ebp+var_10]
.text:00401365 add eax, 5
.text:00401368 mov [ebp+var_A0], eax
.text:0040136E
.text:0040136E loc_40136E: ; CODE XREF: _KeyGen+64j
.text:0040136E mov eax, [ebp+var_A0]
.text:00401374 mov [ebp+var_10], eax
.text:00401377 lea eax, [ebp+var_10]
.text:0040137A xor dword ptr [eax], 12Ch
.text:00401380 mov edx, [ebp+var_10]
.text:00401383 mov eax, edx
.text:00401385 shl eax, 2
.text:00401388 add eax, edx
.text:0040138A shl eax, 2
.text:0040138D mov [ebp+var_10], eax
.text:00401390 mov eax, [ebp+var_10]
.text:00401393 mov [ebp+var_A4], eax
.text:00401399 cmp [ebp+var_A4], 0
.text:004013A0 jns short loc_4013A9
.text:004013A2 add [ebp+var_A4], 3
.text:004013A9
.text:004013A9 loc_4013A9: ; CODE XREF: _KeyGen+A4j
.text:004013A9 mov eax, [ebp+var_A4]
.text:004013AF sar eax, 2
.text:004013B2 mov [ebp+var_10], eax
.text:004013B5 mov edx, 0F0F0F0F0h
.text:004013BA lea eax, [ebp+var_10]
.text:004013BD and [eax], edx
.text:004013BF lea eax, [ebp+Counter1]
.text:004013C5 inc dword ptr [eax]
.text:004013C7 jmp loc_40131D
.text:004013CC
.text:004013CC loc_4013CC: ; CODE XREF: _KeyGen+2Aj
.text:004013CC mov eax, [ebp+uNameLen]
.text:004013CF mov [ebp+Counter1], eax
.text:004013D5
.text:004013D5 loc_4013D5: ; CODE XREF: _KeyGen+183j
.text:004013D5 cmp [ebp+Counter1], 0
.text:004013DC jle loc_401484
.text:004013E2 mov eax, [ebp+szUserName]
.text:004013E5 add eax, [ebp+Counter1]
.text:004013EB movsx eax, byte ptr [eax]
.text:004013EE mov [ebp+var_14], eax
.text:004013F1 mov eax, [ebp+Counter1]
.text:004013F7 cmp eax, [ebp+uNameLen]
.text:004013FA jz short loc_40141A
.text:004013FC mov eax, [ebp+Counter1]
.text:00401402 add eax, [ebp+szUserName]
.text:00401405 inc eax
.text:00401406 movsx eax, byte ptr [eax]
.text:00401409 mov [ebp+var_A8], eax
.text:0040140F mov eax, [ebp+var_14]
.text:00401412 add [ebp+var_A8], eax
.text:00401418 jmp short loc_401426
.text:0040141A
.text:0040141A loc_40141A: ; CODE XREF: _KeyGen+FEj
.text:0040141A mov eax, [ebp+var_14]
.text:0040141D add eax, 5
.text:00401420 mov [ebp+var_A8], eax
.text:00401426
.text:00401426 loc_401426: ; CODE XREF: _KeyGen+11Cj
.text:00401426 mov eax, [ebp+var_A8]
.text:0040142C mov [ebp+var_14], eax
.text:0040142F lea eax, [ebp+var_14]
.text:00401432 xor dword ptr [eax], 12Ch
.text:00401438 mov edx, [ebp+var_14]
.text:0040143B mov eax, edx
.text:0040143D shl eax, 2
.text:00401440 add eax, edx
.text:00401442 shl eax, 2
.text:00401445 mov [ebp+var_14], eax
.text:00401448 mov eax, [ebp+var_14]
.text:0040144B mov [ebp+var_AC], eax
.text:00401451 cmp [ebp+var_AC], 0
.text:00401458 jns short loc_401461
.text:0040145A add [ebp+var_AC], 3
.text:00401461
.text:00401461 loc_401461: ; CODE XREF: _KeyGen+15Cj
.text:00401461 mov eax, [ebp+var_AC]
.text:00401467 sar eax, 2
.text:0040146A mov [ebp+var_14], eax
.text:0040146D mov edx, 0F0F0F0F0h
.text:00401472 lea eax, [ebp+var_14]
.text:00401475 and [eax], edx
.text:00401477 lea eax, [ebp+Counter1]
.text:0040147D dec dword ptr [eax]
.text:0040147F jmp loc_4013D5
.text:00401484
.text:00401484 loc_401484: ; CODE XREF: _KeyGen+E0j
.text:00401484 mov eax, [ebp+var_14]
.text:00401487 mov [esp+0D8h+var_C8], eax
.text:0040148B mov eax, [ebp+var_14]
.text:0040148E add eax, [ebp+var_10]
.text:00401491 mov [esp+0D8h+var_CC], eax
.text:00401495 mov eax, [ebp+var_10]
.text:00401498 mov [esp+0D8h+var_D0], eax
.text:0040149C mov [esp+0D8h+var_D4], offset aRegLxLxKey ; "REG-%lX-%lX-KEY"
.text:004014A4 mov eax, [ebp+szSerial]
.text:004014A7 mov [esp+0D8h+var_D8], eax
.text:004014AA call wsprintfA
.text:004014AF leave
.text:004014B0 retn
.text:004014B0 _KeyGen endp
===================================================================
总结成C语言的格式就是(注意最后的wsprintf调用中,格式字符串中只有两个格式符,但是给了三个参
数,因此最后一个参数被忽略,实际有效的只是var_10和var_10 + var_14而已):
===================================================================
void KeyGen (char *szUserName, char *szSerial)
/* 算法过程 */
{
int uNameLen = strlen(szUserName);
int var_10, var_14, counter1;
for (counter1 = 0; counter1 < uNameLen; counter1 ++)
{
var_10 = szUserName[counter1];
var_10 = (var_10 + (counter1 == 0)? 5: szUserName[counter1 - 1]) ^ 0x12C;
var_10 = ((var_10 << 2) + var_10) << 2; //相当于var_10 *= 20;
if (var_10 < 0) var_10 = var_10 + 3;
var_10 = (var_10 / 4) & 0xF0F0F0F0;
}
for (counter1 = uNameLen; counter1 > 0; counter1 --)
{
var_14 = szUserName[counter1];
var_14 = (var_14 + (counter1 == uNameLen)? 5: szUserName[counter1 + 1]) ^ 0x12C;
var_14 = ((var_14 << 2) + var_14) << 2;
if (var_14 < 0) var_14 = var_14 + 3;
var_14 = (var_14 / 4) & 0xF0F0F0F0;
}
wsprintf (szSerial, "REG-%lX-%lX-KEY", var_10, var_10 + var_14);
return;
}
===================================================================
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
上传的附件: