首页
社区
课程
招聘
[原创]happytown的“我的第7个CrackMe”算法分析
发表于: 2005-11-2 13:13 8226

[原创]happytown的“我的第7个CrackMe”算法分析

hud 活跃值
2
2005-11-2 13:13
8226

我是个刚学破解一个月左右的菜鸟,还是第一次发表破文,前面shooooo大侠早就破解出来了,我就介绍一下算法吧,请各位多指教。

本地下载:http://bbs.pediy.com/upload/2005/37/files/crackme_0007.rar

用PEID检查,没加壳,用Ollydbg打开,随便输入用户名和试练码,下断点于GetDlgItemTextA。

以下没指明时的位数是指输入的试练码位数,由于篇幅,F7步进的部分就没有写出来了,除了GetDlgItemTextA,以下绝大部分有备注处的Call都需要用F7进入。

0040150F  call <jmp.&user32.GetDlgItemT>
         ; 断点GetDlgItemTextA
00401514  lea ecx,dword ptr ds:[403020]
         ; 用户名长度区域80(128.)
0040151A  mov dword ptr ds:[4030A0],ecx
00401520  mov dword ptr ds:[4030A4],eax
         ; 用户输入的用户名长度
00401525  cmp dword ptr ds:[4030A4],4   
         ; 用户名长度不小于4位,否则over
0040152C  jnb short CrackMe.00401532
0040152E  leave
0040152F  retn 10
00401532  push 100                     
00401537  push CrackMe.004030AC         ; |Buffer = CrackMe.004030AC
0040153C  push 3ED                      ; |ControlID = 3ED (1005.)
00401541  push dword ptr ss:[ebp+8]     ; |hWnd
00401544  call <jmp.&user32.GetDlgItemT>; \GetDlgItemTextA
00401549  mov dword ptr ds:[4030A8],eax
         ;  用户输入的注册码长度
0040154E  cmp eax,10                    
         ;  注册码长度长度为16,否则over
00401551  je short CrackMe.00401557
00401553  leave
00401554  retn 10
00401557  lea ecx,dword ptr ds:[4030AC]
0040155D  mov dword ptr ds:[4031AC],ecx
00401563  call CrackMe.00401068         
         ;  第1位要等于H
00401568  xor eax,eax
0040156A  call CrackMe.0040109D         
         ;  第2位要等于T
0040156F  or eax,edx
00401571  call CrackMe.004010D2         
         ;  第3位要等于-
00401576  test eax,eax
00401578  call CrackMe.00401107         
         ;  第4位要等于7
0040157D  xor ecx,eax
0040157F  call CrackMe.0040113C         
         ;  5、7位相加 = 10、11位相加,且要是偶数
00401584  not ecx                       
00401586  push dword ptr ds:[4030A4]
0040158C  push dword ptr ds:[4031AC]
00401592  call CrackMe.0040120E
         ;  用户名各位相加除用户名长度应等于注册码第6位
00401597  add ecx,dword ptr ds:[4030A4]
0040159D  call CrackMe.004011A1
         ;  注册码8、9位相加=用户名第2位、倒数第2位相加
004015A2  xchg eax,ecx
004015A3  shl eax,1
004015A5  mov edx,eax
004015A7  call CrackMe.00401252
004015AC  mov eax,12345678
004015B1  call CrackMe.004012A3
004015B6  add eax,ecx
004015B8  call CrackMe.004012EF         
         ;  注册码13位+6位要是奇数
004015BD  xor ecx,ecx
004015BF  push dword ptr ds:[4030A0]
004015C5  call CrackMe.0040102D
004015CA  call CrackMe.00401340         
         ;  注册码13~15位相加+用户名长度=10A
004015CF  add ecx,ebx
004015D1  push 100
004015D6  push CrackMe.004030AC
004015DB  push 3ED
004015E0  push dword ptr ss:[ebp+8]
004015E3  call <jmp.&user32.GetDlgItemT>
004015E8  mov ecx,eax
004015EA  rol ecx,5
004015ED  mov eax,ecx
004015EF  call CrackMe.004013A0         
         ;  如果前面都正确,这里就能得到FFF
004015F4  sub eax,ecx
004015F6  cmp dword ptr ds:[4031B0],0FF>
         ;  对比结果是否等于0FFF
00401600  jnz short CrackMe.00401616   
         ;  这里相等就成功了
00401602  push 40                       ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
00401604  push CrackMe.004020D0         ; |Title = "Congratulations"
00401609  push CrackMe.004020C1         ; |Text = "GOOD JOB, MAN!"
0040160E  push dword ptr ss:[ebp+8]     ; |hOwner
         ;  这里成功后会有对话框"GOOD JOB, MAN!"

*******************************************************************

算法总结:

1. 用户名长度要在4位以上,注册码是16位;
2. 注册码前4位为HT-7,我猜测是H(appy)T(own Crackme)-7;
3. 注册码5、7位的和=10、11位的和, 而且相加的和要是偶数;
4. 用户名各位相加,除用户名长度,等于注册码第6位;
5. 注册码8、9位的和 = 用户名2、倒数第2位的和
6. 注册码13位和第6位相加的和要是奇数
7. 而且(13、14、15位相加) + 名长 = 10A;
8. 16位=用户名倒数第2位
9. 注册码第12位没什么要求,填个数字1吧。

以下是在Win-TC里用C语言写的注册机:

/******************************************************************

happytown crackme 7 C语言注册机- By hud, November 2, 2005

******************************************************************/
#include <stdio.h>

void main()
{
    char name[20], sn[17] = "HT-7"; /*前4位是HT-7:h(appy)t(own crackme)7 */
    int i, len, x = 0;

    printf("Input name(>=4):\n");
    scanf("%s", name);

    len = strlen(name);
    if (len<4)                      /*用户名要4位以上 */
    {
        printf("Name at least 4 character!\n");
        return;
    }

    for (i=0; i<len; ++i)           /*用户名各位相加 */
        x += name[i];
   
    x /= len;                       /*用户名各位相加和除用户名长度 */
    sn[5] = x;                      /*商即是第6位sn[5](C语言数组以0为基数) */

    sn[4] = sn[9] = '5';            /*注册码5、7位和=10、11位和,且为偶数 */
    sn[6] = sn[10] = '7';

    sn[7] = name[1];                /*注册码8、9位和=用户名第2位和倒数2位相加 */
    sn[8] = name[len-2];

    x = (0x10A - len)/3;            /*注册码13~15位的和减用户名长度要等于10A */

    if ((sn[5] + x)%2)              /*而且注册码13位和第6位相加为奇数 */
    {
        sn[12] = x;
        sn[13] = x - 1;
    }
    else
    {
        sn[12] = x - 1;
        sn[13] = x;     
    }
    sn[14] = 0x10A - x*2 + 1 - len; /*这几步求13~15位和减用户名长=10A */

    sn[15] = name[len-2];           /*注册码第16位要等于用户名倒数第2位 */
   
    sn[11] = '1';                   /*第12位好像没要求 */
    sn[16] = '\0';
    printf("SN is: %s\n", sn);
    getch();
}


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (11)
雪    币: 233
活跃值: (130)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
2
支持啊!!
2005-11-2 13:37
0
雪    币: 221
活跃值: (161)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
3
谢谢拜月教主了!
2005-11-2 13:46
0
雪    币: 721
活跃值: (350)
能力值: ( LV9,RANK:1250 )
在线值:
发帖
回帖
粉丝
4
最初由 hud 发布
我是个刚学破解一个月左右的菜鸟,还是第一次发表破文,前面shooooo大侠早就破解出来了,我就介绍一下算法吧,请各位多指教。

用PEID检查,没加壳,用Ollydbg打开,随便输入用户名和试练码,下断点于GetDlgItemTextA。

以下没指明时的位数是指输入的试练码位数,由于篇幅,F7步进的部分就没有写出来了,除了GetDlgItemTextA,以下绝大部分有备注处的Call都需要用F7进入。
........


给你100分,思路很清晰。

这个CrackMe就是面向初学者的(其实,我也很菜);无论如何,恭喜你了。
2005-11-3 17:40
0
雪    币: 221
活跃值: (161)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
5
happytown大侠,你好!

非常感谢你,写出这么好的适合初学者的Crackme!我学习破解的这1个月来,最重要的两个练习都是你的作品:happytown crackme6和happytown crackme7。

Crackme6我在算法分析上花了15个钟以上(我很菜,见笑了),大部分分析出来了,其中一个关键最后仍是看了ProZel大侠的破文后才明白的。对这个Crackme6我的印象最为深刻,因为我算法完全明白后当时也没能写出完全正确的注册机,PrOZel及后面一个跟贴的朋友在贴上发的注册机也不完全正确,PrOZel的C++注册机用了两处嵌入式汇编,解决了两个难题。正是这两处嵌入式汇编地方,我想用C/C++来写,由于以前很少接触汇编转为C/C++语言方面,两天后我才写出纯C++的完全正确的注册机。

对Crackme7的分析、写注册机和破文我也用了10个钟左右(我还是非常菜),这个Crackme7的练习对我收获最大的2个地方是:

1. 软件保护时,为了增加破解难度,部分程序员会用很多的跳转。这个Crackme7中的跳转也比较多,也可以说是这个Crackme的主要特点之一。我在跟踪这个软件时,先用F8一直往下走了一遍,很快发现后面有一处“GOOD JOB, MAN"的字串,在它的前面有一个和0x0FFF的比较。我返回去再跟踪时,发现有一个值从F开始,每通过一步就会增加,刚开始时每次跟到这里总是发现比0x0FFF小了很多,于是不断地返回去再试。再试时我观察到,很多处如果跳转成功这个值就会变得更大,于是我试着达到每个跳转的条件,很快就一步步接近正确的注册码了。
第一点体会就是:在关键的跳转处要将跳转和不跳转都试一试。

2. 以前我只知道对比方式cmp,这个软件很多地方不用cmp,而间接用XOR或者OR来判断,比较典型的一处有:

004012FA  |>call CrackMe.00401050         
;  取13位
004012FF  |>mov ebx,eax
00401301  |>push 5
00401303  |>push dword ptr ds:[4031AC]
00401309  |>call CrackMe.00401050         
;  取第6位
0040130E  |>add eax,ebx
;  相加后放入eax
00401310  |>mov ecx,2
00401315  |>xor edx,edx
;  edx清0
00401317  |>idiv ecx                       
;  除2,32位整数除法余数会放入edx
00401319  |>or edx,edx                     
;  edx原值大于0时运算后edx才大于0
0040131B  |>jnz short CrackMe.0040132C

这里是取注册码第13位和第6位相加,然后除2。如果edx不等于0,那么就能跳转成功,但是开始edx是等于0的,因为idiv ecx执行一个有符号的整数除法(除2),只有当eax(13位和第6位相加)为奇数时,除2会有余数,这个余数会放到edx中,那么or edx, edx后,edx才不等于0。

happytown大侠,我还会继续向你学习的,再次感谢!
2005-11-3 18:59
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
楼主,我跟进 00401563  call CrackMe.00401068  怎么知道是H呢?      

00401068    $  53            push ebx
00401069       56            db 56                                   ;  CHAR 'V'
0040106A       57            db 57                                   ;  CHAR 'W'
0040106B       6A            db 6A                                   ;  CHAR 'j'
0040106C       00            db 00
0040106D    .  FF35 AC314000 push dword ptr ds:[4031AC]              ;  CrackMe7.004030AC
00401073    .  E8 D8FFFFFF   call CrackMe7.00401050
00401078    .  83F8 48       cmp eax,48
0040107B    .  74 0F         je short CrackMe7.0040108C
0040107D    .  6A 00         push 0
0040107F    .  FF35 B0314000 push dword ptr ds:[4031B0]
00401085    .  E8 76FFFFFF   call CrackMe7.00401000
0040108A    .  EB 0D         jmp short CrackMe7.00401099
0040108C    >  6A 01         push 1
0040108E    .  FF35 B0314000 push dword ptr ds:[4031B0]
00401094    .  E8 67FFFFFF   call CrackMe7.00401000
00401099    >  5F            pop edi
0040109A    .  5E            pop esi
0040109B    .  5B            pop ebx
0040109C    .  C3            retn
2005-11-5 19:31
0
雪    币: 221
活跃值: (161)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
7
00401563  |. >call CrackMe.00401068                           
;  取注册码1位,和48(H)比较,F7进入其函数内部为:

