标签:style class blog code http ext
[反汇编练习] 160个CrackMe之007.
本系列文章的目的是从一个没有任何经验的新手的角度(其实就是我自己),一步步尝试将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,选择第7个aLoNg3x.2,保存下来。运行程序,程序界面如下:
使用PEID查一下,Delphi 4.0 - 5.0。点一下About-Help,看完之后和上一个要求一样。不用想了,直接上IDR分析。
3、思路分析和破解流程
IDR打开后如图:
我们先看一下窗口信息(F5):
我们发现,除了第一个register按钮之后,还有一个again按钮。看来这个程序意思很明确点击完Reg之后还需要通过again按钮的验证。我们在树列表中查看下按钮对应事件的关系。
首先,分析Register的事件:
双击IDR最下面的列表中的RegisterClick.event,右上角会显示对应的反汇编。
根据上一个程序的经验,大概地分析一下:
Delphi字符串函数反汇编参考:http://www.cnblogs.com/bbdxf/p/3787684.html
_CrackMe200::TPrincipale.RegisterzClick 00442F28 push ebp 00442F29 mov ebp,esp 00442F2B add esp,0FFFFFFF8 00442F2E push ebx 00442F2F push esi 00442F30 xor ecx,ecx 00442F32 mov dword ptr [ebp-8],ecx 00442F35 mov ebx,eax 00442F37 xor eax,eax 00442F39 push ebp 00442F3A push 443022 00442F3F push dword ptr fs:[eax] 00442F42 mov dword ptr fs:[eax],esp 00442F45 lea edx,[ebp-8] 00442F48 mov eax,dword ptr [ebx+2DC]; TPrincipale.Codice:TEdit 00442F4E call TControl.GetText 00442F53 mov eax,dword ptr [ebp-8] 00442F56 lea edx,[ebp-4] 00442F59 call @ValLong 00442F5E mov esi,eax 00442F60 cmp dword ptr [ebp-4],0 >00442F64 je 00442F9D 00442F66 mov eax,443038; ‘You MUST insert a valid Long Integer Value in the Code Editor... Thank you :)‘ 00442F6B call ShowMessage 00442F70 lea edx,[ebp-8] 00442F73 mov eax,dword ptr [ebx+2DC]; TPrincipale.Codice:TEdit 00442F79 call TControl.GetText 00442F7E mov eax,dword ptr [ebp-8] 00442F81 call 00442A8C 00442F86 mov [00445830],eax; gvar_00445830 00442F8B mov edx,443090; ‘0‘ 00442F90 mov eax,dword ptr [ebx+2DC]; TPrincipale.Codice:TEdit 00442F96 call TControl.SetText >00442F9B jmp 0044300C 00442F9D test esi,esi >00442F9F jle 00442FFB 00442FA1 lea edx,[ebp-8] 00442FA4 mov eax,dword ptr [ebx+2D8]; TPrincipale.Nome:TEdit 00442FAA call TControl.GetText 00442FAF mov ecx,dword ptr [ebp-8] 00442FB2 mov edx,esi 00442FB4 mov eax,[00445830]; 0x0 gvar_00445830 00442FB9 call 004429A8 ; // 关键CALL 00442FBE test al,al >00442FC0 je 00442FF2 ; // 关键跳转 00442FC2 xor edx,edx ;// 将注册按钮隐藏,然后显示出来那个again按钮 00442FC4 mov eax,dword ptr [ebx+2CC]; TPrincipale.Registerz:TButton 00442FCA call TControl.SetVisible 00442FCF mov dl,1 00442FD1 mov eax,dword ptr [ebx+2E8]; TPrincipale.Again:TButton 00442FD7 call TControl.SetVisible 00442FDC xor edx,edx 00442FDE mov eax,dword ptr [ebx+2D8]; TPrincipale.Nome:TEdit 00442FE4 mov ecx,dword ptr [eax] 00442FE6 call dword ptr [ecx+60]; TControl.SetEnabled 00442FE9 xor eax,eax 00442FEB mov [00445830],eax; gvar_00445830 >00442FF0 jmp 0044300C 00442FF2 xor eax,eax 00442FF4 mov [00445830],eax; gvar_00445830 >00442FF9 jmp 0044300C 00442FFB mov eax,44309C; ‘Please... The Code Must be > 0‘ 00443000 call ShowMessage 00443005 xor eax,eax 00443007 mov [00445830],eax; gvar_00445830 0044300C xor eax,eax 0044300E pop edx 0044300F pop ecx 00443010 pop ecx 00443011 mov dword ptr fs:[eax],edx 00443014 push 443029 00443019 lea eax,[ebp-8] 0044301C call @LStrClr 00443021 ret <00443022 jmp @HandleFinally <00443027 jmp 00443019 00443029 pop esi 0044302A pop ebx 0044302B pop ecx 0044302C pop ecx 0044302D pop ebp 0044302E ret
根据函数的名称,我们很容易分析出了关键Call 004429A8, 在CALL下面的JE就是爆破的关键跳转。
将exe程序拖到OD中打开,根据IDR中的地址,转到(Ctrl+G)开头地址:00442F28。在程序中输入伪码,参照IDR中的汇编,修改关键的跳转JE 00442FF2,选中JE->右键->Binary->Fill with NOPs。在程序中随意输入,点击Register按钮,发现注册按钮不见了,again按钮出来了。
我们继续在IDR中找到againClick.event,双击进去,继续分析一下:
_CrackMe200::TPrincipale.AgainClick 004430BC push ebp 004430BD mov ebp,esp 004430BF push 0 004430C1 push 0 004430C3 push 0 004430C5 push ebx 004430C6 push esi 004430C7 mov ebx,eax 004430C9 xor eax,eax 004430CB push ebp 004430CC push 44322D 004430D1 push dword ptr fs:[eax] 004430D4 mov dword ptr fs:[eax],esp 004430D7 lea edx,[ebp-0C] 004430DA mov eax,dword ptr [ebx+2DC]; TPrincipale.Codice:TEdit 004430E0 call TControl.GetText 004430E5 mov eax,dword ptr [ebp-0C] 004430E8 lea edx,[ebp-4] 004430EB call @ValLong 004430F0 mov esi,eax 004430F2 cmp dword ptr [ebp-4],0 >004430F6 je 00443132 004430F8 mov eax,443244; ‘You MUST insert a valid Long Integer Value in the Code Editor... Thank you :)‘ 004430FD call ShowMessage 00443102 lea edx,[ebp-0C] 00443105 mov eax,dword ptr [ebx+2DC]; TPrincipale.Codice:TEdit 0044310B call TControl.GetText 00443110 mov eax,dword ptr [ebp-0C] 00443113 call 00442A8C 00443118 mov [00445830],eax; gvar_00445830 0044311D mov edx,44329C; ‘0‘ 00443122 mov eax,dword ptr [ebx+2DC]; TPrincipale.Codice:TEdit 00443128 call TControl.SetText >0044312D jmp 0044320F 00443132 test esi,esi >00443134 jle 004431FE 0044313A lea edx,[ebp-0C] 0044313D mov eax,dword ptr [ebx+2D8]; TPrincipale.Nome:TEdit 00443143 call TControl.GetText 00443148 mov ecx,dword ptr [ebp-0C] 0044314B mov edx,esi 0044314D mov eax,[00445830]; 0x0 gvar_00445830 00443152 call 004429A8 00443157 test al,al >00443159 je 004431CE 0044315B xor edx,edx 0044315D mov eax,dword ptr [ebx+2E8]; TPrincipale.Again:TButton 00443163 call TControl.SetVisible 00443168 lea edx,[ebp-8] 0044316B mov eax,[0044582C]; 0x0 gvar_0044582C:TPrincipale 00443170 call TControl.GetText 00443175 lea eax,[ebp-8] 00443178 call UniqueString 0044317D mov byte ptr [eax+5],65 00443181 lea eax,[ebp-8] 00443184 call UniqueString 00443189 mov byte ptr [eax+6],64 0044318D lea eax,[ebp-8] 00443190 mov ecx,13 00443195 mov edx,0C 0044319A call @LStrDelete 0044319F lea edx,[ebp-0C] 004431A2 mov eax,dword ptr [ebx+2D8]; TPrincipale.Nome:TEdit 004431A8 call TControl.GetText 004431AD mov edx,dword ptr [ebp-0C] 004431B0 lea eax,[ebp-8] 004431B3 call @LStrCat 004431B8 mov edx,dword ptr [ebp-8] 004431BB mov eax,[0044582C]; 0x0 gvar_0044582C:TPrincipale 004431C0 call TControl.SetText 004431C5 xor eax,eax 004431C7 mov [00445830],eax; gvar_00445830 >004431CC jmp 0044320F 004431CE xor eax,eax 004431D0 mov [00445830],eax; gvar_00445830 004431D5 xor edx,edx 004431D7 mov eax,dword ptr [ebx+2E8]; TPrincipale.Again:TButton 004431DD call TControl.SetVisible 004431E2 mov dl,1 004431E4 mov eax,dword ptr [ebx+2CC]; TPrincipale.Registerz:TButton 004431EA call TControl.SetVisible 004431EF mov dl,1 004431F1 mov eax,dword ptr [ebx+2D8]; TPrincipale.Nome:TEdit 004431F7 mov ecx,dword ptr [eax] 004431F9 call dword ptr [ecx+60]; TControl.SetEnabled >004431FC jmp 0044320F 004431FE mov eax,4432A8; ‘Please... The Code Must be > 0‘ 00443203 call ShowMessage 00443208 xor eax,eax 0044320A mov [00445830],eax; gvar_00445830 0044320F xor eax,eax 00443211 pop edx 00443212 pop ecx 00443213 pop ecx 00443214 mov dword ptr fs:[eax],edx 00443217 push 443234 0044321C lea eax,[ebp-0C] 0044321F call @LStrClr 00443224 lea eax,[ebp-8] 00443227 call @LStrClr 0044322C ret <0044322D jmp @HandleFinally <00443232 jmp 0044321C 00443234 pop esi 00443235 pop ebx 00443236 mov esp,ebp 00443238 pop ebp 00443239 ret
大概地一看,是不是发现他们的逻辑基本和Register的差不多?关键Call也一样。没关系,我们继续在JE 004431CE右键,Binary->Fill with NOPs。这时,回到程序,继续点击Again,是不是两个按钮都被隐藏了?!!哈哈哈!
(多试几次,会发现第一个编辑框还是有字数限制的,需要大于4个)
小结:两个按钮调用了相同的关键CALL,即使用了同一个算法函数对注册码进行验证,这要爆破这一个函数或它的跳转就行。
4、注册机尝试
由于两个按钮的关键Call一样,所以这个算法就很省事了,直接在 OD中跳转到CALL 004429A8的地方,F8单步分析。
慢着,是不是很多CALL还不知道什么意思?是啊,先用IDR看一下嘛!
在IDR中也是用Ctrl+G,输入地址 004429A8,确定:
_CrackMe200::sub_004429A8 004429A8 push ebp 004429A9 mov ebp,esp 004429AB add esp,0FFFFFFF4 004429AE push ebx 004429AF push esi 004429B0 push edi 004429B1 mov dword ptr [ebp-8],ecx 004429B4 mov dword ptr [ebp-4],edx 004429B7 mov edi,eax 004429B9 mov eax,dword ptr [ebp-8] 004429BC call @LStrAddRef 004429C1 xor eax,eax 004429C3 push ebp 004429C4 push 442A7A 004429C9 push dword ptr fs:[eax] 004429CC mov dword ptr fs:[eax],esp 004429CF mov eax,dword ptr [ebp-8] 004429D2 call @LStrLen 004429D7 cmp eax,4 >004429DA jle 00442A62 004429E0 xor ebx,ebx 004429E2 mov eax,dword ptr [ebp-8] 004429E5 call @LStrLen 004429EA test eax,eax >004429EC jle 00442A26 004429EE mov dword ptr [ebp-0C],eax 004429F1 mov esi,1 004429F6 mov eax,dword ptr [ebp-8] 004429F9 call @LStrLen 004429FE cmp eax,1 >00442A01 jl 00442A20 00442A03 mov edx,dword ptr [ebp-8] 00442A06 movzx edx,byte ptr [edx+esi-1] 00442A0B mov ecx,dword ptr [ebp-8] 00442A0E movzx ecx,byte ptr [ecx+eax-1] 00442A13 imul edx,ecx 00442A16 imul edx,edi 00442A19 add ebx,edx 00442A1B dec eax 00442A1C test eax,eax <00442A1E jne 00442A03 00442A20 inc esi 00442A21 dec dword ptr [ebp-0C] <00442A24 jne 004429F6 00442A26 mov eax,ebx 00442A28 cdq 00442A29 xor eax,edx 00442A2B sub eax,edx 00442A2D mov ecx,0A2C2A 00442A32 cdq 00442A33 idiv eax,ecx 00442A35 mov ebx,edx 00442A37 mov eax,dword ptr [ebp-4] 00442A3A mov ecx,59 00442A3F cdq 00442A40 idiv eax,ecx 00442A42 mov ecx,eax 00442A44 mov eax,dword ptr [ebp-4] 00442A47 mov esi,50 00442A4C cdq 00442A4D idiv eax,esi 00442A4F add ecx,edx 00442A51 inc ecx 00442A52 mov dword ptr [ebp-4],ecx 00442A55 cmp ebx,dword ptr [ebp-4] >00442A58 jne 00442A5E 00442A5A mov bl,1 >00442A5C jmp 00442A64 00442A5E xor ebx,ebx >00442A60 jmp 00442A64 00442A62 xor ebx,ebx 00442A64 xor eax,eax 00442A66 pop edx 00442A67 pop ecx 00442A68 pop ecx 00442A69 mov dword ptr fs:[eax],edx 00442A6C push 442A81 00442A71 lea eax,[ebp-8] 00442A74 call @LStrClr 00442A79 ret <00442A7A jmp @HandleFinally <00442A7F jmp 00442A71 00442A81 mov eax,ebx 00442A83 pop edi 00442A84 pop esi 00442A85 pop ebx 00442A86 mov esp,ebp 00442A88 pop ebp 00442A89 ret
然后在OD中F8单步调试详细分析:
004429A8 /$ 55 push ebp ; // reg和again 关键Call 004429A9 |. 8BEC mov ebp,esp 004429AB |. 83C4 F4 add esp,-0xC 004429AE |. 53 push ebx 004429AF |. 56 push esi 004429B0 |. 57 push edi 004429B1 |. 894D F8 mov [local.2],ecx 004429B4 |. 8955 FC mov [local.1],edx ; // edx=123321转整数 004429B7 |. 8BF8 mov edi,eax ; // edi=eax=0 004429B9 |. 8B45 F8 mov eax,[local.2] 004429BC |. E8 2712FCFF call 00403BE8 ; LStrAddRef 004429C1 |. 33C0 xor eax,eax 004429C3 |. 55 push ebp 004429C4 |. 68 7A2A4400 push 00442A7A 004429C9 |. 64:FF30 push dword ptr fs:[eax] 004429CC |. 64:8920 mov dword ptr fs:[eax],esp 004429CF |. 8B45 F8 mov eax,[local.2] ; bbdxf 004429D2 |. E8 5D10FCFF call 00403A34 ; @LStrLen 004429D7 |. 83F8 04 cmp eax,0x4 004429DA |. 0F8E 82000000 jle 00442A62 ; 必须 > 4 004429E0 |. 33DB xor ebx,ebx ; // ebx=0 004429E2 |. 8B45 F8 mov eax,[local.2] 004429E5 |. E8 4A10FCFF call 00403A34 ; @LStrLen 004429EA |. 85C0 test eax,eax 004429EC |. 7E 38 jle short 00442A26 004429EE |. 8945 F4 mov [local.3],eax 004429F1 |. BE 01000000 mov esi,0x1 004429F6 |> 8B45 F8 /mov eax,[local.2] 004429F9 |. E8 3610FCFF |call 00403A34 ; @LStrLen 004429FE |. 83F8 01 |cmp eax,0x1 00442A01 |. 7C 1D |jl short 00442A20 00442A03 |> 8B55 F8 |/mov edx,[local.2] 00442A06 |. 0FB65432 FF ||movzx edx,byte ptr ds:[edx+esi-0x1] ; // 从第一个字符开始 00442A0B |. 8B4D F8 ||mov ecx,[local.2] 00442A0E |. 0FB64C01 FF ||movzx ecx,byte ptr ds:[ecx+eax-0x1] ; // 最后一个字符 00442A13 |. 0FAFD1 ||imul edx,ecx ; // 第一个字符乘上最后一个-- 00442A16 |. 0FAFD7 ||imul edx,edi ; // edi=0, 所以结果一直为0 00442A19 |. 03DA ||add ebx,edx ; // ebx=0, 所以结果一直为0 00442A1B |. 48 ||dec eax ; // len-- 00442A1C |. 85C0 ||test eax,eax 00442A1E |.^ 75 E3 |\jnz short 00442A03 00442A20 |> 46 |inc esi 00442A21 |. FF4D F4 |dec [local.3] ; // len-- 00442A24 |.^ 75 D0 \jnz short 004429F6 ; // 整个循环结束,edx=0,ebx=0 00442A26 |> 8BC3 mov eax,ebx 00442A28 |. 99 cdq 00442A29 |. 33C2 xor eax,edx ; // eax=0,edx=0 00442A2B |. 2BC2 sub eax,edx 00442A2D |. B9 2A2C0A00 mov ecx,0xA2C2A 00442A32 |. 99 cdq 00442A33 |. F7F9 idiv ecx ; // eax/ecx 00442A35 |. 8BDA mov ebx,edx ; // 这里以上的计算结果都一定是0 00442A37 |. 8B45 FC mov eax,[local.1] ; // 序列号转整数,从这里开始才是有意义的计算 00442A3A |. B9 59000000 mov ecx,0x59 00442A3F |. 99 cdq ; // edx=0 00442A40 |. F7F9 idiv ecx ; // eax=eax/0x59 00442A42 |. 8BC8 mov ecx,eax ; // 存储结果到ecx 00442A44 |. 8B45 FC mov eax,[local.1] 00442A47 |. BE 50000000 mov esi,0x50 00442A4C |. 99 cdq 00442A4D |. F7FE idiv esi ; // eax=eax/0x50, edx=eax % 0x50 00442A4F |. 03CA add ecx,edx ; // ecx=ecx+edx 00442A51 |. 41 inc ecx ; // ecx++ 00442A52 |. 894D FC mov [local.1],ecx 00442A55 |. 3B5D FC cmp ebx,[local.1] 00442A58 |. 75 04 jnz short 00442A5E ; // ebx等于返回值,应该为1 00442A5A |. B3 01 mov bl,0x1 00442A5C |. EB 06 jmp short 00442A64 00442A5E |> 33DB xor ebx,ebx 00442A60 |. EB 02 jmp short 00442A64 00442A62 |> 33DB xor ebx,ebx 00442A64 |> 33C0 xor eax,eax ; // 都调到这里 00442A66 |. 5A pop edx 00442A67 |. 59 pop ecx 00442A68 |. 59 pop ecx 00442A69 |. 64:8910 mov dword ptr fs:[eax],edx 00442A6C |. 68 812A4400 push 00442A81 00442A71 |> 8D45 F8 lea eax,[local.2] 00442A74 |. E8 3F0DFCFF call 004037B8 ; @LStrClr 00442A79 \. C3 retn 00442A7A .^ E9 F907FCFF jmp 00403278 00442A7F .^ EB F0 jmp short 00442A71 00442A81 . 8BC3 mov eax,ebx ; // 返回到这里 00442A83 . 5F pop edi 00442A84 . 5E pop esi 00442A85 . 5B pop ebx 00442A86 . 8BE5 mov esp,ebp 00442A88 . 5D pop ebp 00442A89 . C3 retn
请详细看上面的汇编代码的注释:在地址00442A35以上部分的代码最终的结果一直为0,这个地址下面的代码才真正的有实际意义。
其中,有意义这一块的C/CPP代码大概如下:
int nCode=123321; // 用户输入的注册码,必须是4位以上,转换为整数 int n1 = nCode/0x59; int n2 = nCode%0x50; if( n1+n2+1 == 0 ) { // 第一次隐藏Register按钮,显示again按钮,第二次隐藏again按钮 }else{ //显示register按钮,隐藏again按钮 }
为什么我没有写出一个注册机呢?
算法原理大概是一个数除以0x59的结果加上它对于0x50取模,然后结果加上1等于0。不加1还好理解,加上1就顿时感觉蛋疼了,我实在无法找到这样一个数。(当然也不排除我理解错了,求大牛指导!!)
007结束。
-----------
PS: 有人说,帖子内容很难看懂,问我能否做成视频?
我想说:
首先,我的贴子都是按照我分析的流程写的,相关的工具、参考资料、代码分析、关键算法分析流程 全部都已经很详细地写下来了。只要你拿着OD和有限的几个工具,除非你真的连F7、F8分析代码都不会,否则对照我的文章肯定能看到一些有价值的东西,至少我是这么认为的。
其次,【无论多么简单的事情,想要做好,都是不容易的!】经常混论坛的都知道,爆破虽易,算法不易!确实自己水平有限,无法做到举重若轻,几分钟分析出一个算法。这种水平我真达不到!此系列文章的重头戏不在于爆破,而在于算法部分。每天破解的80%以上的时间都是在分析算法的部分,还有10%是用来整理排版帖子,虽然做的不是很好,但我问心无愧地做到了自己的承诺,尽自己所能。帖子尚且不易,视频何其远乎!我只能说声抱歉了!
最后,谢谢大家的捧场!
BY 笨笨D幸福
[反汇编练习] 160个CrackMe之007,布布扣,bubuko.com
标签:style class blog code http ext
原文地址:http://www.cnblogs.com/bbdxf/p/3790039.html