首页
社区
课程
招聘
首次尝试逆向C语言的SDK程序
发表于: 2006-8-26 00:07 7335

首次尝试逆向C语言的SDK程序

2006-8-26 00:07
7335

下载地址: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直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (3)
雪    币: 291
活跃值: (213)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
2
Very Good!

不过观赏价值大于实用价值,因为实际的软件往往十分复杂,即便是CrackMe,能够在一两天内完全逆向出来的可能也只有ASM和C SDK编写的了,而且,只要把代码稍加混淆处理可能就十分令人头痛了。

当然,能够欣赏到这样的文章也是一种享受,期待楼主的下一个作品
2006-8-26 00:28
0
雪    币: 333
活跃值: (11)
能力值: ( LV12,RANK:770 )
在线值:
发帖
回帖
粉丝
3
楼主十分的辛苦啊!
2006-8-26 09:27
0
雪    币: 338
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
学习啊!!
2006-8-26 12:21
0
游客
登录 | 注册 方可回帖
返回
//