首页
社区
课程
招聘
[原创]版主riijj的例子ncrackme破解过程(求邀请码)
发表于: 2010-7-8 15:44 4052

[原创]版主riijj的例子ncrackme破解过程(求邀请码)

2010-7-8 15:44
4052
菜鸟刚开始学习破解,借用版主riijj的例子ncrackme进行学习。在研究过程中分别做了简单爆破、获得序列号和编写注册机,先分别记录如下。小第在这里抛砖了,有什么不对的希望各位牛人不吝赐教。

1 简单爆破
用OllyDbg打开ncrackme.exe后,可以看到程序在比较序列号后会利用MessageBox弹出成功或失败的信息。CALL ncrackme.00402230是调用比较算法,TEST EAX,TAX则是看EAX是否为0,为0则注册成功并弹出成功对话框,如果不为0则调转到ncrackme.0040108F,弹出失败对话框。

00401050   .  817C24 08 110>CMP DWORD PTR SS:[ESP+8],111                 ;第一个参数,消息
00401058   .  75 74         JNZ SHORT ncrackme.004010CE
0040105A   .  8B4424 0C     MOV EAX,DWORD PTR SS:[ESP+C]                    ; 第二个参数,控件ID
0040105E   .  66:3D EA03    CMP AX,3EA
00401062   .  75 42         JNZ SHORT ncrackme.004010A6
00401064   .  E8 C7010000   CALL ncrackme.00401230
00401069   .  85C0          TEST EAX,EAX
0040106B   .  6A 00         PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL
0040106D   .  68 80504000   PUSH ncrackme.00405080                   ; |Title = "ncrackme"
00401072   .  75 1B         JNZ SHORT ncrackme.0040108F              ; |
00401074   .  A1 B8564000   MOV EAX,DWORD PTR DS:[4056B8]            ; |
00401079   .  68 64504000   PUSH ncrackme.00405064                   ; |Text = "Registration successful."
0040107E   .  50            PUSH EAX                                 ; |hOwner => NULL
0040107F   .  FF15 C0404000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA
00401085   .  E8 A6020000   CALL ncrackme.00401330
0040108A   .  33C0          XOR EAX,EAX
0040108C   .  C2 1000       RETN 10
0040108F   >  8B0D B8564000 MOV ECX,DWORD PTR DS:[4056B8]            ; |
00401095   .  68 50504000   PUSH ncrackme.00405050                   ; |Text = "Registration fail."
0040109A   .  51            PUSH ECX                                 ; |hOwner => NULL
0040109B   .  FF15 C0404000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA
004010A1   .  33C0          XOR EAX,EAX
004010A3   .  C2 1000       RETN 10
004010A6   >  66:3D EB03    CMP AX,3EB
004010AA   .  75 22         JNZ SHORT ncrackme.004010CE
004010AC   .  A1 C0564000   MOV EAX,DWORD PTR DS:[4056C0]
004010B1   .  85C0          TEST EAX,EAX
004010B3   .  74 19         JE SHORT ncrackme.004010CE
004010B5   .  8B15 B8564000 MOV EDX,DWORD PTR DS:[4056B8]
004010BB   .  6A 00         PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL
004010BD   .  68 80504000   PUSH ncrackme.00405080                   ; |Title = "ncrackme"
004010C2   .  68 30504000   PUSH ncrackme.00405030                   ; |Text = "good function, i was cracked"
004010C7   .  52            PUSH EDX                                 ; |hOwner => NULL
004010C8   .  FF15 C0404000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA
004010CE   >  33C0          XOR EAX,EAX
004010D0   .  C2 1000       RETN 10

所以只需要将JNZ SHORT ncrackme.0040108F操作去掉就可以了。具体操作如下:双击00401072并用空操作(NOP)替换,保存到可执行文件。这样无论输入什么用户名和序列号都会弹出注册成功的对话框。修改后的代码如下:

