系统 : Windows xp
程序 : Chafe.1
程序下载地址 :http://pan.baidu.com/s/1dDJzdoH
要求 : 注册机编写
使用工具 : OD & IDA
这个CRACKME最初是在http://www.cnblogs.com/bbdxf/p/3817880.html这篇博文里发现的,跟你看到的一样,原文只分析了算法,并没有给出注册机的写法。
原博主笨笨D幸福是一个逆向前辈,曾在一个多月里破解了37个CrackMe,是我这种破解新手非常崇拜的大牛。然而,2014年7月18日之后他的博客就再也没有更新过,最后的动态也只是说:“最近一直出差,这两个月都没时间了。现在还在外地呢”。
我不知道他因为什么原因突然停更,但我觉得有必要走上他曾经探索过的道路。时间不够也罢,心情不好也罢,断在中途的这条道路就由我替你延续,这个CRACKME就由我来破解!
还是老规矩,IDA载入程序,查看字串表得到关键字串“YES! You found your serial!!”。定位字串调用位置并向上翻找到关键算法:
0040128D |> 3D 13010000 cmp eax, 113
00401292 |. 75 50 jnz short 004012E4
00401294 |. E8 BA010000 call 00401453 ; Case 113 (WM_TIMER) of switch 0040123F
00401299 |. 0FBE05 663140>movsx eax, byte ptr [403166] ; eax值取自403166
004012A0 |. 3A05 67314000 cmp al, byte ptr [403167]
004012A6 |. 75 06 jnz short 004012AE
004012A8 |. 33C0 xor eax, eax
004012AA |. C9 leave
004012AB |. C2 1000 retn 10
004012AE |> A2 67314000 mov byte ptr [403167], al
004012B3 |. 83F8 10 cmp eax, 10 ; 如果eax的值为10,则成功
004012B6 |. 74 16 je short 004012CE
004012B8 |. 68 65304000 push 00403065 ; /Text = "Your serial is not valid."
004012BD |. FF35 7C314000 push dword ptr [40317C] ; |hWnd = 0019090C ('Your serial is not valid.',class='Edit',parent=0022092A)
004012C3 |. E8 66020000 call <jmp.&USER32.SetWindowTextA> ; SetWindowTextA
004012C8 |. 33C0 xor eax, eax
004012CA |. C9 leave
004012CB |. C2 1000 retn 10
004012CE |> 68 7F304000 push 0040307F ; /Text = "YES! You found your serial!!"
004012D3 |. FF35 7C314000 push dword ptr [40317C] ; |hWnd = 0019090C ('Your serial is not valid.',class='Edit',parent=0022092A)
004012D9 |. E8 50020000 call <jmp.&USER32.SetWindowTextA> ; SetWindowTextA
004012DE |. 33C0 xor eax, eax
004012E0 |. C9 leave
对403166内存下写入断点:运行程序找出4处对内存的写入操作:
0040146F . 8B25 A0314000 mov esp, dword ptr [4031A0] ; ↓获取int值成功加4(1)
00401475 . 6A 00 push 0 ; /IsSigned = FALSE
00401477 . 8D45 FC lea eax, dword ptr [ebp-4] ; |
0040147A . 50 push eax ; |pSuccess
0040147B . 6A 64 push 64 ; |ControlID = 64 (100.)
0040147D . FF35 70314000 push dword ptr [403170] ; |hWnd = 0022092A ('TEXme v1.0',class='TEXcls')
00401483 . E8 64000000 call <jmp.&USER32.GetDlgItemInt> ; GetDlgItemInt
00401488 . A3 88314000 mov dword ptr [403188], eax ; 保存序列号
0040148D . 837D FC 00 cmp dword ptr [ebp-4], 0 ; 获取失败?
00401491 . 74 07 je short 0040149A
00401493 . 8005 66314000>add byte ptr [403166], 4 ; 成功数值加4,403166 = 4
0040149A > C9 leave
0040149B . C3 retn
还有:
00401069 . 6A 14 push 14 ; /Count = 14 (20.)
0040106B . 68 8C314000 push 0040318C ; |Buffer = Chafe_1.0040318C
00401070 . FF35 74314000 push dword ptr [403174] ; |hWnd = 003D0922 (class='Edit',parent=0022092A)
00401076 . E8 7D040000 call <jmp.&USER32.GetWindowTextA> ; GetWindowTextA
0040107B . B9 14000000 mov ecx, 14 ; ↑返回用户名长度
00401080 . 2BC8 sub ecx, eax
00401082 . 8DB8 8C314000 lea edi, dword ptr [eax+40318C]
00401088 > C607 00 mov byte ptr [edi], 0
0040108B . 47 inc edi
0040108C . 49 dec ecx
0040108D .^ 75 F9 jnz short 00401088
0040108F . 85C0 test eax, eax ; 用户名是0?
00401091 . 74 10 je short 004010A3
00401093 . 8005 66314000>add byte ptr [403166], 4 ; 用户名不为空则加4(2)
0040109A . C605 68314000>mov byte ptr [403168], 0
004010A1 . EB 06 jmp short 004010A9
004010A3 > 8825 66314000 mov byte ptr [403166], ah
004010A9 > C9 leave
004010AA . C3 retn
以及很关键的第三处:
00401361 . 8D3D 8C314000 lea edi, dword ptr [40318C] ; 取用户名(一直调用这个子程序)
00401367 . 0FBE05 683140>movsx eax, byte ptr [403168] ; 取内存地址
0040136E . 03F8 add edi, eax
00401370 . FE05 68314000 inc byte ptr [403168] ; 自增
00401376 . A1 88314000 mov eax, dword ptr [403188]
0040137B . 8B25 A0314000 mov esp, dword ptr [4031A0]
00401381 . 40 inc eax
00401382 . FF05 88314000 inc dword ptr [403188]
00401388 . 3307 xor eax, dword ptr [edi]
0040138A . A3 88314000 mov dword ptr [403188], eax
0040138F . 803D 68314000>cmp byte ptr [403168], 10 ; 循环到第16次,403168为10
00401396 . 75 07 jnz short 0040139F
00401398 . 8005 66314000>add byte ptr [403166], 4 ; 调用第16次时+4(3)
0040139F > C9 leave
004013A0 . C3 retn
最后一处:
0040149C . A1 88314000 mov eax, dword ptr [403188] ; 当此内存的值为F6EEDB88时,
004014A1 . 05 78241109 add eax, 9112478
004014A6 . 85C0 test eax, eax ; eax为0
004014A8 . 75 09 jnz short 004014B3 ; 跳转+4,否则清空403166
004014AA . 8005 66314000>add byte ptr [403166], 4 ; 403166 = 0x10
004014B1 . EB 07 jmp short 004014BA
004014B3 > C605 66314000>mov byte ptr [403166], 0
004014BA > 8B25 A0314000 mov esp, dword ptr [4031A0]
004014C0 . C9 leave
004014C1 . C3 retn
结合第三、第四个汇编代码段可知程序将用户名和序列号进行了一些操作,并判断最后得出的操作数确定是否注册成功。这是典型的采用二元函数进行加密的方式:F(用户名,序列号)= 特定值。
这个算法比较简单,我们直接打开http://www.cnblogs.com/ZRBYYXDM/p/5115596.html中搭建的框架,并修改OnBtnDecrypt函数如下:
void CKengen_TemplateDlg::OnBtnDecrypt()
{
// TODO: Add your control notification handler code here
CString str;
GetDlgItemText( IDC_EDIT_NAME,str ); //获取用户名字串基本信息。
int len = str.GetLength();
if ( len <= 19 ){ //格式控制。
//模拟F(用户名,序列号)= 特定值
/*
char Name[20] = "";
unsigned int Serial = 1; //序列号123
strcpy( Name,str ); //复制用户名字串
//
for ( int i = 0 ; i != 16 ; i++ ){
Serial++;
Serial ^= ( Name[i] + Name[i+1] * 0x100 + Name[i+2] * 0x10000 + Name[i+3] * 0x1000000 );
}
if ( Serial == 0xF6EEDB88 )
MessageBox( "Ok" );
else
MessageBox( "Error" );
*/
//写出F(用户名,序列号)= 特定值 的逆算法
char Name[20] = "";
unsigned int value = 0xF6EEDB88;
strcpy( Name,str ); //复制用户名字串
for ( int i = 15 ; i != -1 ; i-- ){
value ^= ( Name[i] + Name[i+1] * 0x100 + Name[i+2] * 0x10000 + Name[i+3] * 0x1000000 );
value--;
}
CString PassWord;
PassWord.Format( "%u",value );
SetDlgItemText( IDC_EDIT_PASSWORD,PassWord );
}
else
MessageBox( "用户名格式错误!" );
}
再在OnInitDialog中添加此代码修改标题:SetWindowText(_T("Chafe.1_Keygen"));
运行效果: