系统 : Windows xp
程序 : Duelist's Crackme #4
程序下载地址 :http://pan.baidu.com/s/1qXv2RRu
要求 : 注册机编写
使用工具 : IDA Pro & OD
“PEDIY CrackMe 2007”中关于此程序的破文标题为“简单的CrackMe的算法分析”,可以在“搜索”标签下查找出原文。
使用IDA载入程序,根据成功/失败的字符串提示找到关键代码,本程序中关键代码在401127处:
00401127 > /6A 00 push 0 ; /lParam = 0 00401129 . |6A 00 push 0 ; |wParam = 0 0040112B . |6A 0E push 0E ; |Message = WM_GETTEXTLENGTH 0040112D . |6A 03 push 3 ; |ControlID = 3 0040112F . |FF75 08 push dword ptr [ebp+8] ; |hWnd 00401132 . |E8 41020000 call <jmp.&USER32.SendDlgItemMessageA>; \SendDlgItemMessageA 00401137 . |A3 AF214000 mov dword ptr [4021AF], eax 0040113C . |83F8 00 cmp eax, 0 ; 用户名长度不等于0 0040113F . |0F84 D5000000 je 0040121A 00401145 . |83F8 08 cmp eax, 8 ; 用户名长度 不大于8 00401148 . |0F8F CC000000 jg 0040121A 0040114E . |8BF0 mov esi, eax 00401150 . |6A 00 push 0 ; /lParam = 0 00401152 . |6A 00 push 0 ; |wParam = 0 00401154 . |6A 0E push 0E ; |Message = WM_GETTEXTLENGTH 00401156 . |6A 04 push 4 ; |ControlID = 4 00401158 . |FF75 08 push dword ptr [ebp+8] ; |hWnd 0040115B . |E8 18020000 call <jmp.&USER32.SendDlgItemMessageA>; \SendDlgItemMessageA 00401160 . |83F8 00 cmp eax, 0 ; 密码长度不等于0 00401163 . |0F84 B1000000 je 0040121A 00401169 . |3BF0 cmp esi, eax ; 用户名长度要和密码长度相同 0040116B . |0F85 A9000000 jnz 0040121A 00401171 . |68 60214000 push 00402160 ; /lParam = 402160 00401176 . |6A 08 push 8 ; |wParam = 8 00401178 . |6A 0D push 0D ; |Message = WM_GETTEXT 0040117A . |6A 03 push 3 ; |ControlID = 3 0040117C . |FF75 08 push dword ptr [ebp+8] ; |hWnd 0040117F . |E8 F4010000 call <jmp.&USER32.SendDlgItemMessageA>; \SendDlgItemMessageA 00401184 . |68 79214000 push 00402179 ; /lParam = 402179 00401189 . |6A 10 push 10 ; |wParam = 10 0040118B . |6A 0D push 0D ; |Message = WM_GETTEXT 0040118D . |6A 04 push 4 ; |ControlID = 4 0040118F . |FF75 08 push dword ptr [ebp+8] ; |hWnd 00401192 . |E8 E1010000 call <jmp.&USER32.SendDlgItemMessageA>; \SendDlgItemMessageA 00401197 . |B9 FFFFFFFF mov ecx, -1 0040119C > |41 inc ecx 0040119D . |0FBE81 602140>movsx eax, byte ptr [ecx+402160] ; 遍历用户名字串 004011A4 . |83F8 00 cmp eax, 0 ; Switch (cases 0..7A) 004011A7 . |74 32 je short 004011DB 004011A9 . |BE FFFFFFFF mov esi, -1 004011AE . |83F8 41 cmp eax, 41 ; 每个字符的ASCII值都应 不小于 41h 004011B1 . |7C 67 jl short 0040121A 004011B3 . |83F8 7A cmp eax, 7A ; 每个字符的ASCII值都应 不高于 7Ah 004011B6 . |77 62 ja short 0040121A 004011B8 . |83F8 5A cmp eax, 5A ; 当字符小于5Ah时则跳转(当字符是大写字符时) 004011BB . |7C 03 jl short 004011C0 004011BD . |83E8 20 sub eax, 20 ; 将小写字母转化为大写; Cases 5A ('Z'),5B ('['),5C ('\'),5D (']'),5E ('^'),5F ('_'),60 ('`'),61 ('a'),62 ('b'),63 ('c'),64 ('d'),65 ('e'),66 ('f'),67 ('g'),68 ('h'),69 ('i'),6A ('j'),6B ('k'),6C ('l'),6D ('m')... of switch 004011A4 004011C0 > |46 inc esi ; 密钥表循环变量自增; Cases 41 ('A'),42 ('B'),43 ('C'),44 ('D'),45 ('E'),46 ('F'),47 ('G'),48 ('H'),49 ('I'),4A ('J'),4B ('K'),4C ('L'),4D ('M'),4E ('N'),4F ('O'),50 ('P'),51 ('Q'),52 ('R'),53 ('S'),54 ('T')... of switch 004011A4 004011C1 . |0FBE96 172040>movsx edx, byte ptr [esi+402017] 004011C8 . |3BC2 cmp eax, edx ; 当密钥表中的这个元素不等于用户名字符时,取出下一个元素比对 004011CA .^|75 F4 jnz short 004011C0 004011CC . |0FBE86 3C2040>movsx eax, byte ptr [esi+40203C] ; 取出对照表中相同位置的元素替换掉字符 004011D3 . |8981 94214000 mov dword ptr [ecx+402194], eax ; 再将结果字符保存 004011D9 .^|EB C1 jmp short 0040119C 004011DB > |FF35 AF214000 push dword ptr [4021AF] ; 长度入栈; Case 0 of switch 004011A4 004011E1 . |68 94214000 push 00402194 ; 结果字符串入栈 004011E6 . |68 79214000 push 00402179 ; 输入的序列号入栈 004011EB . |E8 54000000 call 00401244 ; 比较字符串子程序 004011F0 . |83F8 01 cmp eax, 1 004011F3 .^|0F84 DEFEFFFF je 004010D7 004011F9 . |EB 1F jmp short 0040121A 004011FB > |837D 10 01 cmp dword ptr [ebp+10], 1 ; | 004011FF .^\0F84 22FFFFFF je 00401127 ; | 00401205 . 837D 10 02 cmp dword ptr [ebp+10], 2 ; | 00401209 . 75 2F jnz short 0040123A ; | 0040120B > E8 B4000000 call <jmp.&KERNEL32.ExitProcess> ; \ExitProcess 00401210 . B8 01000000 mov eax, 1 00401215 .^ E9 FFFEFFFF jmp 00401119 0040121A > 68 00200000 push 2000 ; /Style = MB_OK|MB_TASKMODAL; Default case of switch 004011A4 0040121F . 68 01204000 push 00402001 ; |Title = "Duelist's Crackme #4" 00401224 . 68 AE204000 push 004020AE ; |Text = "Your registration info is invalid... Note that most of the special chars may raise registration problems!" 00401229 . 6A 00 push 0 ; |hOwner = NULL 0040122B . E8 36010000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
我们可以看到这是一个通过一个替换表来对用户名进行变形实现加密的算法,通过获得替换表,我们可以实现解密。
在IDA的字符串表中可以找到替换表,发现它的定义如下:
DATA:00402017 byte_402017 db 41h ; DATA XREF: DialogFunc+F3r DATA:00402018 a1lsk2djf4hgp3q db '1LSK2DJF4HGP3QWO5EIR6UTYZ8MXN7CBV9',0 DATA:0040203B db 20h DATA:0040203C byte_40203C db 53h ; DATA XREF: DialogFunc+FEr DATA:0040203D aU7csjkf09ncsdo db 'U7CSJKF09NCSDO9SDF09SDRLVK7809S4NF',0
注意!这里402017处和40203C处的数据也是替换表的一部分!
算法分析完毕,加密用的替换表也发现了,那我们马上开始编写解密程序吧!
复制一份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 == 0 || len > 8 ) //当字符串长度等于0或者大于8时 MessageBox( "用户名必须长度不等于0且不大于8!" ); else { CString Temp = ""; BOOL isOK = true; for ( int i = 0 ; i < len ; i++ ){ char ch = str[i]; if ( ch < 0x41 || ch > 0x7A ){ MessageBox( "输入用户名有误!" ); isOK = false; break; } if ( ch > 0x5A ) //转换大小写 ch -= 0x20; CString SerialTable = "A1LSK2DJF4HGP3QWO5EIR6UTYZ8MXN7CBV9"; //密钥表 CString SubTable = "SU7CSJKF09NCSDO9SDF09SDRLVK7809S4NF"; //替换表 for ( int j = 0 ; j < SerialTable.GetLength() ; j++ ){ if ( ch == SerialTable[j] ){ Temp += SubTable[j]; break; } } } if ( isOK ) { GetDlgItem( IDC_EDIT_Number )->SetWindowText( Temp ); } } //CDialog::OnOK(); //屏蔽基类OnOk函数 }
再在OnInitDialog中添加此代码修改标题:SetWindowText(_T("due-cm4_Keygen"));
运行程序,并将解密得到的序列号黏贴至due-cm4程序中,单击check。。。
效果拔群:
我们一路奋战,不是为了改变世界,而是不让世界改变我们
——《熔炉》