1、申 请 I D:沉默的小光
2、个人邮箱:liangjin0414@163.com
3、原创技术文章:对160个CrackMe之007的探究
之前在论坛里看到“[反汇编练习] 160个CrackMe之xxx”系列贴子(链接http://www.52pojie.cn/forum.php?mod=viewthread&tid=265379),加上自己一直想学习反汇编,于是下载该帖子中的资源,开始尝试自己破解。资源下载请参照原帖。
之所以写这个帖子,是因为这是第一个完全由我自己发现的破解思路,计算出一个可以通过程序验证的注册码。
下面开始:
首先打开这个程序
点击Help按钮,它会告诉你,这个crackMe只能通过填码的方式,不能修改原文件(之前6个都是通过修改原文件达到目的的),这对于刚开始研究的我无疑是一个挑战。
点Cancella,是直接删除Codice的值,通过尝试发现,Codice只能输出1~0x7FFFFFFF之间的数,点Register没有任何反映。
开始破解,照之前的经验:
打开OD,附加到这个程序上
点击键盘Alt+E,进入模块查看页面
选择aLoNg3_x模块,右键,跟随入口,进入到这个模块的代码里来
之前已经用工具检查过,这个程序是没有带壳的,因此直接找就好
和以前一样:右键,选择中文搜索引擎,选择搜索ASCII字串
不出所料:找到不少字符串。
选择见过的两个“You MUST insert a valid Long Integer Value in the Code Editor... Thank you :)”,跟入对比后发现这两个位置代码结构类似
这是一个弹框的提示信息,跟着这个字串地址下面的是一个call,说明这个call是一个类似C语言API里的MessageBox的函数,给所有的这个函数写上注释
操作这个字串的地址的上一条语句是一条跳转语句
004430F2 |. 837D FC 00 cmp [local.1],0x0
004430F6 |. 74 3A je XaLoNg3x_.00443132
说明上面是判断字串是否符合规定的判断,在这条JE语句上下断点,输入符合规定的数字
断下了,往下单步跟,发现一个判断语句
00442FBE |. 84C0 test al,al
00442FC0 74 30 je XaLoNg3x_.00442FF2
EAX寄存器通常用来作返回值用的,于是怀疑这个判断语句作用,选中je那条语句右键-》二进制-》用NOP填充,下好断点
再次运行程序,填好数字,点下register按钮,到被修改的这一句里断下,一句一句单步步过,同时观察crackMe是否有变化(由第6个crackMe得到的经验)
到了执行过
[Asm] 纯文本查看 复制代码 00442FCA |. E8 6101FEFF call aLoNg3x_.00423130 ; ;显示/隐藏
这句后,register按钮消失了,变成这样:
通过这样,推测这是一句显示/隐藏按钮的语句
给所有的这样的call写上注释(ctrl+F,填刚才的命令,ctrl+L一个一个地写注释)
可以发现总共有5个这样的地方,通过对参数分析(register对应的参数ebx+0x2CC,again对应ebx+2B8,edx为0消失,为1显示),能发现
有一个“落单”的call,从它的参数来看,是让again消失的
可以发现这已经是第二步了。其它4个call都是成对出现的,从参数上看,它们是恰好相反的,这和点register和点again按钮反映相反完全相同
下面我们要计算出注册码,就要把上面的
[Asm] 纯文本查看 复制代码 00442FBE |. 84C0 test al,al
00442FC0 74 30 je XaLoNg3x_.00442FF2
“变成”非0。
人00442FBE上一句call那里下断点,断下后,从寄存器中的值可以看到ecx为Nome的指针,而edx为codice的十六进制数据,还有一个参数不知道有什么作用,暂时不管,从这个call跟进去
发现local.2被赋值成Nome,local.1被赋值成codice
下面通过一个判断(判断Nome是几位,少于5位直接失败)后是一堆对计算,
结构是一个两重循环:
[Asm] 纯文本查看 复制代码 004429F6 |> /8B45 F8 /mov eax,[local.2] ; nome
004429F9 |. |E8 3610FCFF |call aLoNg3x_.00403A34 ; getStringLength
004429FE |. |83F8 01 |cmp eax,0x1
00442A01 |. |7C 1D |jl XaLoNg3x_.00442A20
00442A03 |> |8B55 F8 |/mov edx,[local.2] ; nome
00442A06 |. |0FB65432 FF ||movzx edx,byte ptr ds:[edx+esi-0x1] ; 取第esi位字符
00442A0B |. |8B4D F8 ||mov ecx,[local.2] ; nome
00442A0E |. |0FB64C01 FF ||movzx ecx,byte ptr ds:[ecx+eax-0x1] ; 取第eax位
00442A13 |. |0FAFD1 ||imul edx,ecx ; edx = edx * ecx
00442A16 |. |0FAFD7 ||imul edx,edi
00442A19 |. |03DA ||add ebx,edx
00442A1B |. |48 ||dec eax
00442A1C |. |85C0 ||test eax,eax
00442A1E |.^|75 E3 |\jnz XaLoNg3x_.00442A03
00442A20 |> |46 |inc esi
00442A21 |. |FF4D F4 |dec [local.3]
00442A24 |.^\75 D0 \jnz XaLoNg3x_.004429F6
ebx成了累加器,通过对这个二重循环分析,翻译成C语言代码如下:
[C] 纯文本查看 复制代码 do
{
$eax = strlen(nome);
do
{
$edx = nome[$esi-1];
$ecx = nome[$eax-1];
$edx *= $ecx;
$edx *= $edi;
$ebx += $edx;
$eax--;
}while($eax>0);
$esi++;
length--;
}while(length>0);
其实就是照抄啦。[Asm] 纯文本查看 复制代码 00442A26 |> \8BC3 mov eax,ebx
00442A28 |. 99 cdq
00442A29 |. 33C2 xor eax,edx
00442A2B |. 2BC2 sub eax,edx
00442A2D |. B9 2A2C0A00 mov ecx,0xA2C2A
00442A32 |. 99 cdq
00442A33 |. F7F9 idiv ecx
00442A35 |. 8BDA mov ebx,edx ; ebx = edx
00442A37 |. 8B45 FC mov eax,[local.1] ; codice
00442A3A |. B9 59000000 mov ecx,0x59
00442A3F |. 99 cdq
00442A40 |. F7F9 idiv ecx ; codice/0x59
00442A42 |. 8BC8 mov ecx,eax ; eax为商,edx为余数
00442A44 |. 8B45 FC mov eax,[local.1] ; codice被除数
00442A47 |. BE 50000000 mov esi,0x50
00442A4C |. 99 cdq
00442A4D |. F7FE idiv esi ; codice/0x50
00442A4F |. 03CA add ecx,edx
00442A51 |. 41 inc ecx ; ecx = -1
00442A52 |. 894D FC mov [local.1],ecx ; ecx = -1
00442A55 |. 3B5D FC cmp ebx,[local.1] ; ebx = edx
加上程序后面继续处理的,最后是判断ebx和ecx是不是相等,相等的话就成功了。试了好几次,ebx都是0,按代码计算来看,要解出这样一个方程
x%0x50+x/0x59=-1
这样的x是找不到的。对ebx的值跟进发现它来自我们不明白含义的那个参数
[Asm] 纯文本查看 复制代码 00442FB4 |. A1 30584400 mov eax,dword ptr ds:[0x445830]
对这个地址下内存写入断点,对经过几次尝试,我们修改Nome,Codice或是点register都不会使这个内存的值修改
但是当我输出了一个超过上界的数,如“11111111111”(11个1),再点register,发现被内存断点断下了,再看这个地址的值已经被修改成0x221B了
经过多次试验,发现输入了一个超过上界的数之后虽然会有弹窗,但是会使这个内存地址的值被修改,而且是本次点击后才计算会影响下次的点击结果
最后还是以输入“11111111111”生成的0x221B来进行计算,当然还是利用C语言
[C++] 纯文本查看 复制代码 #include<stdio.h>
#include<string.h>
void main()
{
int codice=28774115;
char nome[100];
strcpy(nome,"asdfasf");
int length = strlen(nome);
int local2 = (int)nome;
int $eax,$edx,$ebx,$ecx,$esi,$edi;
$eax = 0x221B;
$edi = $eax;
$ebx = 0;
$esi = 1;
do
{
$eax = strlen(nome);
do
{
$edx = nome[$esi-1];
$ecx = nome[$eax-1];
$edx *= $ecx;
$edx *= $edi;
$ebx += $edx;
$eax--;
}while($eax>0);
$esi++;
length--;
}while(length>0);
$eax = $ebx;
_asm{
mov eax,$eax
mov ebx,$ebx
mov ecx,$ecx
mov edx,$edx
mov esi,$esi
mov edi,$edi
mov eax,ebx
cdq
xor eax,edx
sub eax,edx
mov ecx,0xA2C2A
cdq
idiv ecx
mov ebx,edx
mov eax,codice
mov ecx,0x59
cdq
idiv ecx
mov ecx,eax
mov eax,codice
mov esi,0x50
cdq
idiv esi
add ecx,edx
inc ecx
mov $ecx,ecx
mov $ebx,ebx
}
printf("ecx:\t%x\n",$ecx);
printf("ebx:\t%x\n",$ebx);
}
使用这段代码时会发现,增大codice的值会使ecx最终值变大,减小时会使ecx变小,经过几次尝试发现
使用codice=28774115时正好相同
按照上面的方法重新测试之后发现果然可行,弹出again,Nome里的字段变灰
对again下断点发现它和register走的是同一个计算函数,还真是again。
再次相同操作之后,成功。
在做一个注册机的话,在C语言那块利用二分法来自动计算一下codice的值,按照上述步骤再操作一遍就OK了。
|