首页
社区
课程
招聘
趣味CRACKME破解分析(发散思维)
发表于: 2006-8-13 16:26 8639

趣味CRACKME破解分析(发散思维)

2006-8-13 16:26
8639

【破文标题】CRACKME破解分析
【破文作者】逍遥风
【破解工具】OD,计算器
【破解平台】WINXP
【软件简介】Difficulty: 2 - Needs a little brain (or luck)
            Platform: Windows
            Language: Assembler

----------------------------------------------------------------------
CRACKME很有意思,第一次见如此注册方式的CRACKME.分析一下,
根据提示信息很容易找到关键代码。

00401288  /$  55            push    ebp                ;  在这里下断点
00401289  |.  8BEC          mov     ebp, esp
0040128B  |.  81C4 FCFEFFFF add     esp, -104
00401291  |.  C785 FCFEFFFF>mov     dword ptr [ebp-104>
0040129B  |.  68 80000000   push    80                 ; /Count = 80 (128.)
004012A0  |.  8D45 80       lea     eax, [ebp-80]      ; |
004012A3  |.  50            push    eax                ; |Buffer
004012A4  |.  68 ED030000   push    3ED                ; |ControlID = 3ED (1005.)
004012A9  |.  FF35 0C304000 push    dword ptr [40300C] ; |hWnd = 00190644 ('b2c_2k5',class='DLGCLASS')
004012AF  |.  E8 3A010000   call    <jmp.&user32.GetDl>; \GetDlgItemTextA
004012B4  |.  83F8 05       cmp     eax, 5             ;  注册名位数与5比较
004012B7  |.  0F82 C7000000 jb      00401384           ;  小于5位就跳向失败
004012BD  |.  68 80000000   push    80                 ; /Count = 80 (128.)
004012C2  |.  8D85 00FFFFFF lea     eax, [ebp-100]     ; |
004012C8  |.  50            push    eax                ; |Buffer
004012C9  |.  68 EE030000   push    3EE                ; |ControlID = 3EE (1006.)
004012CE  |.  FF35 0C304000 push    dword ptr [40300C] ; |hWnd = 00190644 ('b2c_2k5',class='DLGCLASS')
004012D4  |.  E8 15010000   call    <jmp.&user32.GetDl>; \GetDlgItemTextA
004012D9  |.  83F8 08       cmp     eax, 8             ;  注册码位数与8比较
004012DC  |.  0F85 A2000000 jnz     00401384           ;  注册码不等于8位就跳向失败
从这里可以得知:
注册名必须大于5位
注册码必须等于8位

004012E2  |.  8D95 00FFFFFF lea     edx, [ebp-100]     ;  使EDX等于输入的注册码
004012E8  |.  52            push    edx
004012E9  |.  E8 82010000   call    00401470           ;  将输入的注册码转换成对应的大写
004012EE  |.  50            push    eax
004012EF  |.  E8 92000000   call    00401386
004012F4  |.  83F8 00       cmp     eax, 0             ;  输入注册码了吗?
004012F7  |.  0F84 87000000 je      00401384           ;  没有输入注册码就跳向失败
004012FD  |.  33D2          xor     edx, edx           ;  EDX清零,准备开始计算
004012FF  |.  35 33644752   xor     eax, 52476433      ;  XOR (输入的注册码,0x52476433),结果设为A
00401304  |.  35 56244752   xor     eax, 52472456      ;  XOR (A,0x52472456),结果设为B
00401309  |.  2D 00000004   sub     eax, 4000000       ;  B减去0x4000000结果设为C

这是对注册码的处理,先将注册码中小写的部分转换成对应的大写形式。
设输入的注册码为;CODE
则对注册码的处理过程为:XOR [XOR(CODE,0x52476433),0x52472456],
作者:逍遥风
因为XOR(0x52476433,0x52472456)=0x4065

所以:对注册码处理的过程就相当与 XOR (CODE,0x4065)结果设为B

C=B-0x4000000

0040130E  |.  BB 01000000   mov     ebx, 1                ;  使EBX等于1
00401313  |.  C1C3 10       rol     ebx, 10               ;  ROL (EBX,10)
00401316  |.  8D75 80       lea     esi, [ebp-80]         ;  使ESI等于注册名
00401319  |>  803E 00       /cmp     byte ptr [esi], 0
0040131C  |.  74 0D         |je      short 0040132B
0040131E  |.  0FB616        |movzx   edx, byte ptr [esi]  ;  取注册名每一位的ASCII码
00401321  |.  C1C2 10       |rol     edx, 10              ;  ROL (注册名ASCII,10)
00401324  |.  03C2          |add     eax, edx             ;  C与注册名ASCII码累加计算
00401326  |.  2BC3          |sub     eax, ebx             ;  每一次计算的结果减去定值0x10000
00401328  |.  46            |inc     esi                  ;  每计算一次ESI加1
00401329  |.^ EB EE         \jmp     short 00401319       ;  循环计算
0040132B  |>  8BD8          mov     ebx, eax              ;  使EBX等于计算结果,结果设为D
0040132D  |.  C1CB 10       ror     ebx, 10               ;  ROR(计算结果,10)
00401330  |.  66:81FB 90C3  cmp     bx, 0C390             ;  计算结果的低位与0x0C390相比较
00401335  |.  75 4D         jnz     short 00401384        ;  不相等就注册失败

第一个关键的地方。在这里程序把上一步计算结果与注册名的ASCII码累加,并减去定值。
将最后计算结果的低位与定值0x0C390比较,如果不相等就跳向失败。
所以现在就要根据0x0C390这个值来逆推出注册码的相关部分。

先简化一下计算过程,理清一下思路:
取计算结果C,以注册名lovetc为例
程序的计算过程是

C+6C0000-10000+6F0000-10000+。。。+630000-10000

把它简化,那么就是:C+6C+6F+76+65+74+63-6=C390
现在求C
C+28D-6=C+287=C390
C390-287=C=C190
则C=C190,回归到程序中,C的形式就应该为C190****(8位)

联系上文,看看C是怎么得来的
XOR(A ,0x4065) = B
B - 0x4000000 = C
现在根据C就可以求出B了,简化后的过程就为
B - 400 = C,所以B = C + 400 = C190 + 400 = C590
所以B的形式就应该为:C590****(8位)
现在推倒一下A(即CODE注册码)的情况,但是我们现在并不知道B到底等于什么。
但是注意一下,如果要根据B来求A,就要 XOR(B ,0x4065) = A
B具体等于什么并不清楚,但是B是一个8位数,4065只有4位,这两者XOR运算的结果只会在B的后4位发生变化。
即:
C509XXXX XOR 4065 = (C509)(ABCD)
C509YYYY XOR 4065 = (C509)(EFGH)        
                       |        |------------------X,Y的值只影响这部分   
                       |-------------------不论X,Y等于什么,这部分不变
所以就可以推出
注册名:lovetc
对应注册码的形式应该为:C509****

00401337  |.  8985 FCFEFFFF mov     [ebp-104], eax         ;  保存累加计算的结果D
0040133D  |.  B9 04000000   mov     ecx, 4                 ;  使ECX等于4
00401342  |.  33D2          xor     edx, edx               ;  EDX清零
00401344  |.  33DB          xor     ebx, ebx               ;  EBX清零
00401346  |>  83F9 00       /cmp     ecx, 0                ;  计算完了吗?
00401349  |.  74 0A         |je      short 00401355        ;  计算完就跳走
0040134B  |.  8AD8          |mov     bl, al                ;  两位两位取计算结果D
0040134D  |.  03D3          |add     edx, ebx              ;  累加,计算结果设为E
0040134F  |.  C1C8 08       |ror     eax, 8                ;  取下一位
00401352  |.  49            |dec     ecx                   ;  每计算一次ECX减1
00401353  |.^ EB F1         \jmp     short 00401346        ;  循环计算
00401355  |>  81FA 85020000 cmp     edx, 285               ;  计算结果与0x285比较
0040135B  |.  75 27         jnz     short 00401384         ;  不相等就注册失败

这里,程序对上一步的计算结果D,进行累加计算。

也就是说,我们以输入的注册码,C5091234为例,上一步将C5091234进行计算的结果为C3905251

在这里将C3905251进行计算的过程就是
E=C3+90+52+51 = 1F6

但是,程序要求E必须等于285。又要开始逆推了。

C3和90已经可以看作是定值了,C3+90=152  

因为:E=C3+90+XX+YY=285 所以XX+YY=285-152=132,即后两位相加的和必须为132
现在根据这个条件可得到很多结果,任何选一组:BB + 77 = 132

所以这时:D = C390BB77,
那么 XOR (BB77,4065)= FB12  ,所一个可能的注册码就是:C509FB12

重新载如,看还有什么要求,经过了重复的步骤,现在来到这里
0040135D  |.  8D9D FCFEFFFF lea     ebx, [ebp-104]
00401363  |.  FFD3          call    ebx                    ;  关键CALL,跟进
00401365  |.  83FF 00       cmp     edi, 0                 ;  EDI与0比较
00401368  |.  75 1A         jnz     short 00401384         ;  EDI不等于0就跳向失败
到底在这里做什么呢?跟进关键CALL看看。
0012FB9C  ^\77 BB           ja      short 0012FB59         ;  注意这里
            |―|――――――77BB?巧合?与上一步任意取得值77+BB=132有关吗?
            
0012FB9E    90              nop
0012FB9F    C3              retn
为了证明这里的77BB与上一步计算所取的值77+BB=132是否有关,再选一组相加等于132的值
CC+66=132。转换回要输入的注册码就是C5098C03。再次来到关键CALL
这次,关键CALL的内容发生了变化
0012FB9C    66:CC           int3
            |―|―――――――――――― CC+66=132
0012FB9E    90              nop
0012FB9F    C3              retn
现在就可以看出,关键CALL的第一步如何去做是由我们选定的值决定的。联系关键CALL后面的代码
00401365  |.  83FF 00       cmp     edi, 0                 ;  EDI与0比较
00401368  |.  75 1A         jnz     short 00401384         ;  EDI不等于0就跳向失败

可以知道,程序要求在经过关键CALL后,EDI的值必须为0。所以,关键CALL的作用就是使EDI=0

联系赋值操作,和寄存器清零操作,想想使EDI等于0有哪些方法?
1)mov edi,0  这样代码就是BF00 可是BF+00不等于132
2)xor edi,edi 这样代码就是33FF,33+FF = 132

呵呵~!看到曙光了
即满足相加等于132,又满足可以使EDI等于0
所以,FF33就是我们需要的值,将他转换成要输入的注册码
(C509)[XOR(FF33,4065)]
   |            |
固定值         BF65
所以注册码就是C509BF65,即
注册名:lovetc
注册码:C509BF65

----------------------------------------------------------------------
验证方式另类的CRACKME
有趣的分析过程,佩服作者的智慧
呵呵
----------------------------------------------------------------------
【版权声明】本文只为交流,转载请保留作者及文章完整性


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (16)
雪    币: 256
活跃值: (10)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
2
支持逍遥风
支持逍遥风
慢慢的能懂一些
语句了!这得感谢
他们的无私!!!

顺便问一句,如果我想这样分析
CrackMe 的话,想写破文的话到哪里去找 CrackMe ?
总不能分析别人分析过的吧!!
那样的话,免得说是照抄的!!呵呵!!~!
2006-8-13 16:54
0
雪    币: 338
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
学习!!支持!
2006-8-13 18:03
0
雪    币: 2256
活跃值: (941)
能力值: (RANK:2210 )
在线值:
发帖
回帖
粉丝
4
最初由 coldpine 发布
支持逍遥风
支持逍遥风
慢慢的能懂一些
语句了!这得感谢
他们的无私!!!
........


www.crackmes.de
2006-8-13 19:06
0
雪    币: 333
活跃值: (11)
能力值: ( LV12,RANK:770 )
在线值:
发帖
回帖
粉丝
5
确实挺新颖的
2006-8-13 19:16
0
雪    币: 270
活跃值: (176)
能力值: ( LV12,RANK:370 )
在线值:
发帖
回帖
粉丝
6
有创意
2006-8-14 03:04
0
雪    币: 179
活跃值: (131)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
7
提供一个keygen:
#include <iostream>
using namespace std;

void main()
{
        const int C1 = 0xc390;
        char name[80];
        int asc_sum = 0;
        int len;
        int i;

        do
        {
                cout<<"请输入用户名(长度4位以上):";
                cin>>name;
                len = strlen(name);
        }while(len < 5);

        for (i = 0; i < len; i++)
                asc_sum += name[i];

        cout<<uppercase<<hex<<"序列号为:"<<C1+len-asc_sum+0x400<<"BF56\n";
}

另外逍遥最后的计算有错误,那个常数是0x4065不是0x4056,算得lovetc的注册码就是C509BF56了
2006-8-15 00:12
0
雪    币: 179
活跃值: (131)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
8
看来About里面的PS不是一点用都没有
2006-8-15 00:14
0
雪    币: 2256
活跃值: (941)
能力值: (RANK:2210 )
在线值:
发帖
回帖
粉丝
9
最初由 WAKU 发布
提供一个keygen:
#include <iostream>
using namespace std;

void main()
........


呵呵~!谢谢提醒,那个常数不小心写错了...
2006-8-15 10:43
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
哎,不发贴不让下载,郁闷!
2006-8-15 18:11
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
文章写的很好,小弟在此感谢作者的无私奉献.不过小弟刚入道不久,对破解知识还是欠缺,尤其是如何找到文章中的关键性的代码段来分析算法不是很会,恳求上面的密界高手给小弟指点迷津,谢谢...
2006-8-16 17:57
0
雪    币: 433
活跃值: (176)
能力值: ( LV13,RANK:1250 )
在线值:
发帖
回帖
粉丝
12
最初由 coldpine 发布
顺便问一句,如果我想这样分析
CrackMe 的话,想写破文的话到哪里去找 CrackMe ?
总不能分析别人分析过的吧!!
那样的话,免得说是照抄的!!呵呵!!~!
........


当然可以用别人分析过的,只要你换一种处理方式,譬如逆向之类(我就经常啃别人没啃干净的骨头)
2006-8-19 12:19
0
雪    币: 179
活跃值: (131)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
13
楼上别叫冲天剑了,改名叫逆天剑吧
2006-8-19 12:56
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
没有办法,要法帖啊
我要雪破解
2006-8-19 12:58
0
雪    币: 256
活跃值: (10)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
15
感谢感谢大侠们
2006-8-27 08:01
0
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
站在巨人的肩膀上学习!!!!!
2006-8-27 11:16
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
果然是大侠,头晕
2006-8-30 22:34
0
游客
登录 | 注册 方可回帖
返回
//