00401068   $ >push ebx
00401069     >db 56                                            ;  CHAR 'V'
0040106A     >db 57                                            ;  CHAR 'W'
0040106B     >db 6A                                            ;  CHAR 'j'
0040106C     >db 00
0040106D   . >push dword ptr ds:[4031AC]
00401073   . >call CrackMe.00401050
        ;  这里要F7再进入
00401078   . >cmp eax,48
        ;  上面的结果和48比较,48就是H的ASCII码
0040107B   . >je short CrackMe.0040108C
        ;  相等才会跳

00401073   . >call CrackMe.00401050
        ;  按F7进入内部

00401050  /$ >push ebp
00401051  |. >mov ebp,esp
00401053  |. >push ebx
00401054  |. >push esi
00401055  |. >push edi
00401056  |. >mov eax,[arg.1]                                  ;  输入的试练码
00401059  |. >add eax,[arg.2]                                  ;  [arg.2] = 0, 即是取试练码第1位
0040105C  |. >movzx ecx,byte ptr ds:[eax]                      ;  取得H放入ecx
0040105F  |. >mov eax,ecx                                      ;  H的值48放入eax
00401061  |. >pop edi
00401062  |. >pop esi
00401063  |. >pop ebx
00401064  |. >leave
00401065  \. >ret 8
2005-11-6 09:17
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
很感谢对菜鸟的指点,48就是H的ASCII码
那有什么工具来转换字符到ASCII码吗??
2005-11-6 10:22
0
雪    币: 221
活跃值: (161)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
9
在Ollydbg的命令窗口中输入48,就可以看到它的10进制、16进制、以及ASCII码。关于命令行窗口,如果没有的话,可以去下载插件。

或者直接下载聆风听雨汉化的1.10版, 里面就自带很多插件。
2005-11-6 10:30
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
谢,我下个汉化OD去。。
2005-11-6 10:46
0
雪    币: 721
活跃值: (350)
能力值: ( LV9,RANK:1250 )
在线值:
发帖
回帖
粉丝
11
最初由 hud 发布
happytown大侠,你好!

非常感谢你,写出这么好的适合初学者的Crackme!我学习破解的这1个月来,最重要的两个练习都是你的作品:happytown crackme6和happytown crackme7。

Crackme6我在算法分析上花了15个钟以上(我很菜,见笑了),大部分分析出来了,其中一个关键最后仍是看了ProZel大侠的破文后才明白的。对这个Crackme6我的印象最为深刻,因为我算法完全明白后当时也没能写出完全正确的注册机,PrOZel及后面一个跟贴的朋友在贴上发的注册机也不完全正确,PrOZel的C++注册机用了两处嵌入式汇编,解决了两个难题。正是这两处嵌入式汇编地方,我想用C/C++来写,由于以前很少接触汇编转为C/C++语言方面,两天后我才写出纯C++的完全正确的注册机。
........


过奖了!

请允许我重复一遍:其实我也很菜!

的确如此,我一直在刻意写一些适合初学者入门的CrackMe。

因为我认为,有一些初学者对Crack很感兴趣,但刚开始就让大家面对难度太大的CrackMe定会使这些人打退堂鼓。所以,我就在写CrackMe时,留心了这一点:既不失Crack之实,又不致难度太大。

也算我在这方面的一个尝试吧!当然,效果如何,还得看大家的感觉和反馈了。
2005-11-8 19:52
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
翻下老贴,呵呵,刚开始练习,今天到这个了。发现第12位好象不应是任意字符吧:
--------------------------
004012A8  |.  A1 A4304000   mov     eax, [4030A4]
004012AD  |.  B9 03000000   mov     ecx, 3
004012B2  |.  F7F9          idiv    ecx                              ;  用户名长度除以3
004012B4  |.  8BF2          mov     esi, edx
004012B6  |.  6A 0B         push    0B
004012B8  |.  FF35 AC314000 push    dword ptr [4031AC]
004012BE  |.  E8 8DFDFFFF   call    00401050                         ;  取注册码第12位
004012C3  |.  83E8 30       sub     eax, 30                          ;  ASCII-30h
004012C6  |.  33D6          xor     edx, esi                         ;  应=用户名长度 Mod 3
004012C8  |.  0BD2          or      edx, edx
004012CA  |.  74 0F         je      short 004012DB
2006-12-22 22:58
0
游客
登录 | 注册 方可回帖
返回
//