首页
社区
课程
招聘
[原创]一个简单的CrackMe的详细分析和注册机制作
发表于: 2010-11-9 18:49 8862

[原创]一个简单的CrackMe的详细分析和注册机制作

2010-11-9 18:49
8862

【文章标题】:一个简单的CrackMe的详细分析和注册机制作
【文章作者】: 孤叶飘零
【作者邮箱】: buaa.libo@gmail.com
【软件名称】: The BigMan's CrackMe #6
【下载地址】: 论坛下载
【加壳方式】: 无壳
【使用工具】: OD
【作者声明】: 冒个泡,证明我没鬼混。。。和大家分享自己的学习经历
--------------------------------------------------------------------------------
用PEID查看文件,发现LCC Win32 1.x -> Jacob Navia [Overlay]
打开文件,输入Name:buaalibo
                     Serial:123456789
发现没有任何提示。
用OD载入,在这里
004011CB >/$  64:A1 0100000>mov     eax, dword ptr fs:[1]
004011D1  |.  55            push    ebp
004011D2  |.  89E5          mov     ebp, esp
004011D4  |.  6A FF         push    -1
004011D6  |.  68 1C204000   push    0040201C
004011DB  |.  68 9A104000   push    0040109A
004011E0  |.  50            push    eax
004011E1  |.  64:8925 00000>mov     dword ptr fs:[0], esp
004011E8  |.  83EC 10       sub     esp, 10
004011EB  |.  53            push    ebx
004011EC  |.  56            push    esi
004011ED  |.  57            push    edi
004011EE  |.  8965 E8       mov     dword ptr [ebp-18], esp
004011F1  |.  50            push    eax
004011F2  |.  D93C24        fstcw   word ptr [esp]
004011F5  |.  66:810C24 000>or      word ptr [esp], 300
004011FB  |.  D92C24        fldcw   word ptr [esp]
004011FE  |.  83C4 04       add     esp, 4

按CTRL+N,查看输入表,发现GetDlgItemTextA,这个应该就是读取用户名和注册码的地方,在反汇编窗口中跟随,
77D6B05E >  8BFF            mov     edi, edi
77D6B060    55              push    ebp
77D6B061    8BEC            mov     ebp, esp
77D6B063    FF75 0C         push    dword ptr [ebp+C]
77D6B066    FF75 08         push    dword ptr [ebp+8]
77D6B069    E8 0093FBFF     call    GetDlgItem

在此F2下断点,F9运行程序,输入Name:buaalibo
                                               Serial:123456789
点击CHECK ,断下,ALT+F9返回程序领空
00401534  |.  6A 65         push    65                               ; |ControlID = 65 (101.)
00401536  |.  FF75 08       push    dword ptr [ebp+8]                ; |hWnd
00401539  |.  E8 FA010000   call    <jmp.&USER32.GetDlgItemTextA>    ; \GetDlgItemTextA
0040153E  |.  89C3          mov     ebx, eax//eax返回的是name的长度
00401540  |.  09DB          or      ebx, ebx//如果没有输入用户名的话会失败
00401542  |.  75 04         jnz     short 00401548
00401544  |.  31C0          xor     eax, eax
00401546  |.  EB 50         jmp     short 00401598
00401548  |>  BF BC020000   mov     edi, 2BC
0040154D  |.  BE 30000000   mov     esi, 30
删除刚才的断点,在0040153E这一句下段,下次从这里开始分析。
F8一直向下走
00401548  |> \BF BC020000   mov     edi, 2BC
0040154D  |.  BE 30000000   mov     esi, 30
00401552  |.  B8 48000000   mov     eax, 48
00401557  |.  99            cdq                                      ;  将EAX扩展到edx
00401558  |.  F7FB          idiv    ebx                              ;  用48除以strlen(name),eax为商 edx为余数
0040155A  |.  29C6          sub     esi, eax                         ;  用30-商
0040155C  |.  8D34B6        lea     esi, dword ptr [esi+esi*4]       ;  esi=esi*5
0040155F  |.  29F7          sub     edi, esi                         ;  用edi=2BC-(esi-48/strlen(name))*5
00401561  |.  6BFF 6B       imul    edi, edi, 6B                     ;  edi=edi imul 6B
00401564  |.  81EF 6CCF0000 sub     edi, 0CF6C                       ;  edi=edi-0CF6C
0040156A  |.  81FF 00230000 cmp     edi, 2300                        ;  edi必须>190<2300

这一段程序是利用用户名长度和给定的2BC,30,48算出一个值,这个值必须在190h-2300h之间,经过试验用户名长度应该在4-8之间。
继续F8
0040157E  |> \8D85 00FFFFFF lea     eax, dword ptr [ebp-100]         ;  eax指向name
00401584  |.  50            push    eax                              ;  name strlen(name)入栈
00401585  |.  53            push    ebx
00401586  |.  FF75 08       push    dword ptr [ebp+8]
00401589  |.  E8 77FDFFFF   call    00401305
0040158E  |.  83C4 0C       add     esp, 0C
00401591  |.  09C0          or      eax, eax
00401593  |.  74 03         je      short 00401598
00401595  |.  31C0          xor     eax, eax
00401597  |.  40            inc     eax
00401598  |>  5F            pop     edi
00401599  |.  5E            pop     esi
0040159A  |.  5B            pop     ebx
0040159B  |.  C9            leave
0040159C  \.  C3            retn

这里只有一个CALL 之后程序会返回,故先在CALL 那里F7跟进
00401305  /$  55            push    ebp
00401306  |.  89E5          mov     ebp, esp
00401308  |.  81EC 2C040000 sub     esp, 42C                         ;  分配空间
0040130E  |.  53            push    ebx
0040130F  |.  56            push    esi
来到这里后一路F8,之间的rep命令一直没搞懂他在干嘛,影响不大。
F8走到
0040139E  |.  68 00010000   push    100                                   ; /Count = 100 (256.)
004013A3  |.  8D85 E1FCFFFF lea     eax, dword ptr [ebp-31F]              ; |
004013A9  |.  50            push    eax                                   ; |Buffer
004013AA  |.  6A 66         push    66                                    ; |ControlID = 66 (102.)
004013AC  |.  FF75 08       push    dword ptr [ebp+8]                     ; |hWnd
004013AF  |.  E8 84030000   call    <jmp.&USER32.GetDlgItemTextA>         ; \GetDlgItemTextA
004013B4  |.  09C0          or      eax, eax                              ;  strlen(code)!=0

在此调用GetDlgItemTextA获得输入的假码,若没有输入则失败

F8
004013B4  |.  09C0          or      eax, eax                              ;  strlen(code)!=0
004013B6  |.  0F84 48010000 je      00401504
004013BC  |.  B8 CF110000   mov     eax, 11CF
004013C1  |.  0FB68D E1FCFF>movzx   ecx, byte ptr [ebp-31F]               ;  ecx=code[0]
004013C8  |.  99            cdq
004013C9  |.  F7F9          idiv    ecx                                   ;  eax=eax/code[0],edx=eax%code[0]
004013CB  |.  83FA 17       cmp     edx, 17                               ;  (11CF+17)%code[0]==0 否则会挂 程序中生成的中间码 从以后的跟踪可以得到注册码第一位为T 可以采用
004013CE  |.  74 07         je      short 004013D7
主要是根据JE之后的JMP一下跳过了下面所有的代码,所以这里把JE改为JMP方便调试

