系统 : 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