系统 : Windows xp
程序 : crackme
程序下载地址 :http://pan.baidu.com/s/1i41oh9r
要求 : 注册机编写
使用工具 : IDA Pro & OD
可在“PEDIY CrackMe 2007”中查找关于此程序的讨论,标题为“简单分析crackme算法之一”。
运行程序,在Help-》Register菜单项中注册,输入测试用户名密码。程序提示错误,错误字串为:“No luck there, mate!”。打开IDA载入程序,通过字串表定位错误字串,并通过交叉参考在关键算法处下断:
00401209 > \6A 00 push 0 ; /lParam = NULL 0040120B . 68 53124000 push 00401253 ; |DlgProc = CRACKME.00401253 00401210 . FF75 08 push dword ptr [ebp+8] ; |hOwner 00401213 . 68 15214000 push 00402115 ; |pTemplate = "DLG_REGIS" 00401218 . FF35 CA204000 push dword ptr [4020CA] ; |hInst = 00400000 0040121E . E8 7D020000 call <jmp.&USER32.DialogBoxParamA> ; \DialogBoxParamA 00401223 . 83F8 00 cmp eax, 0 00401226 .^ 74 BE je short 004011E6 00401228 . 68 8E214000 push 0040218E ; 用户名入栈 0040122D . E8 4C010000 call 0040137E ; 根据用户名进行一些操作得出一个值 00401232 . 50 push eax 00401233 . 68 7E214000 push 0040217E ; 序列号入栈 00401238 . E8 9B010000 call 004013D8 ; 根据序列号进行一些操作得出一个值 0040123D . 83C4 04 add esp, 4 ; 平衡堆栈 00401240 . 58 pop eax 00401241 . 3BC3 cmp eax, ebx ; F(用户名) == F(序列号)? 00401243 . 74 07 je short 0040124C
40137E处汇编代码:
0040137E /$ 8B7424 04 mov esi, dword ptr [esp+4] ; 取用户名 00401382 |. 56 push esi 00401383 |> 8A06 /mov al, byte ptr [esi] 00401385 |. 84C0 |test al, al ; 如果迭代结束 00401387 |. 74 13 |je short 0040139C ; 跳出循环 00401389 |. 3C 41 |cmp al, 41 ; 字符小于0x41,则提示错误 0040138B |. 72 1F |jb short 004013AC 0040138D |. 3C 5A |cmp al, 5A ; 如果字符大于等于5A, 0040138F |. 73 03 |jnb short 00401394 ; 则转化字符为大写 00401391 |. 46 |inc esi ; 循环变量自增 00401392 |.^ EB EF |jmp short 00401383 00401394 |> E8 39000000 |call 004013D2 00401399 |. 46 |inc esi ; 循环变量自增 0040139A |.^ EB E7 \jmp short 00401383 0040139C |> 5E pop esi ; 取用户名 0040139D |. E8 20000000 call 004013C2 004013A2 |. 81F7 78560000 xor edi, 5678 004013A8 |. 8BC7 mov eax, edi ; 异或结果保存在eax里 004013AA |. EB 15 jmp short 004013C1 004013AC |> 5E pop esi 004013AD |. 6A 30 push 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL 004013AF |. 68 60214000 push 00402160 ; |Title = "No luck!" 004013B4 |. 68 69214000 push 00402169 ; |Text = "No luck there, mate!" 004013B9 |. FF75 08 push dword ptr [ebp+8] ; |hOwner 004013BC |. E8 79000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA 004013C1 \> C3 retn 004013C2 /$ 33FF xor edi, edi 004013C4 |. 33DB xor ebx, ebx 004013C6 |> 8A1E /mov bl, byte ptr [esi] 004013C8 |. 84DB |test bl, bl ; 如果迭代结束 004013CA |. 74 05 |je short 004013D1 ; 跳出循环 004013CC |. 03FB |add edi, ebx ; 累加字符值 004013CE |. 46 |inc esi ; 循环变量自增 004013CF |.^ EB F5 \jmp short 004013C6 004013D1 \> C3 retn ; 结果保存在edi 004013D2 /$ 2C 20 sub al, 20 004013D4 |. 8806 mov byte ptr [esi], al 004013D6 \. C3 retn
4013D8处汇编代码:
004013D8 /$ 33C0 xor eax, eax 004013DA |. 33FF xor edi, edi 004013DC |. 33DB xor ebx, ebx 004013DE |. 8B7424 04 mov esi, dword ptr [esp+4] ; 取序列号 004013E2 |> B0 0A /mov al, 0A 004013E4 |. 8A1E |mov bl, byte ptr [esi] 004013E6 |. 84DB |test bl, bl ; 如果迭代结束 004013E8 |. 74 0B |je short 004013F5 ; 则跳出循环 004013EA |. 80EB 30 |sub bl, 30 ; 将字符转化为它对应的数字 004013ED |. 0FAFF8 |imul edi, eax 004013F0 |. 03FB |add edi, ebx 004013F2 |. 46 |inc esi ; 循环变量自增 004013F3 |.^ EB ED \jmp short 004013E2 004013F5 |> 81F7 34120000 xor edi, 1234 ; 结果与0x1234异或 004013FB |. 8BDF mov ebx, edi ; 存入ebx 004013FD \. C3 retn
以上就是关键处代码,通过观察不难发现,该程序采用的是非明码比较(F(用户名)=F(序列号))。这样的程序我们用高级语言写出F(用户名),再算出F(序列号)的逆算法即可。
打开http://www.cnblogs.com/ZRBYYXDM/p/5115596.html中搭建的框架,修改OnOk函数如下:
void CKengen_TemplateDlg::OnBtnDecrypt() { // TODO: Add your control notification handler code here CString str; GetDlgItemText( IDC_EDIT_NAME,str ); //获取用户名字串基本信息。 int len = str.GetLength(); bool StrIsOk = true; for ( int i = 0 ; i != len ; i++ ){ if ( str[i] < 0x41 ){ StrIsOk = false; break; } } if ( StrIsOk ){ //格式控制。 str.MakeUpper(); //转化为大写。 unsigned int sum = 0; int i; for ( i = 0 ; i != len ; i++ ) sum += str[i]; sum ^= 0x5678; /* //模拟F(序列号) CString Serial = "1234"; unsigned int res = 0,num = 0; int al = 0xA; for ( i = 0 ; i != len ; i++ ){ num = Serial[i] - 0x30; res *= al; res += num; } res ^= 0x1234; */ //写出F(序列号)的逆算法 /* 一开始想的是进行异或操作后,逆向整个for循环,类似于从最后一个元素Serial[len]反推, res-=num;res/=al;这样,但发现要求的就是num的值,感觉该函数根本不可逆。 想来想去只好翻书看看有没有求解方法,发现程序与《解密与解密》P107处的Serial程序算法居然一样。 原来F(序列号)中的for循环只是将字串转化为了它对应的十进制数!最后做了一个异或而已。 也就是说,将F(用户名)的结果与0x1234进行异或,得出的就是本来的序列号! 通过这次破解,我知道了逆向不一定要微观上一步步反推,还可以从宏观上判断函数到底做了什么。 */ str.Format( "%d",( sum ^ 0x1234 ) ); SetDlgItemText( IDC_EDIT_PASSWORD,str ); } else MessageBox( "用户名格式错误!" ); }
再在OnInitDialog中添加此代码修改标题:SetWindowText(_T("CRACKME_Keygen"));
运行效果:
时间: 2024-10-06 09:35:11