作为一个非计算机专业的学生,我最讨厌的就是期末考试的最后几周了,虽然在复习着课本,但脑子里不自觉的就转到计算机上了。好吧,我保证,这是我在1月16号寒假之前的最后一篇帖子了。
今天这个crackme的算法,发费了我快一个下午的时间,中途遇见了几个不懂的汇编指令,然后就百度,分析。程序中有一个das指令,其实就是他浪费了我绝大多数的时间。最后还是通过分析大佬的一篇关于此crackme的逆向帖子,才稍微搞懂了这个程序。大佬的帖子我放在附件了,就不在这贴出来了。好,接下来看看这个craceme吧。
这个程序,加了一个APSpack的壳,通过脱壳软件就可以脱掉。
我们输入了用户名和注册码之后,Register按钮还是处于禁止状态,这说明,该程序是不断的获取我们的输入,知道输入的注册码正确才会变成激活的状态。
没有错误的字符串提示,我的思路就是,通过查找获取输入字符串的函数,去到关键的代码处。
Ctrl + N (查找当前模块中的名称(标签)),可以看到,GetDlgItemTextA函数:
回车,双击,来到它的调用处:
看到了,两个调用,那就可以肯定了,分别是获取用户名和获取注册码。
往下看,还可以看到一个EnableWindow函数,我猜,就是激活和禁止Register的函数。这个函数,之前没碰到过,所以去查了MSDN:
EnableWindow接收两个函数,hWnd:需要禁止或激活的窗口句柄。
bEnable:True代表激活 False代表禁止
此处的bEnable以eax的方式传递,push eax 的上方有一个call,我又猜想 :call处的函数是主要的算法函数,如果注册码对了,通过eax返回TRUE,否则返回FALSE。然后将eax的内容传给EnableWindow.
我的猜想对不对呢?实验一下不就知道了。进入call 004012F3:
眼睛一扫,发现大部分的数学运算指令,哼,这就是算法的核心了。我先用爆破的方式 来逆向这个小东西。
进入函数,直接给eax赋值为1,然后返回:
保存文件,打开,随便输入几个字符,可以看到Regester已经被激活了:
底下的图片也变成了CRACKED!
算法部分:
我学习逆向的时候,看大佬们的帖子,他们都是将分析的过程,以注释的方式写在od里,我呢,也跟大佬们一样,这样,以后看汇编代码的时候,也可以快速的回忆起当时的思路。
[Asm] 纯文本查看 复制代码 004012F3 /$ 90 nop
004012F4 8B0D 89234000 mov ecx, dword ptr [402389] ; len 用户名
004012FA 85C9 test ecx, ecx
004012FC |. 74 71 je short 0040136F ; 长度为0 注册按钮禁用
004012FE |. 49 dec ecx ; len - 1
004012FF |. 8BF1 mov esi, ecx
00401301 |. BF 53234000 mov edi, 00402353 ; ASCII "lingyin"
00401306 |. BB 4E4D4144 mov ebx, 44414D4E ; tmp = 0x44414d4e
0040130B |. 33D2 xor edx, edx ; sum = 0
0040130D |. 8BCA mov ecx, edx ; i = 0
0040130F |> 33C0 /xor eax, eax
00401311 |. 8A040F |mov al, byte ptr [edi+ecx]
00401314 |. 03D0 |add edx, eax ; sum += name[i]
00401316 |. D1CB |ror ebx, 1 ; ror tmp 1
00401318 |. D3CB |ror ebx, cl ; ror tmp i
0040131A |. 33DA |xor ebx, edx ; tmp = tmp ^ sum
0040131C |. 3BCE |cmp ecx, esi ; 是否遍历完毕
0040131E |. 74 03 |je short 00401323
00401320 |. 41 |inc ecx ; i++
00401321 |.^ EB EC \jmp short 0040130F
00401323 |> 81CB 10101010 or ebx, 10101010 ; tmp | 0x10101010
00401329 |. 87DA xchg edx, ebx ; sum 和 tmp 值的交换
0040132B |. BF 21234000 mov edi, 00402321 ; 指向注册码
00401330 |. 8B0D 8D234000 mov ecx, dword ptr [40238D]
00401336 |. 83F9 08 cmp ecx, 8 ; 注册码的长度 = 8
00401339 |. 75 34 jnz short 0040136F
0040133B |. 33C9 xor ecx, ecx ; j = 0
0040133D |> 33C0 /xor eax, eax
0040133F |. C1C2 08 |rol edx, 8 ; edx = tmp rol tmp 8
00401342 |. 8AC2 |mov al, dl ; var_1 = tmp的低八位
00401344 |. 8AD8 |mov bl, al ; var_2 = var_1
00401346 |. 24 0F |and al, 0F ; var_1 = var_1 & 0xf
00401348 |. C0EB 04 |shr bl, 4 ; shr var_2 4
0040134B |. 80E3 0F |and bl, 0F ; var_2 = var_2 & 0xf
0040134E |. 3C 0A |cmp al, 0A
00401350 |. 1C 69 |sbb al, 69 ; var_1 = var_1 - 0x69 - CF
00401352 |. 2F |das
00401353 |. 38444F 01 |cmp byte ptr [edi+ecx*2+1], al
00401357 |. 75 16 |jnz short 0040136F
00401359 |. 8AC3 |mov al, bl ; var_1 = var_2
0040135B |. 3C 0A |cmp al, 0A
0040135D |. 1C 69 |sbb al, 69 ; var_1 = var_1 - 0x69
0040135F |. 2F |das
00401360 |. 38044F |cmp byte ptr [edi+ecx*2], al
00401363 |. 75 0A |jnz short 0040136F
00401365 |. 41 |inc ecx
00401366 |. 83F9 04 |cmp ecx, 4
00401369 |.^ 75 D2 \jnz short 0040133D
0040136B |. 33C0 xor eax, eax ; 成功
0040136D |. 40 inc eax
0040136E |. C3 retn
0040136F |> 33C0 xor eax, eax ; 失败
00401371 \. C3 retn
das指令,我不会,百度了一下,发现还是不怎么明白 。
上面那段指令,var_1 和var_2 那一段,我真的是思考了很久,最后,算了,还是去Search吧。Search后,我知道了,下面那段代码是将经处理后的用户名的16进制直接转换为ascii字符的形式。。。。。。。。。。。。。

好吧,以我现在的能力,还看不懂这段代码,不管了,知道意思就好了,最后,写一个注册机:
[C] 纯文本查看 复制代码 #include<stdio.h>
#include<string.h>
//循环右移宏定义
#define ROTATE_RIGHT(x,n) (((x) >> (n)) | ((x) << (32 - (n))))
//用户名处理算法
void F_Name()
{
unsigned long tmp = 0x44414D4E;
char name[20];
unsigned long sum = 0;
int len;
int i;
printf("请输入用户名:\n");
scanf("%s",name);
len = strlen(name);
for(i = 0;i < len;i++)
{
sum += name[i];
tmp = ROTATE_RIGHT(tmp,1);
tmp = ROTATE_RIGHT(tmp,i);
tmp = tmp ^ sum;
}
tmp = tmp | 0x10101010;
printf("%X\n",tmp);
}
int main()
{
F_Name();
getchar();
return 0;
}
既然是将16进制转换为ascii的形式,再与输入的注册码进行对比,那么,我们将处理后的用户名以16进制的格式输出,得到的字符,不就是我们的注册码吗。
输入:xuepojie 注册码是:7573FA1F
但是,下面的图片还没有显示cracked,算了,不管了,反正我主要是研究算法。
唉,功力还是太低,等考完试,继续修炼。
|