-
-
[原创]版主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
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期)
赞赏
看原图
赞赏
雪币:
留言: