[反汇编练习] 160个CrackMe之006.
本系列文章的目的是从一个没有任何经验的新手的角度(其实就是我自己),一步步尝试将160个CrackMe全部破解,如果可以,通过任何方式写出一个类似于注册机的东西。
其中,文章中按照如下逻辑编排(解决如下问题):
1、使用什么环境和工具
2、程序分析
3、思路分析和破解流程
4、注册机的探索
----------------------------------
提醒各位看客: 如果文章中的逻辑看不明白,那你一定是没有亲手操刀!OD中的跳转提示很强大,只要你跟踪了,不用怎么看代码就理解了!
----------------------------------
1、工具和环境:
WinXP SP3 + 52Pojie六周年纪念版OD + PEID + 汇编金手指。
160个CrackMe的打包文件。
下载地址: http://pan.baidu.com/s/1xUWOY 密码: jbnq
注:
1、Win7系统对于模块和程序开启了随机初始地址的功能,会给分析带来很大的负担,所以不建议使用Win7进行分析。
2、以上工具都是在52PoJie论坛下的原版程序,NOD32不报毒,个人承诺绝对不会进行任何和木马病毒相关内容。
2、程序分析:
想要破解一个程序,必须先了解这个程序。所以,在破解过程中,对最初程序的分析很重要,他可以帮助我们理解作者的目的和意图,特别是对于注册码的处理细节,从而方便我们反向跟踪和推导。
和上一节一样,打开CHM,选择第6个aLoNg3x,保存下来。运行程序,程序界面如下:
这是一个典型的Name/Serial(Delphi)程序,但是,因为是Delphi的我恨死它了!
随意输入Nome和Codice,我们发现OK没法点。好吧,我们看看About-Help,'The purpose of this CrackMe v. 1.00 by aLoNg3x is to MAKE INVISIBLE the buttons "OK" and "Cancella" in order to see the Ringzer0 logo. So you must insert the correct codes'。大概意思就是将两个按钮直接隐藏掉就算是破解了。
看到这里我有一种不祥的预感:难道又是一个游戏?事实证明我的预感是很准的….
讨厌也没办法,使用PEID查看:没有壳,delphi 4.0 - 5.0, 随意一个Dede或者IDR什么的都能反编译它。这里我还是选择昨天才找到的IDR。
3、思路分析和破解流程
遇到这种游戏类型的,根本就不可能有啥思路,直接反编译,大概地看看:
双击_CrackMe100模块,我们看到只有4个函数,codiceChange/Okclick/NomeChange/CancellaClick/AboutClick事件。除了About之外,其他的似乎我们都要跟踪。
由于OK按钮按时不可用,我们先从codiceChange开始:
在IDR中双击codiceChange事件,在反汇编区域找到开头地址:00442AF4,使用OD打开程序,Ctrl+G,输入这个地址,根据IDR反汇编的提示,F8调试,记录重要数据:
00442C78 /. 55 push ebp ; // _CrackMe100::TPrincipale.CodiceChange 00442C79 |. 8BEC mov ebp,esp 00442C7B |. 33C9 xor ecx,ecx 00442C7D |. 51 push ecx 00442C7E |. 51 push ecx 00442C7F |. 51 push ecx 00442C80 |. 51 push ecx 00442C81 |. 53 push ebx 00442C82 |. 56 push esi 00442C83 |. 8BD8 mov ebx,eax 00442C85 |. 33C0 xor eax,eax 00442C87 |. 55 push ebp 00442C88 |. 68 562D4400 push 00442D56 00442C8D |. 64:FF30 push dword ptr fs:[eax] 00442C90 |. 64:8920 mov dword ptr fs:[eax],esp 00442C93 |. 8D55 F8 lea edx,[local.2] 00442C96 |. 8B83 E0020000 mov eax,dword ptr ds:[ebx+0x2E0] ; TPrincipale.Codice:TEdit 00442C9C |. E8 1F06FEFF call 004232C0 ; TControl.GetText 00442CA1 |. 8B45 F8 mov eax,[local.2] ; // eax=11223 00442CA4 |. 8D55 FC lea edx,[local.1] 00442CA7 |. E8 ACFCFBFF call 00402958 ; @ValLong 00442CAC |. 8BF0 mov esi,eax ; // eax=0x2BD7=11223 00442CAE |. 837D FC 00 cmp [local.1],0x0 00442CB2 |. 74 18 je short 00442CCC 00442CB4 |. 8D55 F4 lea edx,[local.3] 00442CB7 |. 8BC6 mov eax,esi 00442CB9 |. E8 8249FCFF call 00407640 ; IntToStr 00442CBE |. 8B55 F4 mov edx,[local.3] 00442CC1 |. 8B83 E0020000 mov eax,dword ptr ds:[ebx+0x2E0] ; TPrincipale.Codice:TEdit 00442CC7 |. E8 2406FEFF call 004232F0 00442CCC |> 8B83 D0020000 mov eax,dword ptr ds:[ebx+0x2D0] ; TPrincipale.Cancella:TButton 00442CD2 |. 8078 47 00 cmp byte ptr ds:[eax+0x47],0x0 ; TButton.FVisible:Boolean 00442CD6 |. 75 0F jnz short 00442CE7 00442CD8 |. B2 01 mov dl,0x1 00442CDA |. 8B83 CC020000 mov eax,dword ptr ds:[ebx+0x2CC] ; Principale.Ok:TButton 00442CE0 |. 8B08 mov ecx,dword ptr ds:[eax] 00442CE2 |. FF51 60 call dword ptr ds:[ecx+0x60] ; TControl.SetEnabled 00442CE5 |. EB 49 jmp short 00442D30 00442CE7 |> 8D55 F8 lea edx,[local.2] 00442CEA |. 8B83 E0020000 mov eax,dword ptr ds:[ebx+0x2E0] ; TPrincipale.Codice:TEdit 00442CF0 |. E8 CB05FEFF call 004232C0 ; TControl.GetText 00442CF5 |. 8B45 F8 mov eax,[local.2] 00442CF8 |. 50 push eax 00442CF9 |. 8D55 F0 lea edx,[local.4] 00442CFC |. 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC] ; TPrincipale.Nome:TEdit 00442D02 |. E8 B905FEFF call 004232C0 ; TControl.GetText 00442D07 |. 8B45 F0 mov eax,[local.4] ; eax=bbdxf 00442D0A |. 5A pop edx ; 11223 00442D0B |. E8 2CFDFFFF call 00442A3C ; // 验证的CALL 00442D10 |. 84C0 test al,al ; al=0 00442D12 |. 74 0F je short 00442D23 00442D14 |. B2 01 mov dl,0x1 00442D16 |. 8B83 CC020000 mov eax,dword ptr ds:[ebx+0x2CC] ; TPrincipale.Ok:TButton 00442D1C |. 8B08 mov ecx,dword ptr ds:[eax] 00442D1E |. FF51 60 call dword ptr ds:[ecx+0x60] ; TControl.SetEnabled 00442D21 |. EB 0D jmp short 00442D30 00442D23 |> 33D2 xor edx,edx 00442D25 |. 8B83 CC020000 mov eax,dword ptr ds:[ebx+0x2CC] ; TPrincipale.Ok:TButton 00442D2B |. 8B08 mov ecx,dword ptr ds:[eax] 00442D2D |. FF51 60 call dword ptr ds:[ecx+0x60] ; TControl.SetEnabled 00442D30 |> 33C0 xor eax,eax 00442D32 |. 5A pop edx 00442D33 |. 59 pop ecx 00442D34 |. 59 pop ecx 00442D35 |. 64:8910 mov dword ptr fs:[eax],edx 00442D38 |. 68 5D2D4400 push 00442D5D 00442D3D |> 8D45 F0 lea eax,[local.4] 00442D40 |. E8 730AFCFF call 004037B8 ; @LStrClr 00442D45 |. 8D45 F4 lea eax,[local.3] 00442D48 |. E8 6B0AFCFF call 004037B8 ; @LStrClr 00442D4D |. 8D45 F8 lea eax,[local.2] 00442D50 |. E8 630AFCFF call 004037B8 ; @LStrClr 00442D55 . C3 retn 00442D56 .^ E9 1D05FCFF jmp 00403278 00442D5B .^ EB E0 jmp short 00442D3D 00442D5D . 5E pop esi 00442D5E . 5B pop ebx 00442D5F . 8BE5 mov esp,ebp 00442D61 . 5D pop ebp 00442D62 . C3 retn
发现除了Call 00442A3C 其它的都有具体含义,并且当这个call的返回值不为0时,OK按钮会被设置为Enable,看来关键Call就是它了!F7跟进去:
00442A3C /$ 55 push ebp ; // 一个子函数 00442A3D |. 8BEC mov ebp,esp 00442A3F |. 83C4 F8 add esp,-0x8 00442A42 |. 53 push ebx 00442A43 |. 56 push esi 00442A44 |. 8955 F8 mov [local.2],edx 00442A47 |. 8945 FC mov [local.1],eax 00442A4A |. 8B45 FC mov eax,[local.1] 00442A4D |. E8 9611FCFF call 00403BE8 ; // 引用计数CALL 00442A52 |. 8B45 F8 mov eax,[local.2] 00442A55 |. E8 8E11FCFF call 00403BE8 00442A5A |. 33C0 xor eax,eax 00442A5C |. 55 push ebp 00442A5D |. 68 E52A4400 push 00442AE5 00442A62 |. 64:FF30 push dword ptr fs:[eax] 00442A65 |. 64:8920 mov dword ptr fs:[eax],esp 00442A68 |. 8B45 FC mov eax,[local.1] ; bbdxf 00442A6B |. E8 C40FFCFF call 00403A34 ; @LStrLen 00442A70 |. 83F8 05 cmp eax,0x5 ; // len > 5 00442A73 |. 7E 53 jle short 00442AC8 00442A75 |. 8B45 FC mov eax,[local.1] 00442A78 |. E8 B70FFCFF call 00403A34 00442A7D |. 8BD8 mov ebx,eax ; // eax=6 00442A7F |. 8B45 FC mov eax,[local.1] ; // eax = bbdxf6 00442A82 |. E8 AD0FFCFF call 00403A34 00442A87 |. 8BD0 mov edx,eax ; edx=len 00442A89 |. 4A dec edx ; edx-- 00442A8A |. 85D2 test edx,edx 00442A8C |. 7E 20 jle short 00442AAE 00442A8E |. B8 01000000 mov eax,0x1 ; // eax==1,序号 00442A93 |> 8B4D FC /mov ecx,[local.1] ; // bbdxf6 00442A96 |. 0FB64C01 FF |movzx ecx,byte ptr ds:[ecx+eax-0x1] ; // 取一个字符,现在是第一个 00442A9B |. 8B75 FC |mov esi,[local.1] 00442A9E |. 0FB63406 |movzx esi,byte ptr ds:[esi+eax] ; // 取第二个字符 00442AA2 |. 0FAFCE |imul ecx,esi ; // esi=62,ecx=62,乘法 00442AA5 |. 0FAFC8 |imul ecx,eax ; // ecx*eax=62*62*1 00442AA8 |. 03D9 |add ebx,ecx ; // ebx=len; ebx+=ecx 00442AAA |. 40 |inc eax ; // eax++ 00442AAB |. 4A |dec edx ; // edx-- 00442AAC |.^ 75 E5 jnz short 00442A93 00442AAE |> 8B45 F8 mov eax,[local.2] ; // eax= 6, ebx=0002298E 00442AB1 |. E8 BA4BFCFF call 00407670 ; StrToInt 00442AB6 |. 2BD8 sub ebx,eax ; // eax=000419C4=268740,ebx-eax 00442AB8 |. 81FB 9A020000 cmp ebx,0x29A ; // 差==0x29A 00442ABE 75 04 jnz short 00442AC4 00442AC0 |. B3 01 mov bl,0x1 ; // 可见 00442AC2 |. EB 06 jmp short 00442ACA 00442AC4 |> 33DB xor ebx,ebx ; // 不可见 00442AC6 |. EB 02 jmp short 00442ACA 00442AC8 |> 33DB xor ebx,ebx
这一块的算法类似于:
char Nome[] = "bbdxf6" // eax char code[] = "112233" int nLen = strlen(Nome); // esi int nRet = nLen; // ebx for( int i=1;i<nLen;i++) { nRet += Nome[i-1]*Nome[i]*i; } nRet -= atoi(code); if( nRet == 0x29A ) { // 返回0x01,OK按钮可以使用 }else{ // 返回0,OK按钮禁止使用 }
当然,爆破可以直接在这个关键CALL之后将JE使用NOP替代。
下面同样方法进行OK按钮的事件处理:
00442D64 /. 55 push ebp ; //_CrackMe100::TPrincipale.OkClick 00442D65 |. 8BEC mov ebp,esp 00442D67 |. 6A 00 push 0x0 00442D69 |. 53 push ebx 00442D6A |. 8BD8 mov ebx,eax 00442D6C |. 33C0 xor eax,eax 00442D6E |. 55 push ebp 00442D6F |. 68 ED2D4400 push 00442DED 00442D74 |. 64:FF30 push dword ptr fs:[eax] 00442D77 |. 64:8920 mov dword ptr fs:[eax],esp 00442D7A |. 8B83 D0020000 mov eax,dword ptr ds:[ebx+0x2D0] ; TPrincipale.Cancella:TButton 00442D80 |. 8078 47 01 cmp byte ptr ds:[eax+0x47],0x1 ; TButton.FVisible:Boolean 00442D84 |. 75 12 jnz short 00442D98 ; // 判断cancle按钮是否被隐藏了 00442D86 |. BA 002E4400 mov edx,00442E00 ; UNICODE "0" 00442D8B |. 8B83 E0020000 mov eax,dword ptr ds:[ebx+0x2E0] ; TPrincipale.Codice:TEdit 00442D91 E8 5A05FEFF call 004232F0 ; TControl.SetText 00442D96 EB 3F jmp short 00442DD7 00442D98 |> 8D55 FC lea edx,[local.1] 00442D9B |. 8B83 E0020000 mov eax,dword ptr ds:[ebx+0x2E0] ; TPrincipale.Codice:TEdit 00442DA1 |. E8 1A05FEFF call 004232C0 ; TControl.GetText 00442DA6 |. 8B45 FC mov eax,[local.1] 00442DA9 |. E8 C248FCFF call 00407670 ; StrToInt 00442DAE |. 50 push eax 00442DAF |. 8D55 FC lea edx,[local.1] 00442DB2 |. 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC] ; TPrincipale.Nome:TEdit 00442DB8 |. E8 0305FEFF call 004232C0 ; TControl.GetText 00442DBD |. 8B45 FC mov eax,[local.1] 00442DC0 |. 5A pop edx 00442DC1 |. E8 DAFDFFFF call 00442BA0 ; // 关键检测call 00442DC6 |. 84C0 test al,al 00442DC8 |. 74 0D je short 00442DD7 00442DCA |. 33D2 xor edx,edx 00442DCC |. 8B83 CC020000 mov eax,dword ptr ds:[ebx+0x2CC] ; TPrincipale.Ok:TButton 00442DD2 |. E8 D903FEFF call 004231B0 ; TControl.SetVisible 00442DD7 |> 33C0 xor eax,eax 00442DD9 |. 5A pop edx 00442DDA |. 59 pop ecx 00442DDB |. 59 pop ecx 00442DDC |. 64:8910 mov dword ptr fs:[eax],edx 00442DDF |. 68 F42D4400 push 00442DF4 00442DE4 |> 8D45 FC lea eax,[local.1] 00442DE7 |. E8 CC09FCFF call 004037B8 ; @LStrClr 00442DEC . C3 retn 00442DED .^ E9 8604FCFF jmp 00403278 00442DF2 .^ EB F0 jmp short 00442DE4 00442DF4 . 5B pop ebx 00442DF5 . 59 pop ecx 00442DF6 . 5D pop ebp 00442DF7 . C3 retn
关键CALL的分析:
00442BE8 |. /7E 60 jle short 00442C4A 00442BEA |. |8B45 F8 mov eax,[local.2] ; 141044 00442BED |. |E8 420EFCFF call 00403A34 ; StrLen 00442BF2 |. |8BF0 mov esi,eax 00442BF4 |. |83FE 01 cmp esi,0x1 00442BF7 |. |7C 2F jl short 00442C28 00442BF9 |> |8D45 F4 /lea eax,[local.3] 00442BFC |. |E8 0310FCFF |call 00403C04 ; // 检查string引用 00442C01 |. |8D4430 FF |lea eax,dword ptr ds:[eax+esi-0x1] ; // esi=len 00442C05 |. |50 |push eax 00442C06 |. |8B45 F8 |mov eax,[local.2] 00442C09 |. |0FB64430 FF |movzx eax,byte ptr ds:[eax+esi-0x1] ; // 最后一个 00442C0E |. |F7E8 |imul eax ; eax=eax*eax 00442C10 |. |0FBFC0 |movsx eax,ax ; // eax=a90; 只要低16位 00442C13 |. |F7EE |imul esi ; // eax*len-- 00442C15 |. |B9 19000000 |mov ecx,0x19 ; // ecx=0x19 00442C1A |. |99 |cdq 00442C1B |. |F7F9 |idiv ecx ; // 除以ecx,eax=整除值,edx=Mod值 00442C1D |. |83C2 41 |add edx,0x41 ; // edx=mod之后+0x14 00442C20 |. |58 |pop eax 00442C21 |. |8810 |mov byte ptr ds:[eax],dl 00442C23 |. |4E |dec esi 00442C24 |. |85F6 |test esi,esi 00442C26 |.^|75 D1 jnz short 00442BF9 00442C28 |> |8B45 F4 mov eax,[local.3] ; BIDQUY 00442C2B |. |8B55 FC mov edx,[local.1] ; bbdxf6 00442C2E |. |E8 110FFCFF call 00403B44 ; strcmp 00442C33 |75 17 jnz short 00442C4C 00442C35 |. |8B45 FC mov eax,[local.1] 00442C38 |. |8B55 F4 mov edx,[local.3] 00442C3B |. |E8 040FFCFF call 00403B44 00442C40 |. |75 04 jnz short 00442C46 00442C42 |. |B3 01 mov bl,0x1 00442C44 |. |EB 06 jmp short 00442C4C 00442C46 |> |33DB xor ebx,ebx 00442C48 |. |EB 02 jmp short 00442C4C
这一块的算法类似于:
char Nome[] = "bbdxf6" // eax char code[] = "112233" int nLen = strlen(Nome); // esi int nRet = 0; // eax for( int i=0;i<nLen;i++) { int nTmp = Nome[nLen-1-i]*Nome[nLen-1-i]*(nLen-i); nRet = nTmp%0x19 + 0x41; // 转换为大写字母 Nome[nLen-1-i] = nRet; } int nrt = strCmp("bbdxf6",Nome); //x2,两次比较原始字符串和生成后的字符串是否相同 if( nrt == 0 ) { // 返回0x01,OK按钮隐藏 }else{ // 返回0,OK按钮显示 }
到这里我就停下来了。为什么?因为有一种被戏耍的感觉。他这个游戏大概应该是这样的:
1、先根据Nome和Codice的值满足第一个算法,则OK按钮变为可以点击。-- 算法1
2、再根据Nome和Codice满足第二个算法,则OK会被隐藏。-- 算法2
3、Nome满足第三种算法时,点击Cancella按钮,Cancella按钮会隐藏。-- 算法3
这样,才会实现作者说的把两个按钮都隐藏了的效果。这都不是关键!关键是不借助任何东西找到同时满足这三种算法的Nome和Codice的几率和买体彩中头奖的概率差不多了!
次篇文章就到这里了!感兴趣的可继续跟踪其他的2个事件,结构和关键CAll都和这两个结构差不多(为什么我知道,因为我已经看过了!哈哈)。
4、注册机的探索
见3,那里已经分析了。
PS:我讨厌反汇编Delphi,特别讨厌它们以游戏方式写的CrackMe。
BY 笨笨D幸福