系统 : Windows xp
程序 : k4n2
程序下载地址 :http://pan.baidu.com/s/1sklslvJ
要求 : 注册机编写
使用工具 : IDA Pro & OD
“PEDIY CrackMe 2007”中关于此程序的破文标题为“Borland C++写的Crackme的算法分析(适合新手)”,可在“搜索”标签下查找。
使用IDA载入程序,根据成功/失败的字符串提示找到关键代码,本程序中关键代码在401052处:
00401052 |. B9 19000000 mov ecx, 19 00401057 |. F3:A5 rep movs dword ptr es:[edi], dword ptr [esi] ; edi的值在这里确定下来,0012F538 00401059 |. 33C0 xor eax, eax 0040105B |. 8945 C4 mov dword ptr [ebp-3C], eax 0040105E |. 33D2 xor edx, edx 00401060 |. 8955 C0 mov dword ptr [ebp-40], edx 00401063 |. 33C9 xor ecx, ecx 00401065 |. 894D BC mov dword ptr [ebp-44], ecx 00401068 |. 33C0 xor eax, eax 0040106A |. 8945 B8 mov dword ptr [ebp-48], eax 0040106D |. 33D2 xor edx, edx 0040106F |. 8955 B4 mov dword ptr [ebp-4C], edx 00401072 |. 33C9 xor ecx, ecx 00401074 |. 894D B0 mov dword ptr [ebp-50], ecx 00401077 |. 33C0 xor eax, eax 00401079 |. 8945 AC mov dword ptr [ebp-54], eax 0040107C |. 33D2 xor edx, edx 0040107E |. 8955 A8 mov dword ptr [ebp-58], edx 00401081 |. 6A 66 push 66 ; /ControlID = 66 (102.) 00401083 |. 53 push ebx ; |hWnd 00401084 |. E8 D99C0000 call <jmp.&USER32.GetDlgItem> ; GetDlgItem 00401089 |. 6A 64 push 64 ; /Count = 64 (100.) 0040108B |. 8D8D 44FFFFFF lea ecx, dword ptr [ebp-BC] ; | 00401091 |. 51 push ecx ; |Buffer 00401092 |. 50 push eax ; |hWnd 00401093 |. E8 D69C0000 call <jmp.&USER32.GetWindowTextA> ; GetWindowTextA 00401098 |. 6A 68 push 68 ; /ControlID = 68 (104.) 0040109A |. 53 push ebx ; |hWnd 0040109B |. E8 C29C0000 call <jmp.&USER32.GetDlgItem> ; GetDlgItem 004010A0 |. 6A 64 push 64 ; /Count = 64 (100.) 004010A2 |. 8D95 E0FEFFFF lea edx, dword ptr [ebp-120] ; | 004010A8 |. 52 push edx ; |Buffer 004010A9 |. 50 push eax ; |hWnd 004010AA |. E8 BF9C0000 call <jmp.&USER32.GetWindowTextA> ; GetWindowTextA 004010AF |. 6A 67 push 67 ; /ControlID = 67 (103.) 004010B1 |. 53 push ebx ; |hWnd 004010B2 |. E8 AB9C0000 call <jmp.&USER32.GetDlgItem> ; GetDlgItem 004010B7 |. 8945 FC mov dword ptr [ebp-4], eax 004010BA |. 8D85 44FFFFFF lea eax, dword ptr [ebp-BC] 004010C0 |. 50 push eax 004010C1 |. E8 2A060000 call 004016F0 004010C6 |. 59 pop ecx 004010C7 |. 8945 D4 mov dword ptr [ebp-2C], eax 004010CA |. 8D8D E0FEFFFF lea ecx, dword ptr [ebp-120] 004010D0 |. 51 push ecx 004010D1 |. E8 1A060000 call 004016F0 004010D6 |. 59 pop ecx 004010D7 |. 68 EAB04000 push 0040B0EA 004010DC |. E8 0F060000 call 004016F0 004010E1 |. 59 pop ecx 004010E2 |. 68 0EB14000 push 0040B10E 004010E7 |. E8 04060000 call 004016F0 004010EC |. 59 pop ecx 004010ED |. 837D D4 03 cmp dword ptr [ebp-2C], 3 ; 用户名字串长度<=3 ,则跳转 004010F1 |. 0F8E 38010000 jle 0040122F ; 是则跳转到出错函数 004010F7 |. 33D2 xor edx, edx 004010F9 |. 33DB xor ebx, ebx 004010FB |. 8B55 D4 mov edx, dword ptr [ebp-2C] ; edx = 长度 004010FE |. 0155 C4 add dword ptr [ebp-3C], edx ; 长度赋给变量3C(假设有这么个变量) 00401101 |. 0155 C4 add dword ptr [ebp-3C], edx ; 变量 = 长度 * 2 00401104 |. 8BC2 mov eax, edx 00401106 |. 83C0 05 add eax, 5 ; eax = 长度+5 00401109 |. 8945 B8 mov dword ptr [ebp-48], eax ; 结果保存至变量48(假设) 0040110C |. 33C0 xor eax, eax 0040110E |. 8BCF mov ecx, edi 00401110 |. 83C1 04 add ecx, 4 00401113 |. 894D B4 mov dword ptr [ebp-4C], ecx ; 变量4C(假设)的值=0012F538+4 00401116 |. 33C9 xor ecx, ecx 00401118 |. 0155 BC add dword ptr [ebp-44], edx 0040111B |. 017D BC add dword ptr [ebp-44], edi ; 变量44(假设)=用户名字串长度+edi(0012F538) 0040111E |. 6BFF 03 imul edi, edi, 3 00401121 |. 897D C0 mov dword ptr [ebp-40], edi ; 变量40(假设)=0012F538*3 00401124 |. 33FF xor edi, edi 00401126 |. 0FBE8C05 44FF>movsx ecx, byte ptr [ebp+eax-BC] ; 取字符串首个字符 0040112E |. 83F9 61 cmp ecx, 61 ; 如果该字符不是小写字母或是‘{}|~’ 00401131 |. 7C 07 jl short 0040113A ; 则跳转进入子程序进行一些操作 00401133 |. 90 nop 00401134 |. 90 nop 00401135 |. 90 nop 00401136 |. 90 nop 00401137 |. 83E9 20 sub ecx, 20 ; 是小写字母则转换为大写 0040113A |> 8BF1 mov esi, ecx 0040113C |. 03DE add ebx, esi 0040113E |. 0FAFD9 imul ebx, ecx 00401141 |. 4A dec edx 00401142 |> 0FBE8C2F 44FF>/movsx ecx, byte ptr [edi+ebp-BC] ; 遍历用户名字符串,该指令取前一个字符 0040114A |. 0FBEB42F 45FF>|movsx esi, byte ptr [edi+ebp-BB] ; 该指令取出后一个字符 00401152 |. 83F9 61 |cmp ecx, 61 ; 如果前一个字符是小写字母,则跳转 00401155 |. 7D 12 |jge short 00401169 ; 将字符转换为大写 00401157 |. 90 |nop 00401158 |. 90 |nop 00401159 |. 90 |nop 0040115A |. 90 |nop 0040115B |> 83FE 61 |cmp esi, 61 ; 如果后一个字符是小写字母,则跳转 0040115E |. 7D 0E |jge short 0040116E 00401160 |. 90 |nop 00401161 |. 90 |nop 00401162 |. 90 |nop 00401163 |. 90 |nop 00401164 |. EB 0B |jmp short 00401171 00401166 | 90 |nop 00401167 | 90 |nop 00401168 | 90 |nop 00401169 |> 83E9 20 |sub ecx, 20 0040116C |.^ EB ED |jmp short 0040115B 0040116E |> 83EE 20 |sub esi, 20 ; 将字符转换为大写 00401171 |> 47 |inc edi ; 索引变量自增 00401172 |. 03DE |add ebx, esi ; 对ebx进行一些操作。。 00401174 |. 0FAFD9 |imul ebx, ecx 00401177 |. 4A |dec edx ; 循环变量自减 00401178 |.^ 75 C8 jnz short 00401142 0040117A |. 895D C8 mov dword ptr [ebp-38], ebx ; 循环的目的看似是转换大小写,其实是对ebx的值做调整,最后保存至变量38(假设) 0040117D |. 33C9 xor ecx, ecx 0040117F |. 33D2 xor edx, edx 00401181 |. 33DB xor ebx, ebx 00401183 |. 33C0 xor eax, eax 00401185 |. 837D D4 32 cmp dword ptr [ebp-2C], 32 ; 如果用户名字串长度大于等于32h,则跳转 00401189 |. 0F8D A0000000 jge 0040122F ; 是则跳转到出错函数 0040118F |> 0FBE840D 44FF>/movsx eax, byte ptr [ebp+ecx-BC] ; 遍历用户名字符串 00401197 |. 03C1 |add eax, ecx ; 加上循环变量 00401199 |. 03D8 |add ebx, eax ; 累加的值存入ebx 0040119B |. 41 |inc ecx 0040119C |. 3B4D D4 |cmp ecx, dword ptr [ebp-2C] 0040119F |.^ 75 EE jnz short 0040118F 004011A1 |. D1C0 rol eax, 1 ; 把eax循环左移1次,每次从最高位(最左)移出的数据位都补充到最低位 004011A3 |. 35 40E20100 xor eax, 1E240 ; 将eax与1E240h异或 004011A8 |. 8945 B0 mov dword ptr [ebp-50], eax ; 保存至变量50(假设) 004011AB |. 33C9 xor ecx, ecx 004011AD |. 33D2 xor edx, edx 004011AF |. 33DB xor ebx, ebx 004011B1 |. 33C0 xor eax, eax 004011B3 |> 0FBE840D 44FF>/movsx eax, byte ptr [ebp+ecx-BC] ; 遍历用户名字符串 004011BB |. 6BD0 06 |imul edx, eax, 6 ; edx = eax * 6 004011BE |. 33C2 |xor eax, edx 004011C0 |. 03D8 |add ebx, eax 004011C2 |. 41 |inc ecx 004011C3 |. 3B4D D4 |cmp ecx, dword ptr [ebp-2C] 004011C6 |.^ 75 EB jnz short 004011B3 004011C8 |. 035D B0 add ebx, dword ptr [ebp-50] ; 结果加上变量50(假设) 004011CB |. 895D AC mov dword ptr [ebp-54], ebx ; 保存至变量54(假设) 004011CE |. FF75 C0 push dword ptr [ebp-40] 004011D1 |. FF75 C4 push dword ptr [ebp-3C] 004011D4 |. FF75 BC push dword ptr [ebp-44] 004011D7 |. FF75 C8 push dword ptr [ebp-38] 004011DA |. FF75 B4 push dword ptr [ebp-4C] 004011DD |. FF75 B8 push dword ptr [ebp-48] 004011E0 |. FF75 AC push dword ptr [ebp-54] 004011E3 |. FF75 B0 push dword ptr [ebp-50] 004011E6 |. 68 38B44000 push 0040B438 ; ASCII "%lX%lu-%lu%lX-%lu%lu-%lX%lX" 004011EB |. 8D85 7CFEFFFF lea eax, dword ptr [ebp-184] 004011F1 |. 50 push eax 004011F2 |. E8 8D3D0000 call 00404F84 ; 将数据按照格式保存至ebp-184 004011F7 |. 83C4 28 add esp, 28 ; 平衡堆栈 004011FA |. 8D95 7CFEFFFF lea edx, dword ptr [ebp-184] 00401200 |. 52 push edx ; /String2 00401201 |. 8D8D E0FEFFFF lea ecx, dword ptr [ebp-120] ; | 00401207 |. 51 push ecx ; |String1 00401208 |. E8 399C0000 call <jmp.&KERNEL32.lstrcmpA> ; lstrcmpA 0040120D |. 85C0 test eax, eax 0040120F |. 75 0F jnz short 00401220 00401211 |. 68 54B44000 push 0040B454 ; /Text = "Congratulations! IF this number comes *FROM YOUR* keygen, Write a tutorial dude ;)." 00401216 |. FF75 FC push dword ptr [ebp-4] ; |hWnd 00401219 |. E8 2C9B0000 call <jmp.&USER32.SetWindowTextA> ; SetWindowTextA 0040121E |. EB 1C jmp short 0040123C 00401220 |> 68 A8B44000 push 0040B4A8 ; /Text = "This serial is *NOT* Valid!! Try again... : UNREGISTERED" 00401225 |. FF75 FC push dword ptr [ebp-4] ; |hWnd 00401228 |. E8 1D9B0000 call <jmp.&USER32.SetWindowTextA> ; SetWindowTextA 0040122D |. EB 0D jmp short 0040123C 0040122F |> 68 E1B44000 push 0040B4E1 ; /Text = "Name must contain more than 3 chars!" 00401234 |. FF75 FC push dword ptr [ebp-4] ; |hWnd 00401237 |. E8 0E9B0000 call <jmp.&USER32.SetWindowTextA> ; SetWindowTextA
好了,算法到这里分析完毕,我们根据加密算法开始编写注册机吧!
复制一份http://www.cnblogs.com/ZRBYYXDM/p/5002789.html中搭建的MFC窗口程序,打开并修改OnOk函数如下:
void CSerialNumber_KeygenDlg::OnOK() { // TODO: Add extra validation here CString str; GetDlgItem( IDC_EDIT_NAME )->GetWindowText( str ); //获取用户名 int len = str.GetLength(); //获取长度 if ( len <= 3 || len >= 50 ) MessageBox( "用户名必须长度大于3、小于50!" ); else { unsigned int Serial[8] = { 0,0,0,0,0,0,0,0 }; //存放组成密钥的字串。 Serial[0] += (str[len - 1] + len - 1); //变量50 __asm { push eax mov eax,Serial[0] rol eax,1 mov Serial[0],eax pop eax } Serial[0] ^= 0x1E240; int i = 0; for ( i = 0 ; i != len ; i++ ) //变量54 Serial[1] += (str[i] * 6) ^ str[i]; Serial[1] += Serial[0]; Serial[2] = len + 5; //变量48 Serial[3] = 0x12F538+4; //变量4C //变量38 if ( str[0] >= 0x61 ) //如果第一个字符是小写字符 Serial[4] = str[0] - 0x20; else Serial[4] = str[0]; Serial[4] *= Serial[4]; str.MakeUpper(); //将小写转换为大写。 for ( i = 0 ; i != (len - 1) ; i++ ){ Serial[4] += str[i + 1]; Serial[4] *= str[i]; } Serial[5] = len + 0x12F538; //变量44 Serial[6] = len * 2; //变量3C Serial[7] = 0x12F538 * 3; //变量40 CString res; res.Format( _T("%lX%lu-%lu%lX-%lu%lu-%lX%lX"),Serial[0],Serial[1], Serial[2],Serial[3],Serial[4],Serial[5],Serial[6],Serial[7] ); GetDlgItem( IDC_EDIT_Number )->SetWindowText( res ); } //CDialog::OnOK(); //屏蔽基类OnOk函数 }
再在OnInitDialog中添加此代码修改标题:SetWindowText(_T("k4n2_Keygen"));
运行程序,并将解密得到的序列号黏贴至k4n2程序中,单击check。。。
效果拔群: