这里练习用到的CrackmMe都来自<<加密与解密>>第3版第5章习题的"PEDIY CrackMe 2007.chm"集合.
先将这个CrackMe用PEID侦测下,发现是加了壳的,鉴于还未学到壳的相关知识,还好这里附带提供了解壳后的文件.OD跟踪,Ctrl+N看到熟悉的GetDlgItemTextA,直接对其下断,然后F9运行随意输入用户名和密码后,点击Check,断在下面:
00401528 |. 68 00010000 push 100 ; /Count = 100 (256.) 0040152D |. 8D85 00FFFFFF lea eax, [local.64] ; | 00401533 |. 50 push eax ; |Buffer 00401534 |. 6A 65 push 65 ; |ControlID = 65 (101.) 00401536 |. FF75 08 push [arg.1] ; |hWnd 00401539 |. E8 FA010000 call <jmp.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA 0040153E |. 89C3 mov ebx, eax 00401540 |. 09DB or ebx, ebx 00401542 |. 75 04 jnz short 00401548 00401544 |. 31C0 xor eax, eax 00401546 |. EB 50 jmp short 00401598 00401548 |> BF BC020000 mov edi, 2BC 0040154D |. BE 30000000 mov esi, 30 00401552 |. B8 48000000 mov eax, 48 00401557 |. 99 cdq 00401558 |. F7FB idiv ebx 0040155A |. 29C6 sub esi, eax 0040155C |. 8D34B6 lea esi, dword ptr [esi+esi*4] ; (initial cpu selection) 0040155F |. 29F7 sub edi, esi 00401561 |. 6BFF 6B imul edi, edi, 6B 00401564 |. 81EF 6CCF0000 sub edi, 0CF6C 0040156A |. 81FF 00230000 cmp edi, 2300 00401570 |. 7F 08 jg short 0040157A 00401572 |. 81FF 90010000 cmp edi, 190 00401578 |. 7D 04 jge short 0040157E 0040157A |> 31C0 xor eax, eax 0040157C |. EB 1A jmp short 00401598 ; 通过计算,用户名长度必须在[3,9] 0040157E |> 8D85 00FFFFFF lea eax, [local.64] 00401584 |. 50 push eax 00401585 |. 53 push ebx 00401586 |. FF75 08 push [arg.1] 00401589 |. E8 77FDFFFF call 00401305 ; 关键,校验过程 0040158E |. 83C4 0C add esp, 0C 00401591 |. 09C0 or eax, eax 00401593 |. 74 03 je short 00401598 ; 返回0 00401595 |. 31C0 xor eax, eax 00401597 |. 40 inc eax ; 返回1 00401598 |> 5F pop edi 00401599 |. 5E pop esi 0040159A |. 5B pop ebx 0040159B |. C9 leave 0040159C \. C3 retn
可以看到00401305是校验过程,会返回0或1.先不管,先找到调用此处的地址00401625,如下:
1 00401625 |. E8 DFFEFFFF call 00401509 ; WM_COMMAND控件响应 2 0040162A |. 59 pop ecx 3 0040162B |. 09C0 or eax, eax 4 0040162D |. 74 53 je short 00401682 ; 处理结果:0完蛋,1成功 5 0040162F |. 6A 40 push 40 ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL 6 00401631 |. 8D45 C7 lea eax, dword ptr [ebp-39] ; | 7 00401634 |. 50 push eax ; |Title 8 00401635 |. 8D45 D3 lea eax, dword ptr [ebp-2D] ; | 9 00401638 |. 50 push eax ; |Text 10 00401639 |. FF75 08 push [arg.1] ; |hOwner 11 0040163C |. E8 1B010000 call <jmp.&USER32.MessageBoxA> ; \成功
可以看到返回0表示失败,1是成功,接下来安心分析校验过程了:
1 0040139E |. 68 00010000 push 100 ; /Count = 100 (256.) 2 004013A3 |. 8D85 E1FCFFFF lea eax, dword ptr [ebp-31F] ; |取得输入密码放入缓冲区 3 004013A9 |. 50 push eax ; |Buffer 4 004013AA |. 6A 66 push 66 ; |ControlID = 66 (102.) 5 004013AC |. FF75 08 push [arg.1] ; |hWnd 6 004013AF |. E8 84030000 call <jmp.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA 7 004013B4 |. 09C0 or eax, eax 8 004013B6 |. 0F84 48010000 je 00401504 9 004013BC |. B8 CF110000 mov eax, 11CF 10 004013C1 |. 0FB68D E1FCFF>movzx ecx, byte ptr [ebp-31F] ; 密码第1位放入ECX 11 004013C8 |. 99 cdq 12 004013C9 |. F7F9 idiv ecx 13 004013CB |. 83FA 17 cmp edx, 17 14 004013CE |. 74 07 je short 004013D7 ; 前面的除法求得密码第一位是6 15 004013D0 |. 31C0 xor eax, eax 16 004013D2 |. E9 2D010000 jmp 00401504 ; 完蛋,返回0 17 004013D7 |> 31DB xor ebx, ebx 18 004013D9 |. EB 0B jmp short 004013E6 19 004013DB |> 8B45 10 /mov eax, [arg.3] 20 004013DE |. 0FBE0418 |movsx eax, byte ptr [eax+ebx] 21 004013E2 |. 0145 FC |add [local.1], eax 22 004013E5 |. 43 |inc ebx 23 004013E6 |> 3B5D 0C cmp ebx, [arg.2] 24 004013E9 |.^ 7C F0 \jl short 004013DB ; 用户名各字符累加 25 004013EB |. 31DB xor ebx, ebx 26 004013ED |. E9 83000000 jmp 00401475 27 004013F2 |> 8B55 10 /mov edx, [arg.3] 28 004013F5 |. 0FBE3C1A |movsx edi, byte ptr [edx+ebx] ; EDI为用户名当前字符 29 004013F9 |. 8B75 FC |mov esi, [local.1] 30 004013FC |. 89D9 |mov ecx, ebx 31 004013FE |. C1E1 02 |shl ecx, 2 32 00401401 |. 89DA |mov edx, ebx 33 00401403 |. 42 |inc edx 34 00401404 |. 29D1 |sub ecx, edx ; ECX=循环变量i*4-(i+1) 35 00401406 |. 0FB68C0D E1FE>|movzx ecx, byte ptr [ebp+ecx-11F] ; ebp-11F是字母表的开始,ECX查表,第一次必为0 36 0040140E |. 89FA |mov edx, edi 37 00401410 |. 31CA |xor edx, ecx ; EDX=用户名当前字符与ECX异或 38 00401412 |. 89F1 |mov ecx, esi ; ESI是用户名字符累加 39 00401414 |. 0FAFCB |imul ecx, ebx 40 00401417 |. 29F1 |sub ecx, esi 41 00401419 |. 89CE |mov esi, ecx ; ESI=ESI*i-ESI 42 0040141B |. 83F6 FF |xor esi, FFFFFFFF ; ESI再与-1异或 43 0040141E |. 8DB432 4D0100>|lea esi, dword ptr [edx+esi+14D] ; ESI=EDX+ESI+14D 44 00401425 |. 8B4D 0C |mov ecx, [arg.2] ; ECX=用户名长度 45 00401428 |. 89DA |mov edx, ebx 46 0040142A |. 83C2 03 |add edx, 3 47 0040142D |. 0FAFCA |imul ecx, edx 48 00401430 |. 0FAFCF |imul ecx, edi ; ECX=ECX*(i+3)*EDI 49 00401433 |. 89F0 |mov eax, esi 50 00401435 |. 01C8 |add eax, ecx ; EAX=ESI+ECX 51 00401437 |. B9 0A000000 |mov ecx, 0A 52 0040143C |. 31D2 |xor edx, edx 53 0040143E |. F7F1 |div ecx ; EAX=EAX/A 54 00401440 |. 83C2 30 |add edx, 30 ; EDX=EAX%A+30 55 00401443 |. 88941D FCFEFF>|mov byte ptr [ebp+ebx-104], dl 56 0040144A |. 0FB6BC1D FCFE>|movzx edi, byte ptr [ebp+ebx-104] 57 00401452 |. 81F7 ACAD0000 |xor edi, 0ADAC ; EDI=DL^0ADAC 58 00401458 |. 89DE |mov esi, ebx 59 0040145A |. 83C6 02 |add esi, 2 60 0040145D |. 89F8 |mov eax, edi 61 0040145F |. 0FAFC6 |imul eax, esi ; EAX=EDI*(i+2) 62 00401462 |. B9 0A000000 |mov ecx, 0A 63 00401467 |. 99 |cdq 64 00401468 |. F7F9 |idiv ecx ; EAX=EAX/A 65 0040146A |. 83C2 30 |add edx, 30 ; EDX=EAX%A+30 66 0040146D |. 88941D FCFEFF>|mov byte ptr [ebp+ebx-104], dl 67 00401474 |. 43 |inc ebx 68 00401475 |> 3B5D 0C cmp ebx, [arg.2] ; 循环次数为用户名长度 69 00401478 ^ 0F8C 74FFFFFF jl 004013F2 70 0040147E |. 8D85 FCFEFFFF lea eax, [local.65] ; 上面的计算将用户名转化为一个字符串 71 00401484 |. 50 push eax 72 00401485 |. 6A 54 push 54 ; 'T' 73 00401487 |. 8D85 DCFBFFFF lea eax, [local.265] 74 0040148D |. 50 push eax ; |Format 75 0040148E |. 8D85 E1FBFFFF lea eax, dword ptr [ebp-41F] ; | 76 00401494 |. 50 push eax ; |s 77 00401495 |. E8 CE020000 call <jmp.&USER32.wsprintfA> ; \wsprintfA 78 0040149A |. 8B7D 0C mov edi, [arg.2] 79 0040149D |. 89F8 mov eax, edi 80 0040149F |. 0FAF45 FC imul eax, [local.1] ; EAX=用户名长度*用户名累加 81 004014A3 |. B9 64000000 mov ecx, 64 82 004014A8 |. 99 cdq 83 004014A9 |. F7F9 idiv ecx 84 004014AB |. 89D7 mov edi, edx 85 004014AD |. 83C7 30 add edi, 30 ; EDI=EAX%64+30 86 004014B0 |. 57 push edi 87 004014B1 |. 8DBD E1FBFFFF lea edi, dword ptr [ebp-41F] 88 004014B7 |. 57 push edi 89 004014B8 |. 8DBD D6FBFFFF lea edi, dword ptr [ebp-42A] 90 004014BE |. 57 push edi ; |Format 91 004014BF |. 8DBD E1FDFFFF lea edi, dword ptr [ebp-21F] ; | 92 004014C5 |. 57 push edi ; |s 93 004014C6 |. E8 9D020000 call <jmp.&USER32.wsprintfA> ; \wsprintfA 94 004014CB |. 83C4 20 add esp, 20 95 004014CE |. 8D8D E1FDFFFF lea ecx, dword ptr [ebp-21F] 96 004014D4 |. 83C8 FF or eax, FFFFFFFF 97 004014D7 |> 40 /inc eax 98 004014D8 |. 803C01 00 |cmp byte ptr [ecx+eax], 0 99 004014DC |.^ 75 F9 \jnz short 004014D7 100 004014DE |. 50 push eax 101 004014DF |. 8D85 E1FCFFFF lea eax, dword ptr [ebp-31F] 102 004014E5 |. 50 push eax 103 004014E6 |. 8D85 E1FDFFFF lea eax, dword ptr [ebp-21F] 104 004014EC |. 50 push eax 105 004014ED |. E8 D0FDFFFF call 004012C2 ; 最终校验函数
1 004012C2 /$ 55 push ebp 2 004012C3 |. 89E5 mov ebp, esp 3 004012C5 |. 53 push ebx 4 004012C6 |. 56 push esi 5 004012C7 |. 57 push edi 6 004012C8 |. 8B5D 10 mov ebx, [arg.3] ; EBX=格式化字串长度 7 004012CB |. 31F6 xor esi, esi 8 004012CD |. 46 inc esi ; ESI初始化为1 9 004012CE |. EB 29 jmp short 004012F9 10 004012D0 |> 8B55 08 /mov edx, [arg.1] 11 004012D3 |. 0FBE3C32 |movsx edi, byte ptr [edx+esi] 12 004012D7 |. 89F8 |mov eax, edi 13 004012D9 |. 83F0 20 |xor eax, 20 14 004012DC |. B9 0A000000 |mov ecx, 0A 15 004012E1 |. 99 |cdq 16 004012E2 |. F7F9 |idiv ecx 17 004012E4 |. 89D7 |mov edi, edx 18 004012E6 |. 83C7 30 |add edi, 30 ; EDI=(字串当前数字^20)%A+30 19 004012E9 |. 8B55 0C |mov edx, [arg.2] 20 004012EC |. 0FBE1432 |movsx edx, byte ptr [edx+esi] ; EDX=密码当前数字(第2位开始,第1位恒为6) 21 004012F0 |. 39D7 |cmp edi, edx 22 004012F2 |. 74 04 |je short 004012F8 ; EDI,EDX,不等则完蛋 23 004012F4 |. 31C0 |xor eax, eax 24 004012F6 |. EB 08 |jmp short 00401300 25 004012F8 |> 46 |inc esi 26 004012F9 |> 39DE cmp esi, ebx 27 004012FB |.^ 7C D3 \jl short 004012D0 ; 下一次比较 28 004012FD |. 31C0 xor eax, eax 29 004012FF |. 40 inc eax 30 00401300 |> 5F pop edi 31 00401301 |. 5E pop esi 32 00401302 |. 5B pop ebx 33 00401303 |. 5D pop ebp 34 00401304 \. C3 retn
主要就是边调试边翻译出加密算法,记录好,这个CrackMe没什么技术难题可言.对于我这样的新手来说,其实这种新手题练的就是耐心和细心,流程跟完了,记录记完了,大致总结一下它的加密方法,然后就可以把笔记汇总写出KeyGen了,如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 const char g_Table[27] = {0,'A','B','C','D','E','F','G','H','I','J', 6 'K','L','M','N','O','P','Q','R','S','T','U', 7 'V','W','X','Y','Z'}; 8 const char* g_letterTable = g_Table + 1; 9 10 void Keygen(const char* user, char* outCode) 11 { 12 const size_t len = strlen(user); 13 int sum = 0; 14 char transform1[10] = {0}; 15 //用户名字符累加 16 for(size_t i=0; i<len; ++i) 17 sum += user[i]; 18 19 for (size_t i=0; i<len; ++i) 20 { 21 int curUserChar = user[i]; 22 //查字母表 23 int ch = *(g_letterTable + i * 4 - (i + 1)); 24 25 int sum1 = sum * i - sum; 26 sum1 = sum1 ^ 0xffffffff; 27 sum1 = (curUserChar ^ ch) + sum1 + 0x14D; 28 29 int a = sum1 + len * (i + 3) * curUserChar; 30 int b = (a % 0xA) + 0x30; 31 32 int c = (b ^ 0xADAC) * (i + 2); 33 int d = (c % 0xA) + 0x30; 34 35 transform1[i] = d; 36 } 37 38 int e = (len * sum) % 0x64 + 0x30; 39 char transform2[20] = {0}; 40 sprintf(transform2, "T%s-%d", transform1, e); 41 42 //根据用户名的变换字符串计算序列号 43 size_t len1 = strlen(transform2); 44 45 //序列号第1位恒为6 46 outCode[0] = '6'; 47 for (int i=1; i<len1; ++i) 48 { 49 outCode[i] = (transform2[i] ^ 0x20) % 0xA + 0x30; 50 } 51 outCode[len1] = 0; 52 } 53 54 int main() 55 { 56 char szUser[50]; 57 char szCode[50]; 58 printf("请输入用户名:"); 59 scanf("%s", szUser); 60 61 size_t len = strlen(szUser); 62 if(len<3 || len>9) 63 { 64 printf("错误!用户名长度范围必须在3-9!\n"); 65 system("pause"); 66 return 0; 67 } 68 69 Keygen(szUser, szCode); 70 printf("序列号是: %s\n", szCode); 71 system("pause"); 72 73 return 0; 74 }