【破解作者】 Night
【作者主页】 http://www.freecracker.com
【使用工具】 OD
【破解平台】 Win7
【软件名称】 ncrackme.exe
------------------------------------------------------------------------------------------------------------
1.使用OD附加程序,使用 bp GetDlgItemTextA 下断点 程序来到下面的地址处。
00401230 /$ 8B0D BC564000 mov ecx,dword ptr ds:[0x4056BC]
00401236 |. 83EC 30 sub esp,0x30
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.GetDlgIte>; user32.GetDlgItemTextA
00401245 |. 6A 10 push 0x10 ; /Count = 10 (16.)
00401247 |. 50 push eax ; |Buffer
00401248 |. 68 E8030000 push 0x3E8 ; |ControlID = 3E8 (1000.)
0040124D |. 51 push ecx ; |hWnd => 000B0674 (class='#32770',parent=000B0638)
0040124E |. 33DB xor ebx,ebx ; |
00401250 |. FFD6 call esi ; \GetDlgItemTextA
00401252 |. 83F8 03 cmp eax,0x3 ; 用户名不能少于三位
00401255 |. 73 0B jnb Xncrackme.00401262
00401257 |. 5E pop esi
00401258 |. B8 01000000 mov eax,0x1
0040125D |. 5B pop ebx
0040125E |. 83C4 30 add esp,0x30
00401261 |. C3 retn
2.由上面代码可以判断出程序读取用户所输入的用户名 并且判断用户名不能少于三位,当用户输入的数据满足条件的情况下程序会跳到下面的代码处
00401262 |> \A1 BC564000 mov eax,dword ptr ds:[0x4056BC] ;
00401267 |. 8D5424 28 lea edx,dword ptr ss:[esp+0x28] ;
0040126B |. 6A 10 push 0x10
0040126D |. 52 push edx
0040126E |. 68 E9030000 push 0x3E9
00401273 |. 50 push eax
00401274 |. FFD6 call esi ; 获取Key值
00401276 |. 0FBE4424 08 movsx eax,byte ptr ss:[esp+0x8] ; 获取用户名第一位
0040127B |. 0FBE4C24 09 movsx ecx,byte ptr ss:[esp+0x9] ; 获取用户名第二位
00401280 |. 99 cdq ; 把edx扩展为eax的高位
00401281 |. F7F9 idiv ecx ; 用户名的第一位与用户名的第二位相除 商存放在eax中 ,余数存放在edx中
00401283 |. 8BCA mov ecx,edx ; ecx中保存的是计算出的余数
00401285 |. 83C8 FF or eax,0xFFFFFFFF ; 用商 与 0xFFFFFFFF 取或运算 结果存放到eax中
00401288 |. 0FBE5424 0A movsx edx,byte ptr ss:[esp+0xA] ; 保存用户名的第三位
0040128D |. 0FAFCA imul ecx,edx ; 用余数与用户名的第三位做乘法运算 结果保存到ecx中
00401290 |. 41 inc ecx ; ecx = ecx + 1
00401291 |. 33D2 xor edx,edx ; 清空edx
00401293 |. F7F1 div ecx ; eax / ecx 商存放到 eax 中 余数存放到edx中
00401295 |. 50 push eax ; 把上次计算出的商压入栈
00401296 |. E8 A5000000 call ncrackme.00401340 ; 保存eax中的结果到0x4050AC
0040129B |. 83C4 04 add esp,0x4
0040129E |. 33F6 xor esi,esi
004012A0 |> E8 A5000000 /call ncrackme.0040134A
{
0040134A /$ A1 AC504000 mov eax,dword ptr ds:[0x4050AC] ; 获取0x4050AC中数据存放到eax中
0040134F |. 69C0 FD430300 imul eax,eax,0x343FD ; eax = eax * 0x343FD
00401355 |. 05 C39E2600 add eax,0x269EC3 ; eax = eax + 0x269EC3
0040135A |. A3 AC504000 mov dword ptr ds:[0x4050AC],eax ; 保存eax中的数据到 0x4050AC中
0040135F |. C1F8 10 sar eax,0x10 ; eax = eax >> 0x10
00401362 |. 25 FF7F0000 and eax,0x7FFF ; eax = eax & 0x7FFF
00401367 \. C3 retn }
004012A5 |. 99 |cdq ; 把edx扩展为eax的高位
004012A6 |. B9 1A000000 |mov ecx,0x1A ; ecx = 0x1A
004012AB |. F7F9 |idiv ecx ; eax / ecx 商保存到eax 中 余数保存到edx中
004012AD |. 80C2 41 |add dl,0x41 ; 把余数的低16位与0x41 求和
004012B0 |. 885434 18 |mov byte ptr ss:[esp+esi+0x18],dl ; 保存上次计算的结果
004012B4 |. 46 |inc esi
004012B5 |. 83FE 0F |cmp esi,0xF
004012B8 |.^ 72 E6 \jb Xncrackme.004012A0
004012BA |. 57 push edi
004012BB |. 8D7C24 0C lea edi,dword ptr ss:[esp+0xC] ; edi 指向 用户名
004012BF |. 83C9 FF or ecx,0xFFFFFFFF ; ecx = ecx | 0xFFFFFFFF
004012C2 |. 33C0 xor eax,eax ; eax = 0
004012C4 |. 33F6 xor esi,esi ; esi = 0
004012C6 |. F2:AE repne scas byte ptr es:[edi] ; 和下面两条汇编指令联合使用 求用户名长度 保存到 ecx 中
004012C8 |. F7D1 not ecx
004012CA |. 49 dec ecx
004012CB |. 74 59 je Xncrackme.00401326 ; 用户名长度不能为 0
004012CD |> 8A4434 0C /mov al,byte ptr ss:[esp+esi+0xC] ; 获取用户名第一位保存到al 中
004012D1 |. C0F8 05 |sar al,0x5 ; al = al >> 0x5
004012D4 |. 0FBEC0 |movsx eax,al ; al 保存到 eax 中
004012D7 |. 8D1480 |lea edx,dword ptr ds:[eax+eax*4] ; edx = eax + eax * 4
004012DA |. 8D04D0 |lea eax,dword ptr ds:[eax+edx*8] ; eax = eax + edx * 8
004012DD |. 8D0440 |lea eax,dword ptr ds:[eax+eax*2] ; eax = eax + eax * 2
004012E0 |. 85C0 |test eax,eax ; 判断eax 是否为 0
004012E2 |. 7E 0A |jle Xncrackme.004012EE
004012E4 |. 8BF8 |mov edi,eax ; edi = eax 做为循环次数
004012E6 |> E8 5F000000 |/call ncrackme.0040134A
004012EB |. 4F ||dec edi
004012EC |.^ 75 F8 |\jnz Xncrackme.004012E6
004012EE |> E8 57000000 |call ncrackme.0040134A
004012F3 |. 99 |cdq
004012F4 |. B9 1A000000 |mov ecx,0x1A ; ecx = 0x1A
004012F9 |. 8D7C24 0C |lea edi,dword ptr ss:[esp+0xC] ; edi 指向用户名
004012FD |. F7F9 |idiv ecx ; eax / ecx 商存放到 eax 中 余数存放到edx中
004012FF |. 0FBE4C34 2C |movsx ecx,byte ptr ss:[esp+esi+0x2C] ; 获取key值的第一位
00401304 |. 80C2 41 |add dl,0x41 ; dl = dl + 0x41 dl 是余数
00401307 |. 0FBEC2 |movsx eax,dl ; 把计算后的dl 存放到 eax中
0040130A |. 2BC1 |sub eax,ecx ; eax = eax - ecx eax是计算后的dl ecx 是key值的第一位
0040130C |. 885434 1C |mov byte ptr ss:[esp+esi+0x1C],dl ; 保存dl的值
00401310 |. 99 |cdq
00401311 |. 33C2 |xor eax,edx ; eax = eax ^ edx
00401313 |. 83C9 FF |or ecx,0xFFFFFFFF ; ecx = ecx | 0xFFFFFFFF ecx是用户名的第一位
00401316 |. 2BC2 |sub eax,edx ; eax = eax - edx
00401318 |. 03D8 |add ebx,eax ; ebx = ebx + eax
0040131A |. 33C0 |xor eax,eax ; eax = 0
0040131C |. 46 |inc esi ; esi = esi + 1
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 Xncrackme.004012CD
00401326 |> 5F pop edi ; 0012FB18
00401327 |. 8BC3 mov eax,ebx
00401329 |. 5E pop esi
0040132A |. 5B pop ebx
0040132B |. 83C4 30 add esp,0x30
0040132E \. C3 retn
经过分析得知这一段应该就是程序的算法部分了。具体分析过程都写到了注释中。注释中涉及到的循环代码都是第一轮循环的的内容。
3.注册机实现如下:
// CrackMe1.cpp : 定义控制台应用程序的入口点。
//
/***
* ncrackme.exe 程序注册机
* 完成时间: 2014年8月20日 17:30
* 完成人 : Night
*/ #include "stdafx.h"
#include "string.h"
int fun0040134A(unsigned int *result)
{
unsigned int value = 0;
*result = (*result) * 0x343FD;
*result = (*result) + 0x269EC3;
value = * result ;
value = value >> 0x10;
value = value & 0x7FFF;
return value;
}
int _tmain(int argc, _TCHAR* argv[])
{
//存放用户名
char userName [100] = { 0 };
//获取Key值
char keyValue [100] = { 0 };
//商
unsigned int quotient = 0 ;
//余数
unsigned int mod = 0 ;
//临时存放计算变量
unsigned int value = 0 ;
unsigned int value1 = 0 ;
unsigned int result = 0 ;
unsigned int retValue = 0 ;
//存放结果
int decodeValue[0xF] = {0} ;
//循环变量
int i = 0;
//存放用户名长度
int userNameLen = 0;
//输入用户名
printf("请输入用户名:");
gets_s(userName);
printf("\n请输入Key值:");
//输入Key值
gets_s(keyValue);
if(strlen(userName)< 3)
{
printf("用户名不能少于三位!");
return 0;
}
quotient = userName[0] / userName[1];
mod = userName[0] % userName[1];
value = quotient | 0xFFFFFFFF;
value1 = mod * userName[2];
value1 = value1 + 1 ;
quotient = value / value1 ;
mod = value % value1 ;
result = quotient ;
do
{
retValue = fun0040134A(&result);
value = 0x1A;
quotient = retValue / value ;
mod = retValue % value ;
mod = mod + 0x41;
decodeValue[i] = mod;
i++;
} while (i<0xF);
userNameLen = strlen(userName);
if (userNameLen == 0)
{
printf("用户名长度不能为0!");
return 0;
}
i = 0;
do
{
value = userName[i] >> 0x5 ;
value1 = value + value * 4;
value = value + value1 * 8;
value = value + value * 2;
if (value == 0)
{
printf("计算的数据不能为0 !");
return 0;
}
do
{
retValue = fun0040134A(&result);
value -- ;
} while (value);
retValue = fun0040134A(&result);
value = 0x1A;
quotient = retValue / value;
mod = retValue % value;
value = keyValue[i];
value1 = mod+0x41;
decodeValue[i] = value1;
value1 = value1 ^ 0;
i++;
} while (i<strlen(userName));
for (int i = 0; i < 0xF; i++)
{
printf("%X\t",decodeValue[i]);
if((i+1)%8 == 0 )
{
printf("\n");
}
}
printf("\n注册码为:\n");
for (int i = 0; i < 0xF; i++)
{
printf("%c",decodeValue[i]);
}
printf("\n");
return 0;
}
PS:编译环境是VS2010 新手练习之作,有哪里不正确请大神指教。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课