00401050   .  817C24 08 110>CMP DWORD PTR SS:[ESP+8],111
00401058   .  75 74         JNZ SHORT ncrackme.004010CE
0040105A   .  8B4424 0C     MOV EAX,DWORD PTR SS:[ESP+C]
0040105E   .  66:3D EA03    CMP AX,3EA
00401062   .  75 42         JNZ SHORT ncrackme.004010A6
00401064   .  E8 C7010000   CALL ncrackme.00401230
00401069   .  85C0          TEST EAX,EAX
0040106B   .  6A 00         PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL
0040106D   .  68 80504000   PUSH ncrackme.00405080                   ; |Title = "ncrackme"
00401072   .  90            NOP                                      ; |
00401073   .  90            NOP                                      ; |
00401074   .  A1 B8564000   MOV EAX,DWORD PTR DS:[4056B8]            ; |
00401079   .  68 64504000   PUSH ncrackme.00405064                   ; |Text = "Registration successful."
0040107E   .  50            PUSH EAX                                 ; |hOwner => NULL
0040107F   .  FF15 C0404000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA
00401085   .  E8 A6020000   CALL ncrackme.00401330
0040108A   .  33C0          XOR EAX,EAX
0040108C   .  C2 1000       RETN 10
0040108F   .  8B0D B8564000 MOV ECX,DWORD PTR DS:[4056B8]            ; |
00401095   .  68 50504000   PUSH ncrackme.00405050                   ; |Text = "Registration fail."
0040109A   .  51            PUSH ECX                                 ; |hOwner => NULL
0040109B   .  FF15 C0404000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA
004010A1   .  33C0          XOR EAX,EAX
004010A3   .  C2 1000       RETN 10
004010A6   >  66:3D EB03    CMP AX,3EB
004010AA   .  75 22         JNZ SHORT ncrackme.004010CE
004010AC   .  A1 C0564000   MOV EAX,DWORD PTR DS:[4056C0]
004010B1   .  85C0          TEST EAX,EAX
004010B3   .  74 19         JE SHORT ncrackme.004010CE
004010B5   .  8B15 B8564000 MOV EDX,DWORD PTR DS:[4056B8]
004010BB   .  6A 00         PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL
004010BD   .  68 80504000   PUSH ncrackme.00405080                   ; |Title = "ncrackme"
004010C2   .  68 30504000   PUSH ncrackme.00405030                   ; |Text = "good function, i was cracked"
004010C7   .  52            PUSH EDX                                 ; |hOwner => NULL
004010C8   .  FF15 C0404000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA
004010CE   >  33C0          XOR EAX,EAX
004010D0   .  C2 1000       RETN 10

2 获取序列号
00401230  /$  8B0D BC564000 MOV ECX,DWORD PTR DS:[4056BC]
00401236  |.  83EC 30       SUB ESP,30                            ; |48个字节
00401239  |.  8D4424 00     LEA EAX,DWORD PTR SS:[ESP]
0040123D  |.  53            PUSH EBX
0040123E  |.  56            PUSH ESI
0040123F  |.  8B35 94404000 MOV ESI,DWORD PTR DS:[<&USER32.GetDlgItemText>;  USER32.GetDlgItemTextA
00401245  |.  6A 10         PUSH 10                                  ; /Count = 10 (16.)
00401247  |.  50            PUSH EAX                                 ; |Buffer
00401248  |.  68 E8030000   PUSH 3E8                                 ; |ControlID = 3E8 (1000.)
0040124D  |.  51            PUSH ECX                                 ; |hWnd => NULL
0040124E  |.  33DB          XOR EBX,EBX                              ; |
00401250  |.  FFD6          CALL ESI                                 ; \GetDlgItemTextA
00401252  |.  83F8 03       CMP EAX,3
00401255  |.  73 0B         JNB SHORT ncrackme.00401262
00401257  |.  5E            POP ESI
00401258  |.  B8 01000000   MOV EAX,1
0040125D  |.  5B            POP EBX
0040125E  |.  83C4 30       ADD ESP,30
00401261  |.  C3            RETN
00401262  |>  A1 BC564000   MOV EAX,DWORD PTR DS:[4056BC]
00401267  |.  8D5424 28     LEA EDX,DWORD PTR SS:[ESP+28]
0040126B  |.  6A 10         PUSH 10
0040126D  |.  52            PUSH EDX                                 ;  Buffer
0040126E  |.  68 E9030000   PUSH 3E9                                 ;  ControlID = 3E9
00401273  |.  50            PUSH EAX                                ; |hWnd => NULL

00401274  |.  FFD6          CALL ESI                                 ;  GetDlgItemTextA
00401276  |.  0FBE4424 08   MOVSX EAX,BYTE PTR SS:[ESP+8]
0040127B  |.  0FBE4C24 09   MOVSX ECX,BYTE PTR SS:[ESP+9]
00401280  |.  99            CDQ
00401281  |.  F7F9          IDIV ECX
00401283  |.  8BCA          MOV ECX,EDX
00401285  |.  83C8 FF       OR EAX,FFFFFFFF
00401288  |.  0FBE5424 0A   MOVSX EDX,BYTE PTR SS:[ESP+A]
0040128D  |.  0FAFCA        IMUL ECX,EDX
00401290  |.  41            INC ECX
00401291  |.  33D2          XOR EDX,EDX
00401293  |.  F7F1          DIV ECX
00401295  |.  50            PUSH EAX
00401296  |.  E8 A5000000   CALL ncrackme.00401340
0040129B  |.  83C4 04       ADD ESP,4
0040129E  |.  33F6          XOR ESI,ESI
004012A0  |>  E8 A5000000   /CALL ncrackme.0040134A
004012A5  |.  99            |CDQ
004012A6  |.  B9 1A000000   |MOV ECX,1A
004012AB  |.  F7F9          |IDIV ECX
004012AD  |.  80C2 41       |ADD DL,41
004012B0  |.  885434 18     |MOV BYTE PTR SS:[ESP+ESI+18],DL
004012B4  |.  46            |INC ESI
004012B5  |.  83FE 0F       |CMP ESI,0F
004012B8  |.^ 72 E6         \JB SHORT ncrackme.004012A0

从上面的代码可以看出,申请的内存为48字节,其中前16字节存储第一个Edit控件(ID为3E8)的内容即用户名,最后16字节存储第二个Edit控件(ID为3E9)的内容即序列号,中间16个字节存在临时值(其实是存在根据用户名计算出来的序列号)。然后根据输入的和计算得到的序列号再运算出一个值,如果该值为0则注册成功。
所以用户输入用户名MJK和序列号123点击注册后,只需要设置断点就可以获得计算的序列号。现设置断点在0040130C处,循环着将计算结果DL依次保存到中间16个字节处,也即ESP+ESI+1C处,循环次数就是用户名的长度。所以只需要将每次的DL值记录下来,现记录为57 46 46,也即WFF。经验证用户名MJK和序列号WFF完全正确。

004012CB  |. /74 59         JE SHORT ncrackme.00401326
004012CD  |> |8A4434 0C     /MOV AL,BYTE PTR SS:[ESP+ESI+C]
004012D1  |. |C0F8 05       |SAR AL,5
004012D4  |. |0FBEC0        |MOVSX EAX,AL
004012D7  |. |8D1480        |LEA EDX,DWORD PTR DS:[EAX+EAX*4]
004012DA  |. |8D04D0        |LEA EAX,DWORD PTR DS:[EAX+EDX*8]
004012DD  |. |8D0440        |LEA EAX,DWORD PTR DS:[EAX+EAX*2]
004012E0  |. |85C0          |TEST EAX,EAX
004012E2  |. |7E 0A         |JLE SHORT ncrackme.004012EE
004012E4  |. |8BF8          |MOV EDI,EAX
004012E6  |> |E8 5F000000   |/CALL ncrackme.0040134A
004012EB  |. |4F            ||DEC EDI
004012EC  |.^|75 F8         |\JNZ SHORT ncrackme.004012E6
004012EE  |> |E8 57000000   |CALL ncrackme.0040134A
004012F3  |. |99            |CDQ
004012F4  |. |B9 1A000000   |MOV ECX,1A
004012F9  |. |8D7C24 0C     |LEA EDI,DWORD PTR SS:[ESP+C]
004012FD  |. |F7F9          |IDIV ECX
004012FF  |. |0FBE4C34 2C   |MOVSX ECX,BYTE PTR SS:[ESP+ESI+2C]
00401304  |. |80C2 41       |ADD DL,41
00401307  |. |0FBEC2        |MOVSX EAX,DL
0040130A  |. |2BC1          |SUB EAX,ECX
0040130C  |. |885434 1C     |MOV BYTE PTR SS:[ESP+ESI+1C],DL
00401310  |. |99            |CDQ
00401311  |. |33C2          |XOR EAX,EDX
00401313  |. |83C9 FF       |OR ECX,FFFFFFFF
00401316  |. |2BC2          |SUB EAX,EDX
00401318  |. |03D8          |ADD EBX,EAX
0040131A  |. |33C0          |XOR EAX,EAX
0040131C  |. |46            |INC ESI
0040131D  |. |F2:AE         |REPNE SCAS BYTE PTR ES:[EDI]
0040131F  |. |F7D1          |NOT ECX
00401321  |. |49            |DEC ECX
00401322  |. |3BF1          |CMP ESI,ECX
00401324  |.^|72 A7         \JB SHORT ncrackme.004012CD

3 写出注册机
写出注册机就需要完全明白程序是如何根据用户名计算出序列号的,然后自己编写代码模拟实现,当然用任何语言编写都是可以的。
经过分析,Register按钮的ID为0x3EA,Locked function按钮的ID为0x3EB,Reg name后面的Edit控件ID为0x3E8,Reg key后面的Edit按钮ID为0x3E9。声明int型全局变量cracked,初始化为0,地址为0x004056C0(可能有变动),声明int型全局变量result,初始化为0,地址为0x004050AC(可能有变动)。全局变量在使用过程中直接利用地址进行操作。

/*
* 调用比较函数,并根据比较结果判断注册成功或失败。
*/
if (cmd == WM_COMMAND) // CMP DWORD PTR SS:[ESP+8], 111
{
   if (ctrlid == 0x000003EA ) // MOV EAX,DWORD PTR SS:[ESP+C]
   {
                DWORD style = MB_OK|MB_APPLMODAL;
                char title[] = "ncrackme";

                if ( CmpFunc() == 0 )  // CALL 00401230
                {
                        MessageBox(NULL, "Registeration successful.", title, style);
                        cracked = 1; // CALL 00401330
                        return 0;      // XOR EAX, EAX
                }
                else // JNZ short 0040108F
                {
                        MessageBox(NULL, "Registeration fail.", title, style);
                        return 0;      // XOR EAX, EAX
                }
        }
        else if (ctrlid == 0x000003EB) // JNZ SHORT 004010A6
        {
                if (cracked != 0)
                {
                        MessageBox(NULL, "good function, i was cracked",
"ncrackme", MB_OK|MB_APPLMODAL);
                }
        }
}

/*
* 获取用户名并计算序列号,与输入的序列号进行比较
*/
// 字段如下, 均为16字节,共48字节: | name | temp | code |
int CmpFunc()
{
   int cmpresult = 0;

   char buffer[48]={0};
   char* name = buffer;
   char* temp = buffer+16;
   char* code = buffer+32;
   

   // hWnd 为对话框句柄
   // 字符串不超过16个(包括结束符)
   // 返回值为字符串个数(不包括结束符)
   if (::GetDlgItemTextA(hWnd, 0x000003E8, name, 16) < 3)
   {
       return 1;
   }
   
   // strlen(name)<3 则直接返回1

   // 字符串不超过16个(包括结束符)
   GetDlgItemTextA(hWnd, 0x000003E9, code, 16);

   int name0 = (int)(signed char)name[0];
   int name1 = (int)(signed char)name[1];
   int name2 = (int)(signed char)name[2];

   result = 0xFFFFFFFF/(1+name0%name1*name2); // CALL 00401340
   for(int i=0; i<15; i++)
   {
      temp[i] = char(CalcuResult()%0x0000001A)+0x41;
   }
   
   for(int j=1; j<0xffffffff && name[j-1]!='\0'; j++);
   j -= 1;  // 获得注册名长度

   if(j != 0)
   {
      for(int k=0; k<j; k++)
      {
          int tmp = (int)(signed char)(name[k]/(2^5));
          int tmp1 = tmp + tmp*4;
          tmp = tmp + tmp1*8;
          tmp = tmp + tmp*2;
          while (tmp > 0) // JLE SHORT 004012EE
          {
              CalcuResult();
              tmp -= 1;
          }

          temp[k] = char (CalcuResult()%0x0000001A)+0x41;

          int x = (int)(signed char)(temp[k]);
          int y = (int)(signed char)(code[k]);
          cmpresult += abs(x - y) - x>>31;  
   }
   
   return cmpresult;
}
// 0040134A
int CalcuResult()
{
     result = result * 0x000343FD + 0x00269EC3;
     return (result>>16)) & 0x00007FFF;
}

根据分析得到的代码,利用VC编写了注册机ncrackme注册机.exe。欢迎试用和指正!
ncrackme注册机.rar
ncrackme破解过程.doc

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 14
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
不错不错 学习下
2010-7-9 11:24
0
游客
登录 | 注册 方可回帖
返回
//