【文章标题】: newsearch忽悠 CRACKME2的分析
【作 者】: laoqian[FCG]
【邮 箱】: -
【主 页】:
www.fcgchina.com
【QQ 号】: -
【软件名称】: newsearch忽悠 CRACKME2
【大 小】: 44k
【下载地址】:
http://bbs.pediy.com/attachment.php?s=&attachmentid=3286
【加壳方式】: 无
【保护方式】: 算法
【编写语言】: VC,MFC42
【操作平台】: winxp
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
第一次在这个板块发文,呵呵!
【详细过程】
最近newsearch一直在研究ecc,研究之余不忘了忽悠,忽悠了几个crackme,说是为了学习ecc而作,我们就来看看,首先说明我不懂ecc,而这个crackme只是有些类似想法而已。
闲话少说,od打开crackme,我们搜索字符参考,只有几个看到"%C%c %c%c%c! ",我们过去,然后在望上面看到,下断点。
疑问,为何在这里下断点,各位看官说为何在这里下呢?――因为我跟踪过他的crackme1,其中有出错提示,所以直接找到这里!
这次就偷懒了,不过如果第一次跟踪,怎么设断点,不是本文的重点,就略过了,见谅!
下面是分析:
004014A0 push -1
004014A2 push CrackMe4.00401B80 ; SE handler installation
004014A7 mov eax,dword ptr fs:[0]
004014AD push eax
004014AE mov dword ptr fs:[0],esp
004014B5 sub esp,6C
004014B8 push ebx
004014B9 push ebp
004014BA push esi
004014BB push edi
004014BC mov ebp,ecx
004014BE push 1
004014C0 call <jmp.&MFC42.#6334>
004014C5 lea ecx,dword ptr ss:[esp+10]
004014C9 call <jmp.&MFC42.#540>
004014CE mov esi,dword ptr ss:[ebp+60] ; Esi = 注册码
004014D1 mov edi,dword ptr ss:[ebp+68] ; Edi = 序列号
004014D4 xor ebx,ebx ; ebx 清零
004014D6 lea ecx,dword ptr ss:[esp+1C]
004014DA push ebx
004014DB mov dword ptr ss:[esp+88],ebx ; ebx 零
004014E2 call CrackMe4.00401630
004014E7 cmp esi,edi ; 判断 注册码 序列号 是否相等
004014E9 mov byte ptr ss:[esp+84],1 ; 置 1
004014F1 je CrackMe4.004015B2 ; 注册码 序列号相等就玩完!
004014F7 mov eax,dword ptr ss:[ebp+64] ; eax=用户名
004014FA cmp eax,edi ; 判断 用户名 序列号 是否相等
004014FC mov dword ptr ss:[esp+18],eax ; eax=用户名
00401500 je CrackMe4.004015B2 ; 相等就玩完!
00401506 cmp esi,eax ; 判断 用户名 注册码 是否相等
00401508 je CrackMe4.004015B2 ; 相等就玩完!
0040150E cmp esi,64 ; 判断 注册码 是否小于100
00401511 jl CrackMe4.004015B2 ; 小于就玩完!
00401517 cmp edi,64
0040151A jl CrackMe4.004015B2
00401520 cmp eax,64
00401523 jl CrackMe4.004015B2 ; 同理
00401529 lea eax,dword ptr ds:[esi+esi*4>; eax=esi*5,[esi=注册码],传给eax
0040152C lea eax,dword ptr ds:[eax+eax*4>; eax=eax*5,传给eax,注意这里和上面!!!重点
0040152F sub edi,eax ; edi=edi-eax=序列号-eax
00401531 lea eax,dword ptr ds:[edi+edi*4>; eax=edi*5,传给eax,注意这里和上面!
00401534 shl eax,1 ; 逻辑左移1位,eax=eax*2
00401536 cmp eax,10 ; 比较eax不能小于10H
00401539 mov dword ptr ss:[esp+14],eax ; eax复制
0040153D jl short CrackMe4.004015B2 ; 小于就玩完!
0040153F fld qword ptr ds:[402510] ; 装入ST0 实数2,用上浮点了,嘿嘿,这里就是指数算法的关键
00401545 fild dword ptr ss:[esp+14] ; eax复制的值,装入ST0 整数
00401549 call <jmp.&MSVCRT._CIpow> ; pow(A,B),A的B次方函数
0040154E fisub dword ptr ss:[esp+18] ; 浮点减法,上面的结果减去用户名,存入ST0,看FPU寄存器
00401552 call <jmp.&MSVCRT._ftol> ; 取整到eax
00401557 mov ecx,eax ; ecx=eax
00401559 cdq ; 清零edx,准备除法
0040155A idiv esi ; 整数除法,eax/esi,商存eax,余数存edx
0040155C sub ecx,esi ; Esi=注册码,验证正确应该相等,减法结果ecx=0,重点!有些迷惑后面的小手段!
0040155E cmp eax,1 ; 正确验证,eax应等于1
00401561 jnz short CrackMe4.0040159F ; 不等跳就玩完!
00401563 lea edx,dword ptr ds:[ecx+64] ; 用上了前面的ecx的值!ecx=0,则edx=64h,d
00401566 lea eax,dword ptr ds:[ecx+6F] ; ecx=0,则eax=6Fh,o
00401569 push edx ; 入栈
0040156A push eax ; 入栈
0040156B lea edx,dword ptr ds:[ecx+47] ; ecx=0,则edx=47h,G
0040156E lea eax,dword ptr ds:[ecx+79] ; ecx=0,则eax=79h, y
00401571 push edx ; 入栈
00401572 add ecx,4D ; ecx=0,则ecx=4Dh,M
00401575 push eax ; 入栈
00401576 push ecx ; 入栈
00401577 lea ecx,dword ptr ss:[esp+24] ; 上面根据入栈顺序,我们出栈顺序得到ACSII码,看到“MyGod”呵呵
0040157B push CrackMe4.00403020 ; ASCII "%C%c %c%c%c! "
00401580 push ecx ; 上面就是我们下断点的依据!
00401581 call <jmp.&MFC42.#2818> ; 这里就是动态生成成功提示的地方!
00401586 mov edx,dword ptr ss:[esp+2C]
0040158A add esp,1C
0040158D push ebx
0040158E push ebx
0040158F push edx ; 看到edx,就是“My God!”
00401590 call <jmp.&MFC42.#1200> ; 成功窗口画面
00401595 push ebx
00401596 mov ecx,ebp
00401598 call <jmp.&MFC42.#6334>
0040159D jmp short CrackMe4.004015BB
0040159F lea ecx,dword ptr ss:[esp+1C]
004015A3 call <jmp.&MFC42.#2514> ; 你被忽悠了!!――一张脸!
004015A8 push ebx
004015A9 mov ecx,ebp
004015AB call <jmp.&MFC42.#6334>
004015B0 jmp short CrackMe4.004015BB
004015B2 lea ecx,dword ptr ss:[esp+1C]
004015B6 call <jmp.&MFC42.#2514>
004015BB lea ecx,dword ptr ss:[esp+1C]
可以逆推做注册机了,呵呵我们假设:
Name,用户名
x,序列号
y,注册码
我们看
0040153F fld qword ptr ds:[402510] ; 装入ST0 实数2,用上浮点了,嘿嘿,这里就是指数算法的关键
00401545 fild dword ptr ss:[esp+14] ; eax复制的值,装入ST0 整数
00401549 call <jmp.&MSVCRT._CIpow> ; pow(A,B),是的B次方函数,此处FPU看到A=2
此处:2^B是一个关键,那么B是对少,往上看,
00401529 -00401536中间计算了,我们总结得到B=10(x-25y)
接下来看到对应关系:
{2^[10(x-25y)]-Name}/y=1
算法看清了,那么怎么逆向他呢,这个需要数学基础,我没有,只好自己瞎搞了!
我设定:
Name,用户名
x,序列号
y,注册码
A,我们假设是整数变量,此处为=2,3,4....整数!
把{2^[10(x-25y)]-Name}/y=1简单转化为:
2^[10(x-25y)]=Name+y
我设A=X-25Y,带入上式得
2^[A*10]=Name+y
再来:
Y=2^[A*10]-Name
X=25Y+A
这就是简化后的算法了,A假设是整数变量,此处为=2,3,4....整数!
程序中增加了一些检测用户名、序列号、注册码的判断,无非是避免一些极端情况的出现!
X,Y,Name都是整数必须大于100,而确定A值,而且仅仅是要使Y大于100!可以看出一个用户名,如果不限定A可有无数解!
本来到此为止了,可是在检验注册机时候,发现,用户名12345678的序列号、注册码的不能注册,奇怪!
经过再次跟踪发现:
因为eax等只能存储8位(16进制),lea命令就是传递地址(而不是地址存储数据,相当于复制),如果超出8位,高位就另存(或者说丢弃?),如当[eax*5]大于2^32时候,就去掉高位,只留下后面8位(16进制)参与计算,就是只保留余数!然后再*5,同理舍去高位。就是那个25Y,要在编程时候处理一下,5Y处理高位,再5y处理高位!!!后面也是同理,这个用汇编容易实现,高级语言就麻烦一点。
这样A=3,4,5...也即能计算出序列号,4.5.6....以此类推!
X=25Y+A只是一种特殊情况,因此编写注册机时候,要根据上面作修改
X={5*{5*Y}}+A,其中{5*y}代表以上分析的结果,还是用汇编写注册机算了!
写注册机时候,应该注意到上面eax等寄存器的问题!
--------------------------------------------------------------------------------
【总结】
这个crackme有点意思,据作者说,对于理解ecc有帮助,嘿嘿,等有时间去看看ecc算法!
如果加上hash函数,再把算法复杂一些,这个注册机制就很难逆向!
根据用户名,算法,确定A值,就可以唯一注册码了!
还有,不是简单把下面ecx=0,就显示成功,而是经过转换,爆破就麻烦一些。
0040155C sub ecx,esi ; Esi=注册码,验证正确应该相等,减法结果ecx=0,重点!有些迷惑
后面的小手段!
0040155E cmp eax,1 ; 正确验证,eax应等于1
00401561 jnz short CrackMe4.0040159F ; 不等跳就玩完!
00401563 lea edx,dword ptr ds:[ecx+64] ; 用上了前面的ecx的值!ecx=0,则edx=64h,d
爆破点,就是让上面ecx=0 ,00401561这里不跳即可!
--------------------------------------------------------------------------------
【版权声明】: 本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法