[反汇编练习] 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调试,记录重要数据: [Asm] 纯文本查看 复制代码 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跟进去: [Asm] 纯文本查看 复制代码 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
这一块的算法类似于: [C++] 纯文本查看 复制代码 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按钮的事件处理: [Asm] 纯文本查看 复制代码 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的分析: [Asm] 纯文本查看 复制代码 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
这一块的算法类似于: [C++] 纯文本查看 复制代码 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幸福
|