[反汇编练习] 160个CrackMe之007
[反汇编练习] 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不报毒,个人承诺绝对不会进行任何和木马病毒相关内容。http://images.cnitblog.com/blog/573547/201406/152219166701903.png
2、程序分析:想要破解一个程序,必须先了解这个程序。所以,在破解过程中,对最初程序的分析很重要,他可以帮助我们理解作者的目的和意图,特别是对于注册码的处理细节,从而方便我们反向跟踪和推导。和上一节一样,打开CHM,选择第7个aLoNg3x.2,保存下来。运行程序,程序界面如下:http://images.cnitblog.com/blog/573547/201406/152219184208704.png使用PEID查一下,Delphi 4.0 - 5.0。点一下About-Help,看完之后和上一个要求一样。不用想了,直接上IDR分析。
3、思路分析和破解流程IDR打开后如图:http://images.cnitblog.com/blog/573547/201406/152219266708624.png
我们先看一下窗口信息(F5):http://images.cnitblog.com/blog/573547/201406/152219285306926.png
我们发现,除了第一个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 ,ecx
00442F35 mov ebx,eax
00442F37 xor eax,eax
00442F39 push ebp
00442F3A push 443022
00442F3F push dword ptr fs:
00442F42 mov dword ptr fs:,esp
00442F45 lea edx,
00442F48 mov eax,dword ptr ; TPrincipale.Codice:TEdit
00442F4E call TControl.GetText
00442F53 mov eax,dword ptr
00442F56 lea edx,
00442F59 call @ValLong
00442F5E mov esi,eax
00442F60 cmp dword ptr ,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,
00442F73 mov eax,dword ptr ; TPrincipale.Codice:TEdit
00442F79 call TControl.GetText
00442F7E mov eax,dword ptr
00442F81 call 00442A8C
00442F86 mov ,eax; gvar_00445830
00442F8B mov edx,443090; '0'
00442F90 mov eax,dword ptr ; TPrincipale.Codice:TEdit
00442F96 call TControl.SetText
>00442F9B jmp 0044300C
00442F9D test esi,esi
>00442F9F jle 00442FFB
00442FA1 lea edx,
00442FA4 mov eax,dword ptr ; TPrincipale.Nome:TEdit
00442FAA call TControl.GetText
00442FAF mov ecx,dword ptr
00442FB2 mov edx,esi
00442FB4 mov eax,; 0x0 gvar_00445830
00442FB9 call 004429A8 ; // 关键CALL
00442FBE test al,al
>00442FC0 je 00442FF2 ; // 关键跳转
00442FC2 xor edx,edx ;// 将注册按钮隐藏,然后显示出来那个again按钮
00442FC4 mov eax,dword ptr ; TPrincipale.Registerz:TButton
00442FCA call TControl.SetVisible
00442FCF mov dl,1
00442FD1 mov eax,dword ptr ; TPrincipale.Again:TButton
00442FD7 call TControl.SetVisible
00442FDC xor edx,edx
00442FDE mov eax,dword ptr ; TPrincipale.Nome:TEdit
00442FE4 mov ecx,dword ptr
00442FE6 call dword ptr ; TControl.SetEnabled
00442FE9 xor eax,eax
00442FEB mov ,eax; gvar_00445830
>00442FF0 jmp 0044300C
00442FF2 xor eax,eax
00442FF4 mov ,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 ,eax; gvar_00445830
0044300C xor eax,eax
0044300E pop edx
0044300F pop ecx
00443010 pop ecx
00443011 mov dword ptr fs:,edx
00443014 push 443029
00443019 lea eax,
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:
004430D4 mov dword ptr fs:,esp
004430D7 lea edx,
004430DA mov eax,dword ptr ; TPrincipale.Codice:TEdit
004430E0 call TControl.GetText
004430E5 mov eax,dword ptr
004430E8 lea edx,
004430EB call @ValLong
004430F0 mov esi,eax
004430F2 cmp dword ptr ,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,
00443105 mov eax,dword ptr ; TPrincipale.Codice:TEdit
0044310B call TControl.GetText
00443110 mov eax,dword ptr
00443113 call 00442A8C
00443118 mov ,eax; gvar_00445830
0044311D mov edx,44329C; '0'
00443122 mov eax,dword ptr ; TPrincipale.Codice:TEdit
00443128 call TControl.SetText
>0044312D jmp 0044320F
00443132 test esi,esi
>00443134 jle 004431FE
0044313A lea edx,
0044313D mov eax,dword ptr ; TPrincipale.Nome:TEdit
00443143 call TControl.GetText
00443148 mov ecx,dword ptr
0044314B mov edx,esi
0044314D mov eax,; 0x0 gvar_00445830
00443152 call 004429A8
00443157 test al,al
>00443159 je 004431CE
0044315B xor edx,edx
0044315D mov eax,dword ptr ; TPrincipale.Again:TButton
00443163 call TControl.SetVisible
00443168 lea edx,
0044316B mov eax,; 0x0 gvar_0044582C:TPrincipale
00443170 call TControl.GetText
00443175 lea eax,
00443178 call UniqueString
0044317D mov byte ptr ,65
00443181 lea eax,
00443184 call UniqueString
00443189 mov byte ptr ,64
0044318D lea eax,
00443190 mov ecx,13
00443195 mov edx,0C
0044319A call @LStrDelete
0044319F lea edx,
004431A2 mov eax,dword ptr ; TPrincipale.Nome:TEdit
004431A8 call TControl.GetText
004431AD mov edx,dword ptr
004431B0 lea eax,
004431B3 call @LStrCat
004431B8 mov edx,dword ptr
004431BB mov eax,; 0x0 gvar_0044582C:TPrincipale
004431C0 call TControl.SetText
004431C5 xor eax,eax
004431C7 mov ,eax; gvar_00445830
>004431CC jmp 0044320F
004431CE xor eax,eax
004431D0 mov ,eax; gvar_00445830
004431D5 xor edx,edx
004431D7 mov eax,dword ptr ; TPrincipale.Again:TButton
004431DD call TControl.SetVisible
004431E2 mov dl,1
004431E4 mov eax,dword ptr ; TPrincipale.Registerz:TButton
004431EA call TControl.SetVisible
004431EF mov dl,1
004431F1 mov eax,dword ptr ; TPrincipale.Nome:TEdit
004431F7 mov ecx,dword ptr
004431F9 call dword ptr ; TControl.SetEnabled
>004431FC jmp 0044320F
004431FE mov eax,4432A8; 'Please... The Code Must be > 0'
00443203 call ShowMessage
00443208 xor eax,eax
0044320A mov ,eax; gvar_00445830
0044320F xor eax,eax
00443211 pop edx
00443212 pop ecx
00443213 pop ecx
00443214 mov dword ptr fs:,edx
00443217 push 443234
0044321C lea eax,
0044321F call @LStrClr
00443224 lea eax,
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 ,ecx
004429B4 mov dword ptr ,edx
004429B7 mov edi,eax
004429B9 mov eax,dword ptr
004429BC call @LStrAddRef
004429C1 xor eax,eax
004429C3 push ebp
004429C4 push 442A7A
004429C9 push dword ptr fs:
004429CC mov dword ptr fs:,esp
004429CF mov eax,dword ptr
004429D2 call @LStrLen
004429D7 cmp eax,4
>004429DA jle 00442A62
004429E0 xor ebx,ebx
004429E2 mov eax,dword ptr
004429E5 call @LStrLen
004429EA test eax,eax
>004429EC jle 00442A26
004429EE mov dword ptr ,eax
004429F1 mov esi,1
004429F6 mov eax,dword ptr
004429F9 call @LStrLen
004429FE cmp eax,1
>00442A01 jl 00442A20
00442A03 mov edx,dword ptr
00442A06 movzx edx,byte ptr
00442A0B mov ecx,dword ptr
00442A0E movzx ecx,byte ptr
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
<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
00442A3A mov ecx,59
00442A3F cdq
00442A40 idiv eax,ecx
00442A42 mov ecx,eax
00442A44 mov eax,dword ptr
00442A47 mov esi,50
00442A4C cdq
00442A4D idiv eax,esi
00442A4F add ecx,edx
00442A51 inc ecx
00442A52 mov dword ptr ,ecx
00442A55 cmp ebx,dword ptr
>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:,edx
00442A6C push 442A81
00442A71 lea eax,
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 ,ecx
004429B4|.8955 FC mov ,edx ;// edx=123321转整数
004429B7|.8BF8 mov edi,eax ;// edi=eax=0
004429B9|.8B45 F8 mov eax,
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:
004429CC|.64:8920 mov dword ptr fs:,esp
004429CF|.8B45 F8 mov eax, ;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,
004429E5|.E8 4A10FCFF call 00403A34 ;@LStrLen
004429EA|.85C0 test eax,eax
004429EC|.7E 38 jle short 00442A26
004429EE|.8945 F4 mov ,eax
004429F1|.BE 01000000 mov esi,0x1
004429F6|>8B45 F8 /mov eax,
004429F9|.E8 3610FCFF |call 00403A34 ;@LStrLen
004429FE|.83F8 01 |cmp eax,0x1
00442A01|.7C 1D |jl short 00442A20
00442A03|>8B55 F8 |/mov edx,
00442A06|.0FB65432 FF ||movzx edx,byte ptr ds: ;// 从第一个字符开始
00442A0B|.8B4D F8 ||mov ecx,
00442A0E|.0FB64C01 FF ||movzx ecx,byte ptr ds: ;// 最后一个字符
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 ;// 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, ;// 序列号转整数,从这里开始才是有意义的计算
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,
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 ,ecx
00442A55|.3B5D FC cmp ebx,
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:,edx
00442A6C|.68 812A4400 push 00442A81
00442A71|>8D45 F8 lea eax,
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幸福
本帖最后由 Pnmker 于 2015-5-25 04:33 编辑
给你一组数试一下 pnmker!!!!!'49479283
首先Nome输入pnmker,codice输入!!!!!', 点击Register.. , 关闭弹出的对话框;(1)
然后codice输入49479283,点击Register.. ,出现again按钮,下面如法炮制 。 (2)
第(1)步,程序计算了一个数值放在了ds:,如果输入的是!!!!!',则ds:=3333 (十进制),计算函数如下
int dummy(const char * p)
{
int esi = 0x37B;
int len = strlen(p);
for(int i = 1; i < len; ++i)
{
esi += (p % 17 + 1) * p;
}
return esi;
}
第(2)步,有两个计算,一个是用户名Nome的计算 :大概就是 (忽略了一个cdq指令可能出现的符号扩展,如果nome长度控制好了一般不会影响)
int nome(const char * user)
{
int len = strlen(user);
int sum = 0;
for(int i = 0; i < len; ++i)sum += user;
sum = sum * sum;
return (sum * ds:) % 0xA2C2A;
}
另一个计算就是注册码codice的计算,正如楼主所讲用到了 /0x59 和取余 0x50,然后再加1
判断的验证通过的依据就是两个计算的结果一样,我把第一个的计算结果记为nome, 设需要输入的正确的注册码为code,那么就是要求解方程(以下用十进制)
code / 89 + code % 80+ 1 = nome
进一步设code % 80 = rhs
那么 code / 89 + rhs + 1 = nome
则code =89* (nome - 1 -rhs )
由于code % 80 = rhs,所以
89*(nome-1-rhs) ≡ rhs (mod 80)// 这个式子是数论中的同余式, 其实就是(89*(nome-1-rhs))% 80 = code % 80 = rhs,下同
化简得
9*(nome-1) - 9*rhs ≡ rhs(mod 80), 而9*rhs ≡ 9*rhs (mod 8),//两个同余式相加得
9*(nome-1) ≡ 10*rhs (mod 8), 那么下面的式子必然满足
9*(nome- 1) = 10*rhs + 80*q, q为正整数
比较上式可以得出nome-1必须能被10整除!!
就是这个 (sum * ds:) % 0xA2C2A - 1能被10整除, 所以在用户名固定不变的情况下sum是个固定值,那么只能去调整ds:的值了,所以在第(1)步才会有!!!!!' 这么奇怪的输入。
注册机我已经写好,只要输入一个用户名,我就能就算出第(1)步和第(2)步所需要输入的codice.
有需要的私聊!
收藏,没人回复? qq516145704 发表于 2014-6-15 23:23
收藏,没人回复?
感谢收藏!握爪!{:1_918:} 本帖最后由 shuguang 于 2014-6-30 21:17 编辑
我也找不到这个值。上百度知道提问了希望有结果 注册码是-100
感谢楼主发布教程让新手学习 收藏备用,有时间练练手{:301_992:} 先跟着看看,慢慢来 这个解决了,该第八个了 你可以尝试用Cancella按钮来破解