首页
社区
课程
招聘
llydd's crackme的逆向
发表于: 2006-10-18 21:01 5494

llydd's crackme的逆向

2006-10-18 21:01
5494

下载地址: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
}

======================================================


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (4)
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
好文,支持剑兄!
2006-10-18 21:10
0
雪    币: 256
活跃值: (10)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
3
好文,支持.
冲天剑兄弟的文章写的很清晰!
我挺佩服的!
学习,学习,再学习.
2006-10-19 07:30
0
雪    币: 380
活跃值: (101)
能力值: ( LV13,RANK:370 )
在线值:
发帖
回帖
粉丝
4
高手啊,高手无处不在
我都觉得我像被剥光了衣服站在论坛里,完完全全的走光
自卑的很,
可是这个CRACKME好像根本写不出注册机来,冲天兄?
2006-10-19 18:21
0
雪    币: 433
活跃值: (176)
能力值: ( LV13,RANK:1250 )
在线值:
发帖
回帖
粉丝
5
最初由 llydd 发布

可是这个CRACKME好像根本写不出注册机来,冲天兄?


目前我所知道的是由用户名求注册码需要解方程,而不能表示成明显的函数,即序列号=f(用户名)的形式。但是对于任意给定的用户名,同样也没有证明方程无解,即没有对应的序列号。
2006-10-19 18:41
0
游客
登录 | 注册 方可回帖
返回
//