F8来到这里
004013DB  |> /8B45 10       /mov     eax, dword ptr [ebp+10]
004013DE  |. |0FBE0418      |movsx   eax, byte ptr [eax+ebx]              ;  取name[i] i=ebx
004013E2  |. |0145 FC       |add     dword ptr [ebp-4], eax               ;  将sum_name存入[ebp-4]
004013E5  |. |43            |inc     ebx                                  ;  i++
004013E6  |> |3B5D 0C        cmp     ebx, dword ptr [ebp+C]               ;  ebx<strlen(name)?
004013E9  |.^\7C F0         \jl      short 004013DB                       ;  小于则跳转
将name[i]每一位的ASCII码相加。
F8来到这里
004013F2  |> /8B55 10       /mov     edx, dword ptr [ebp+10]              ;  edx指向name
004013F5  |. |0FBE3C1A      |movsx   edi, byte ptr [edx+ebx]              ;  edi=name[i]  i=ebx
004013F9  |. |8B75 FC       |mov     esi, dword ptr [ebp-4]               ;  esi=sum_name
004013FC  |. |89D9          |mov     ecx, ebx                             ;  ecx=i
004013FE  |. |C1E1 02       |shl     ecx, 2                               ;  ecx*4
00401401  |. |89DA          |mov     edx, ebx                             ;  i=ebx  j=i=ebx
00401403  |. |42            |inc     edx                                  ;  j=i+1
00401404  |. |29D1          |sub     ecx, edx                             ;  i*4-i-1=i*3-1
00401406  |. |0FB68C0D E1FE>|movzx   ecx, byte ptr [ebp+ecx-11F]          ;  ecx=存好的一组表
0040140E  |. |89FA          |mov     edx, edi                             ;  edx=name[i]
00401410  |. |31CA          |xor     edx, ecx                             ;  edx^table[i]=0
00401412  |. |89F1          |mov     ecx, esi                             ;  ecx=sum_name
00401414  |. |0FAFCB        |imul    ecx, ebx                             ;  ecx=sum_name*i
00401417  |. |29F1          |sub     ecx, esi                             ;  ecx=sum_name*i-sum_name
00401419  |. |89CE          |mov     esi, ecx                             ;  esi=sum_name*i-sum_name
0040141B  |. |83F6 FF       |xor     esi, FFFFFFFF                        ;  esi^=FFFFFFFF
0040141E  |. |8DB432 4D0100>|lea     esi, dword ptr [edx+esi+14D]         ;  esi=esi+edx+14D
00401425  |. |8B4D 0C       |mov     ecx, dword ptr [ebp+C]               ;  ecx=strlen(name)
00401428  |. |89DA          |mov     edx, ebx                             ;  edx=i
0040142A  |. |83C2 03       |add     edx, 3                               ;  i+=3
0040142D  |. |0FAFCA        |imul    ecx, edx                             ;  ecx=strlen(name)*(i+3)
00401430  |. |0FAFCF        |imul    ecx, edi                             ;  ecx=strlen(name)*(i+3)*name[i]
00401433  |. |89F0          |mov     eax, esi                             ;  esi见前面
00401435  |. |01C8          |add     eax, ecx                             ;  eax=esi+strlen(name)*(i+3)*name[i]
00401437  |. |B9 0A000000   |mov     ecx, 0A
0040143C  |. |31D2          |xor     edx, edx
0040143E  |. |F7F1          |div     ecx                                  ;  eax=eax/0A,edx=eax%0A
00401440  |. |83C2 30       |add     edx, 30                              ;  edx=eax%0A+30
00401443  |. |88941D FCFEFF>|mov     byte ptr [ebp+ebx-104], dl           ;  mov  ebp+i-104,dl
0040144A  |. |0FB6BC1D FCFE>|movzx   edi, byte ptr [ebp+ebx-104]          ;  edi=dl
00401452  |. |81F7 ACAD0000 |xor     edi, 0ADAC                           ;  edi=dl^=0ADAC
00401458  |. |89DE          |mov     esi, ebx                             ;  esi=i
0040145A  |. |83C6 02       |add     esi, 2                               ;  esi=i+2
0040145D  |. |89F8          |mov     eax, edi                             ;  eax=dl^=0ADAC
0040145F  |. |0FAFC6        |imul    eax, esi                             ;  eax=eax*(i+2)
00401462  |. |B9 0A000000   |mov     ecx, 0A                              ;  ecx=0A
00401467  |. |99            |cdq
00401468  |. |F7F9          |idiv    ecx                                  ;  eax=eax/0A,edx=eax%0A
0040146A  |. |83C2 30       |add     edx, 30                              ;  edx+=30
0040146D  |. |88941D FCFEFF>|mov     byte ptr [ebp+ebx-104], dl           ;  dl->ebp+i-104
00401474  |. |43            |inc     ebx
00401475  |> |3B5D 0C        cmp     ebx, dword ptr [ebp+C]               ;  ebx<strlen(name)?
00401478  |.^\0F8C 74FFFFFF \jl      004013F2
0040147E  |.  8D85 FCFEFFFF lea     eax, dword ptr [ebp-104]              ;  eax记录算出来的一个码cname
这段程序是利用用户名的长度和用户名本身通过一系列计算(已经写在注释中),得到一个字符串,记为cname
看00401406  |. |0FB68C0D E1FE>|movzx   ecx, byte ptr [ebp+ecx-11F]   
这一行  其中大的ECX为保存好的一组计算时候需要的表,可以在堆栈窗口中翻页找到这个地址中的内容,而且由上一行代码,ecx每次增加3,所以每隔三个取一个字节即可,因为用户名位数不能过多,所以姑且取10个字节吧,
00401484  |.  50            push    eax                                   ;  参数
00401485  |.  6A 54         push    54
00401487  |.  8D85 DCFBFFFF lea     eax, dword ptr [ebp-424]              ;  格式。。eax54
0040148D  |.  50            push    eax                                   ; |Format
0040148E  |.  8D85 E1FBFFFF lea     eax, dword ptr [ebp-41F]              ; |
00401494  |.  50            push    eax                                   ; |s
00401495  |.  E8 CE020000   call    <jmp.&USER32.wsprintfA>               ; \wsprintfA
0040149A  |.  8B7D 0C       mov     edi, dword ptr [ebp+C]                ;  strlen(name)
0040149D  |.  89F8          mov     eax, edi
0040149F  |.  0FAF45 FC     imul    eax, dword ptr [ebp-4]                ;  eax=strlen(name)*sum(name[i])
004014A3  |.  B9 64000000   mov     ecx, 64
004014A8  |.  99            cdq
004014A9  |.  F7F9          idiv    ecx                                   ;  eax=strlen(name)*sum(name[i])/64,edx=...%64
004014AB  |.  89D7          mov     edi, edx
004014AD  |.  83C7 30       add     edi, 30                               ;  edi=strlen(name)*sum(name[i])%64+30
004014B0  |.  57            push    edi
004014B1  |.  8DBD E1FBFFFF lea     edi, dword ptr [ebp-41F]              ;  edi->算出的字符串
004014B7  |.  57            push    edi
004014B8  |.  8DBD D6FBFFFF lea     edi, dword ptr [ebp-42A]              ;  格式 %s-%d
004014BE  |.  57            push    edi                                   ; |Format
004014BF  |.  8DBD E1FDFFFF lea     edi, dword ptr [ebp-21F]              ; |
004014C5  |.  57            push    edi                                   ; |s
004014C6  |.  E8 9D020000   call    <jmp.&USER32.wsprintfA>               ; \wsprintfA

这里,是利用wsprintf为cname前面加一个T(54H),后面加"-"和一个算出来的数字
得到cname1字符串
004014D7  |> /40            /inc     eax                                  ;  eax做为计数器i 循环检测算出的string 是否为有空字符
004014D8  |. |803C01 00     |cmp     byte ptr [ecx+eax], 0
004014DC  |.^\75 F9         \jnz     short 004014D7
004014DE  |.  50            push    eax                                   ;  eax为字符串长度
004014DF  |.  8D85 E1FCFFFF lea     eax, dword ptr [ebp-31F]              ;  eax->code
这里得到cname1的长度
004014DE  |.  50            push    eax                                   ;  eax为字符串长度
004014DF  |.  8D85 E1FCFFFF lea     eax, dword ptr [ebp-31F]              ;  eax->code
004014E5  |.  50            push    eax
004014E6  |.  8D85 E1FDFFFF lea     eax, dword ptr [ebp-21F]
004014EC  |.  50            push    eax
004014ED  |.  E8 D0FDFFFF   call    004012C2

PUSH将cname1的长度cname1和假码入栈,下面的CALL应该就是比较的CALL F7
跟进
004012C8  |.  8B5D 10       mov     ebx, dword ptr [ebp+10]               ;  ebx=strlen(cname)
004012CB  |.  31F6          xor     esi, esi
004012CD  |.  46            inc     esi
004012CE  |.  EB 29         jmp     short 004012F9
004012D0  |>  8B55 08       /mov     edx, dword ptr [ebp+8]
004012D3  |.  0FBE3C32      |movsx   edi, byte ptr [edx+esi]              ;  edi=cname[i] i初始值为1
004012D7  |.  89F8          |mov     eax, edi
004012D9  |.  83F0 20       |xor     eax, 20                              ;  eax=cname[i]^20
004012DC  |.  B9 0A000000   |mov     ecx, 0A
004012E1  |.  99            |cdq
004012E2  |.  F7F9          |idiv    ecx
004012E4  |.  89D7          |mov     edi, edx                             ;  edx=cname[i]^20%0A
004012E6  |.  83C7 30       |add     edi, 30                              ;  edi=cname[i]^20%0A+30
004012E9  |.  8B55 0C       |mov     edx, dword ptr [ebp+C]
004012EC  |.  0FBE1432      |movsx   edx, byte ptr [edx+esi]              ;  edx=code[i]  i初始值为1
004012F0  |.  39D7          |cmp     edi, edx
004012F2      74 04         je      short 004012F8                        ;  这里就是比较的关键  改为jmp即可爆破
004012F4  |.  31C0          |xor     eax, eax
004012F6  |.  EB 08         |jmp     short 00401300
004012F8  |>  46            |inc     esi
004012F9  |>  39DE           cmp     esi, ebx                             ;  esi<strlen(cname)?
004012FB  |.^ 7C D3         \jl      short 004012D0

循环部分便是比较的算法,看得出作者是分别取cname1的第2到第倒数第二个字符经过一系列算法算出一个值然后与输入的假码的每一位比较,只要有一次不相等,便会失败。
到此算法分析完毕。

PS:将004013B6处的je改为JMP 这样只要输入的注册码不为空即可爆破。

这个可以改为JMP后跟踪来到
00401589  |.  E8 77FDFFFF   call    00401305

0040158E  |.  83C4 0C       add     esp, 0C
00401591  |.  09C0          or      eax, eax
00401593  |.  74 03         je      short 00401598
00401595  |.  31C0          xor     eax, eax
00401597  |.  40            inc     eax
00401598  |>  5F            pop     edi
00401599  |.  5E            pop     esi
0040159A  |.  5B            pop     ebx
0040159B  |.  C9            leave
0040159C  \.  C3            retn

这是算法的最后部分,上面的CALL是关键算法。只要出来的EAX不为0则可以注册成功,而现在的EAX保存的是我们输入的假码长度,只要不为0即可注册成功。换句话说 可以把
00401593改为NOP即可爆破。
下来是注册机的制作,既然已经分析出了算法,注册机的制作应该不太难了。在注释的时候我尽量用了C语言的描述。
以上貌似我已经说了很多的废话了,故只将注册机和CrackMe打包,里面有MFC的源码。算法如下
VC++下调试。。
        byte table[]={0x00,0x43,0x46,0x49,0x4C,0x4F,0x52,0x55,0x58,0x00};
        char name[20];
        char cname[20];
        char cname1[20];
        char code[20];
        char reg[20];
        int slen,sum_name=0;
        int temp1,temp2;
        slen=GetDlgItemText(IDC_EDIT1,name,20);
        if(slen<4||slen>8){
                MessageBoxEx(NULL,TEXT("The length must no less than 4 and no more than 8!"),
                            TEXT("Error!"),MB_OK,0);
                goto end;}
        for(int i=0;name[i]!='\0';i++)
        {
                sum_name+=int(name[i]);
        }

        for(int i=0;i<slen;i++)
        {
                temp2=(int)name[i]^table[i];
                temp1=(sum_name*i-sum_name)^0xFFFFFFFF;
                temp1=temp1+temp2+0x14D+slen*(i+3)*int(name[i]);
                temp1=byte(temp1%0xA+0x30)^0xADAC;
                temp1*=(i+2);
                cname[i]=byte((temp1%0xA)+0x30);
               
        }
        cname[slen]='\0';
        int cname_d=(slen*sum_name)%0x64+0x30;
        wsprintf(cname1,"%s-%d",cname,cname_d);
        slen=strlen(cname1);
        int i;
        for( i=0;i<slen;i++)
        {
                reg[i]=((int)cname1[i]^0x20)%0xA+0x30;
        }
        reg[i]='\0';
        wsprintf(code,"%c%s",'T',reg);
        SetDlgItemText(IDC_EDIT2,code);
end:;

P.S. 初次分析,如果有错,请大家指证,共同进步。


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

上传的附件:
收藏
免费 7
支持
分享
最新回复 (9)
雪    币: 166
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
不知怎么的,,上传不了附件了。。附件中无源码 不好意思。。。可以看帖出来的算法
2010-11-10 14:14
0
雪    币: 206
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
自己把这个看完了,并写了注册机,很有收获
2010-11-19 09:27
0
雪    币: 166
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
恩,我也是第一次写,大家一起进步。
2010-11-28 15:17
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
照着楼主的思路 成功写了一个注册机 哈哈
2010-11-29 20:00
0
雪    币: 196
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
这个CM看过了,总体上难度不大~~~,主要有耐性~~~,LZ继续加油~~
2010-11-29 21:00
0
雪    币: 65
活跃值: (118)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
7
膜拜楼主 看看算法
2010-11-30 15:20
0
雪    币: 322
活跃值: (113)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
2010.12.2 加个标记,回家慢慢看
2010-12-2 19:34
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
下了研究一下
2010-12-3 13:06
0
雪    币: 21
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
非常感谢楼主的思路,正在学习中
2010-12-11 17:19
0
游客
登录 | 注册 方可回帖
返回
//