系统 : Windows xp
程序 : crackme1
程序下载地址 :http://pan.baidu.com/s/1gdY4wMJ
要求 : 分析算法
使用工具 :OD
可在“PEDIY CrackMe 2007”中查找关于此程序的讨论,标题为“muckis‘s crakcme #1破解(检测OD)”。
OD载入程序,键入命令:bp GetWindowTextA,成功断下,并返回程序领空:
004014A0 |. 68 EA030000 push 3EA ; /ControlID = 3EA (1002.) 004014A5 |. 8B45 08 mov eax, dword ptr [ebp+8] ; | 004014A8 |. 50 push eax ; |hWnd 004014A9 |. FF15 CC024300 call dword ptr [<&USER32.GetDlgItem>] ; \GetDlgItem 004014AF |. 3BF4 cmp esi, esp 004014B1 |. E8 0A1E0000 call 004032C0 004014B6 |. 8945 9C mov dword ptr [ebp-64], eax 004014B9 |. 8BF4 mov esi, esp 004014BB |. 6A 14 push 14 ; /Count = 14 (20.) 004014BD |. 8D4D C0 lea ecx, dword ptr [ebp-40] ; | 004014C0 |. 51 push ecx ; |Buffer 004014C1 |. 8B55 9C mov edx, dword ptr [ebp-64] ; | 004014C4 |. 52 push edx ; |hWnd 004014C5 |. FF15 D0024300 call dword ptr [<&USER32.GetWindowTextA>] ; \GetWindowTextA 004014CB |. 3BF4 cmp esi, esp 004014CD |. E8 EE1D0000 call 004032C0 004014D2 |. 8BF4 mov esi, esp 004014D4 |. 68 EB030000 push 3EB ; /ControlID = 3EB (1003.) 004014D9 |. 8B45 08 mov eax, dword ptr [ebp+8] ; | 004014DC |. 50 push eax ; |hWnd 004014DD |. FF15 CC024300 call dword ptr [<&USER32.GetDlgItem>] ; \GetDlgItem 004014E3 |. 3BF4 cmp esi, esp 004014E5 |. E8 D61D0000 call 004032C0 004014EA |. 8945 9C mov dword ptr [ebp-64], eax 004014ED |. 8BF4 mov esi, esp 004014EF |. 6A 14 push 14 ; /Count = 14 (20.) 004014F1 |. 8D4D A0 lea ecx, dword ptr [ebp-60] ; | 004014F4 |. 51 push ecx ; |Buffer 004014F5 |. 8B55 9C mov edx, dword ptr [ebp-64] ; | 004014F8 |. 52 push edx ; |hWnd 004014F9 |. FF15 D0024300 call dword ptr [<&USER32.GetWindowTextA>] ; \GetWindowTextA 004014FF |. 3BF4 cmp esi, esp ; ↑获取字串 00401501 |. E8 BA1D0000 call 004032C0 00401506 |. E8 FFFAFFFF call 0040100A 0040150B |. 85C0 test eax, eax 0040150D |. 74 4D je short 0040155C
40150B处的判断指令根据上一条指令call的结果来决定是否跳转,有些cracker喜欢直接根据call执行之后的状态来猜测子程序的作用。此处,不跟入子程序,40150D处不跳转:
0040150F |. 6A 0A push 0A 00401511 |. 8D45 E0 lea eax, dword ptr [ebp-20] ; 取一段地址 00401514 |. 50 push eax ; 入栈 00401515 |. 8D4D C0 lea ecx, dword ptr [ebp-40] ; 取用户名 00401518 |. 51 push ecx ; 入栈 00401519 |. E8 0AFBFFFF call 00401028 0040151E |. 83C4 04 add esp, 4 00401521 |. 50 push eax 00401522 |. E8 49A00000 call 0040B570 00401527 |. 83C4 0C add esp, 0C 0040152A |. 50 push eax 0040152B |. 8D55 A0 lea edx, dword ptr [ebp-60] 0040152E |. 52 push edx 0040152F |. E8 6C1F0000 call 004034A0 00401534 |. 83C4 08 add esp, 8 00401537 |. 85C0 test eax, eax 00401539 |. 75 1F jnz short 0040155A 0040153B |. 8BF4 mov esi, esp 0040153D |. 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL 0040153F |. 68 4CA04200 push 0042A04C ; |lolmuckis crackme #1\ncoded 2006 by mucki 00401544 |. 68 34A04200 push 0042A034 ; |make a keygen ;-) 00401549 |. 8B45 08 mov eax, dword ptr [ebp+8] ; | 0040154C |. 50 push eax ; |hOwner 0040154D |. FF15 D4024300 call dword ptr [<&USER32.MessageBoxA>] ; \MessageBoxA
看上去很正常,有一段F(用户名)函数,有一段指令判断是否注册成功,还有MessageBox提示。似乎就是正确的流程了。
然后,当你分析该算法并写出注册机,高高兴兴地想要测试时就会发现,注册机根本不起作用。
没错,就像你想的那样,正是之前看似人畜无害的call引发了一连串的“蝴蝶效应”。
跟入call:
0040100A $ /E9 01060000 jmp 00401610
继续跟:
00401610 /> \55 push ebp 00401611 |. 8BEC mov ebp, esp 00401613 |. 83EC 40 sub esp, 40 00401616 |. 53 push ebx 00401617 |. 56 push esi 00401618 |. 57 push edi 00401619 |. 8D7D C0 lea edi, dword ptr [ebp-40] ; 取一段内存 0040161C |. B9 10000000 mov ecx, 10 00401621 |. B8 CCCCCCCC mov eax, CCCCCCCC 00401626 |. F3:AB rep stos dword ptr es:[edi] ; 用CCCCCCCC填充16个DWORD内存 00401628 |. 64:A1 1800000>mov eax, dword ptr fs:[18] ; 找到TEB地址 0040162E |. 3E:8B40 30 mov eax, dword ptr [eax+30] ; 在TEB偏移30H处获得PEB地址 00401632 |. 3E:0FB640 02 movzx eax, byte ptr [eax+2] ; BeingDebugged标志 00401637 |. 83F8 01 cmp eax, 1 0040163A |. 74 04 je short 00401640 0040163C |. 33C0 xor eax, eax 0040163E |. EB 05 jmp short 00401645 00401640 |> B8 01000000 mov eax, 1 00401645 |> 5F pop edi 00401646 |. 5E pop esi 00401647 |. 5B pop ebx 00401648 |. 83C4 40 add esp, 40 ; 平衡堆栈 0040164B |. 3BEC cmp ebp, esp 0040164D |. E8 6E1C0000 call 004032C0 00401652 |. 8BE5 mov esp, ebp 00401654 |. 5D pop ebp 00401655 \. C3 retn
Win32 Api为程序提供了IsDebuggerPresent判断自己是否处于调试状态,这个函数读取了当前进程PEB中的BeingDebugged标志。通过判断这个标志,可知程序是否被别人调试。
这里,利用OD的插件HideOD对抗反调试。打开HideOD->设置,选择HideNtDebugBit,单击OK,再单击Hide。这样,程序就跳转进入正常的流程:
0040155C |> \6A 0A push 0A 0040155E |. 8D4D E0 lea ecx, dword ptr [ebp-20] ; 取一段地址 00401561 |. 51 push ecx ; 入栈 00401562 |. 8D55 C0 lea edx, dword ptr [ebp-40] ; 取用户名 00401565 |. 52 push edx ; 入栈 00401566 |. E8 B3FAFFFF call 0040101E ; 根据用户名算出一个值 0040156B |. 83C4 04 add esp, 4 0040156E |. 50 push eax 0040156F |. E8 FC9F0000 call 0040B570 ; 根据这个值算出密钥 00401574 |. 83C4 0C add esp, 0C ; 平衡堆栈 00401577 |. 50 push eax ; 密钥入栈 00401578 |. 8D45 A0 lea eax, dword ptr [ebp-60] ; 取出序列号 0040157B |. 50 push eax ; 序列号入栈 0040157C |. E8 1F1F0000 call 004034A0 ; 是否相等? 00401581 |. 83C4 08 add esp, 8 ; 平衡堆栈 00401584 |. 85C0 test eax, eax ; 相等? 00401586 |. 75 18 jnz short 004015A0 ; 不相等则跳转 00401588 |. 8BF4 mov esi, esp 0040158A |. 68 1CA04200 push 0042A01C ; /now make a keygen! 0040158F |. 8B4D 9C mov ecx, dword ptr [ebp-64] ; | 00401592 |. 51 push ecx ; |hWnd 00401593 |. FF15 D8024300 call dword ptr [<&USER32.SetWindowTextA>] ; \SetWindowTextA
如果之前粗心大意没有发现反调试就开始分析算法写出注册机的话,那么你只能重新开始。没想到,一条简单的call指令,却能增加Cracker这么多的工作量!
跟入40101E:
0040101E $ /E9 9D010000 jmp 004011C0
继续跟:
004011C0 /> \55 push ebp 004011C1 |. 8BEC mov ebp, esp 004011C3 |. 83EC 50 sub esp, 50 ; 开辟空间 004011C6 |. 53 push ebx 004011C7 |. 56 push esi 004011C8 |. 57 push edi 004011C9 |. 8D7D B0 lea edi, dword ptr [ebp-50] ; 取一段内存空间 004011CC |. B9 14000000 mov ecx, 14 004011D1 |. B8 CCCCCCCC mov eax, CCCCCCCC 004011D6 |. F3:AB rep stos dword ptr es:[edi] ; 用CC填充内存空间 004011D8 |. C745 FC 00000>mov dword ptr [ebp-4], 0 004011DF |. C745 F8 00000>mov dword ptr [ebp-8], 0 004011E6 |. C745 F4 00000>mov dword ptr [ebp-C], 0 004011ED |. 8B45 08 mov eax, dword ptr [ebp+8] ; 取用户名字串 004011F0 |. 50 push eax ; 入栈 004011F1 |. E8 2A220000 call 00403420 ; 算出长度 004011F6 |. 83C4 04 add esp, 4 ; 平衡堆栈 004011F9 |. 8945 F0 mov dword ptr [ebp-10], eax ; 保存长度 004011FC |. 8B4D 08 mov ecx, dword ptr [ebp+8] 004011FF |. 51 push ecx ; 用户名入栈 00401200 |. E8 1B210000 call 00403320 ; 转化大小写 00401205 |. 83C4 04 add esp, 4 ; 平衡堆栈 00401208 |. C745 FC 00000>mov dword ptr [ebp-4], 0 0040120F |. EB 09 jmp short 0040121A 00401211 |> 8B55 FC /mov edx, dword ptr [ebp-4] 00401214 |. 83C2 01 |add edx, 1 ; 循环变量自增 00401217 |. 8955 FC |mov dword ptr [ebp-4], edx 0040121A |> 8B45 FC mov eax, dword ptr [ebp-4] ; 取一段内存空间 0040121D |. 3B45 F0 |cmp eax, dword ptr [ebp-10] ; 是否迭代完毕? 00401220 |. 7D 3C |jge short 0040125E 00401222 |. 8B4D 08 |mov ecx, dword ptr [ebp+8] ; 取用户名字串 00401225 |. 034D FC |add ecx, dword ptr [ebp-4] ; 加上循环变量 00401228 |. 33D2 |xor edx, edx 0040122A |. 8A11 |mov dl, byte ptr [ecx] ; 迭代用户名字串 0040122C |. 83FA 20 |cmp edx, 20 ; 是空格? 0040122F |. 74 2B |je short 0040125C ; 则continue 00401231 |. 8B45 08 |mov eax, dword ptr [ebp+8] ; 取用户名字串 00401234 |. 0345 FC |add eax, dword ptr [ebp-4] ; 加上循环变量 00401237 |. 33C9 |xor ecx, ecx 00401239 |. 8A08 |mov cl, byte ptr [eax] ; 迭代用户名字串 0040123B |. 894D F4 |mov dword ptr [ebp-C], ecx ; 保存字符 0040123E |. 8B55 F4 |mov edx, dword ptr [ebp-C] 00401241 |. 69D2 7A150000 |imul edx, edx, 157A ; 字符 乘以 157A 00401247 |. 8955 F4 |mov dword ptr [ebp-C], edx ; 保存结果 0040124A |. 8B45 F4 |mov eax, dword ptr [ebp-C] ; 取结果 0040124D |. 83E8 01 |sub eax, 1 ; 减去1 00401250 |. 8945 F4 |mov dword ptr [ebp-C], eax ; 并保存 00401253 |. 8B4D F8 |mov ecx, dword ptr [ebp-8] ; 取一段内存 00401256 |. 034D F4 |add ecx, dword ptr [ebp-C] ; 累加结果 00401259 |. 894D F8 |mov dword ptr [ebp-8], ecx ; 保存累加结果 0040125C |>^ EB B3 \jmp short 00401211 0040125E |> 8B75 F8 mov esi, dword ptr [ebp-8] 00401261 |. 6BF6 0A imul esi, esi, 0A ; 累加结果乘以0A 00401264 |. 8B55 F8 mov edx, dword ptr [ebp-8] ; 取累加结果 00401267 |. 52 push edx ; 入栈 00401268 |. E8 98FDFFFF call 00401005 ; 浮点运算call 0040126D |. 83C4 04 add esp, 4 00401270 |. 03C6 add eax, esi ; 累加结果加上 浮点运算call的结果 00401272 |. 5F pop edi 00401273 |. 5E pop esi 00401274 |. 5B pop ebx 00401275 |. 83C4 50 add esp, 50 00401278 |. 3BEC cmp ebp, esp 0040127A |. E8 41200000 call 004032C0 0040127F |. 8BE5 mov esp, ebp 00401281 |. 5D pop ebp 00401282 \. C3 retn
跟入浮点运算call:
00401005 $ /E9 D6000000 jmp 004010E0
继续跟:
004010E0 /> \55 push ebp 004010E1 |. 8BEC mov ebp, esp 004010E3 |. 83EC 58 sub esp, 58 004010E6 |. 53 push ebx 004010E7 |. 56 push esi 004010E8 |. 57 push edi 004010E9 |. 8D7D A8 lea edi, dword ptr [ebp-58] 004010EC |. B9 16000000 mov ecx, 16 004010F1 |. B8 CCCCCCCC mov eax, CCCCCCCC 004010F6 |. F3:AB rep stos dword ptr es:[edi] ; 用CC填充一段内存 004010F8 |. C745 FC 00000>mov dword ptr [ebp-4], 0 004010FF |. C745 F8 01000>mov dword ptr [ebp-8], 1 00401106 |. C745 F4 00000>mov dword ptr [ebp-C], 0 0040110D |. C745 FC 0A000>mov dword ptr [ebp-4], 0A 00401114 |. EB 09 jmp short 0040111F 00401116 |> 8B45 FC /mov eax, dword ptr [ebp-4] 00401119 |. 83E8 01 |sub eax, 1 0040111C |. 8945 FC |mov dword ptr [ebp-4], eax 0040111F |> 837D FC 00 cmp dword ptr [ebp-4], 0 ; 小于0? 00401123 |. 7C 4F |jl short 00401174 ; 则退出循环 00401125 |. DB45 08 |fild dword ptr [ebp+8] ; 将累加结果转化为real80对象,并压栈 00401128 |. DD5D E8 |fstp qword ptr [ebp-18] ; 保存转化成浮点数的累加结果 0040112B |. DB45 FC |fild dword ptr [ebp-4] ; 将0A转化为real80对象,并压栈 0040112E |. 83EC 08 |sub esp, 8 ; 开辟8个字节的内存空间 00401131 |. DD1C24 |fstp qword ptr [esp] ; 保存转化成浮点数的0A 00401134 |. 68 00002440 |push 40240000 ; esp + 4入栈 00401139 |. 6A 00 |push 0 0040113B |. E8 C91E0000 |call 00403009 00401140 |. 83C4 10 |add esp, 10 00401143 |. DC7D E8 |fdivr qword ptr [ebp-18] ; 浮点反除 00401146 |. E8 AD210000 |call 004032F8 0040114B |. 8945 F0 |mov dword ptr [ebp-10], eax 0040114E |. 837D F0 00 |cmp dword ptr [ebp-10], 0 00401152 |. 7E 0F |jle short 00401163 00401154 |. 8B4D F8 |mov ecx, dword ptr [ebp-8] 00401157 |. 51 |push ecx 00401158 |. E8 DAFEFFFF |call 00401037 0040115D |. 83C4 04 |add esp, 4 00401160 |. 8945 F8 |mov dword ptr [ebp-8], eax 00401163 |> 8B55 F0 |mov edx, dword ptr [ebp-10] 00401166 |. 0FAF55 F8 |imul edx, dword ptr [ebp-8] 0040116A |. 8B45 F4 |mov eax, dword ptr [ebp-C] 0040116D |. 03C2 |add eax, edx 0040116F |. 8945 F4 |mov dword ptr [ebp-C], eax 00401172 |.^ EB A2 \jmp short 00401116 00401174 |> 8B45 F4 mov eax, dword ptr [ebp-C] ; 算出一个值 00401177 |. 99 cdq ; 把EDX的所有位都设成EAX最高位的值 00401178 |. B9 0A000000 mov ecx, 0A 0040117D |. F7F9 idiv ecx ; 除以0A 0040117F |. 8BC2 mov eax, edx ; 余数放入eax 00401181 |. 5F pop edi 00401182 |. 5E pop esi 00401183 |. 5B pop ebx 00401184 |. 83C4 58 add esp, 58 00401187 |. 3BEC cmp ebp, esp 00401189 |. E8 32210000 call 004032C0 0040118E |. 8BE5 mov esp, ebp 00401190 |. 5D pop ebp 00401191 \. C3 retn
这个浮点运算call中包含了大量的浮点运算和子程序,我还要进一步了解浮点运算的汇编指令才能完整分析算法。这里,只能了解到密钥是累加的结果 加上 浮点运算call算出的余数。
一路retn,回到主函数,继续跟入40B570:
0040B570 /$ 55 push ebp 0040B571 |. 8BEC mov ebp, esp 0040B573 |. 837D 10 0A cmp dword ptr [ebp+10], 0A 0040B577 |. 75 1E jnz short 0040B597 0040B579 |. 837D 08 00 cmp dword ptr [ebp+8], 0 0040B57D |. 7D 18 jge short 0040B597 0040B57F |. 6A 01 push 1 0040B581 |. 8B45 10 mov eax, dword ptr [ebp+10] 0040B584 |. 50 push eax 0040B585 |. 8B4D 0C mov ecx, dword ptr [ebp+C] 0040B588 |. 51 push ecx 0040B589 |. 8B55 08 mov edx, dword ptr [ebp+8] 0040B58C |. 52 push edx 0040B58D |. E8 2E000000 call 0040B5C0 0040B592 |. 83C4 10 add esp, 10 0040B595 |. EB 16 jmp short 0040B5AD 0040B597 |> 6A 00 push 0 0040B599 |. 8B45 10 mov eax, dword ptr [ebp+10] 0040B59C |. 50 push eax 0040B59D |. 8B4D 0C mov ecx, dword ptr [ebp+C] 0040B5A0 |. 51 push ecx 0040B5A1 |. 8B55 08 mov edx, dword ptr [ebp+8] 0040B5A4 |. 52 push edx 0040B5A5 |. E8 16000000 call 0040B5C0 ; 累加结果转化为十进制 0040B5AA |. 83C4 10 add esp, 10 0040B5AD |> 8B45 0C mov eax, dword ptr [ebp+C] 0040B5B0 |. 5D pop ebp 0040B5B1 \. C3 retn
至此,该程序就分析完毕了。除了反调试,程序中还运用了大量的浮点运算。这让我非常头疼,这两天准备去补下浮点运算相关的汇编指令,把这个程序的注册机写出来。
给出一个可用的用户名-序列号组合:
用户名:pediy
序列号:20837376