首页
社区
课程
招聘
[原创]本人原创CRACKME系列 难度由0--9,看你属于哪一级? (更新CRACKME 7的程序实现,附源码)
发表于: 2009-7-20 17:26 275160

[原创]本人原创CRACKME系列 难度由0--9,看你属于哪一级? (更新CRACKME 7的程序实现,附源码)

2009-7-20 17:26
275160

买回《加密与解密 第三版》已经半个多月了,每天晚上下班都会抽出3个小时来学习,用“拨开云雾”四个字来形容这半个月的感觉最恰当不过,一层一层的知识让我应接不暇,现在才发现原来用了好几年的EXE等文件竟存在着这么多的秘密!  虽然我是做嵌入式系统的,目前软件安全性应用得不多,但是能学到这么多感兴趣的东西,毕竟也是一件幸事,不敢独享,在此与众同乐!
    附件为我周末时用VC++编写的CRECKME系列,由于只有两天时间,目前只完成了前6个难度,即难度0--难度5,我后面会陆续再贴出后面的难度6---难度9,也将陆续贴出源码以及我个人的破解方法,望各位能一起参与,多多指教!!

   本来还想贴出各难度的帮助,但想想还是让大家先试着破解,等过一段时间贴出具体源代码和破解流程时再一起贴出好了!

   PS:由于本人用VC++ MFC 编译,所以如果文件打不开的话,请依照提示下载MFC71.dll等文件,如电脑中有装VC++则无需下载。

发现CRECKME 4程序中漏了一句话,导致与我原来的想法不符,现在补上名称为"CRECKME 4修正版",有兴趣的可以从附件中下载尝试。
CRECKME 6 已经上传! 期待分析和注册码,能爆破也行!



结果:
CRECKME 0:明码,无算法,有字符串可下断点,可爆破,无各项反调试措施,无加壳。    难易程度:最简单。   目的:明码典型性。
CRECKME 1:非明码,算法简单,有字符串可下断点,可爆破,无各项反调试措施,无加壳。    难易程度:简单。    目的:非明码的实现。
CRECKME 2:非明码,算法简单,无字符串和敏感API函数可下断点,可爆破,无各项反调试措施,无加壳。    难易程度:简单。 目的:OllyDbg消息断点或其它断点尝试。
CRECKME 3:非明码,算法简单,无字符串和敏感API函数可下断点,有防爆破,无各项反调试措施,无加壳。    难易程度:简单。 目的:尝试防爆破。
CRECKME 4,CRECKME 4修正版:Cyane已爆破,但尚未能得出注册码。 等待注册码后贴解析和源代码。期待中……
CRECKME 5,6:已有爆破,但尚未有注册码。期待中……
CRECKME 7:由于CRECKME 5,6用了别人的VM PROTECT,导致某些人的反感,我这里再次诚挚道歉,并感谢你们的关注。 CRECKME 7中不再使用VM,但有成熟算法和其它一些反调试的手段,代码里也有自己的一些思路,虽然很多是前人走过的,但毕竟也有自己的想法和创新,这个CRECKME花了我几天时间,感谢你们的继续关注!

CRACKME 7实现流程和源码在编程模块已详细说明,有兴趣的可以看以下链接:http://bbs.pediy.com/showthread.php?p=663830#post663830, 特别感谢sessiondiy的提醒和关注!



----------------------------------------------------------------------------------------------------------------------------------------CRECKME 0 解析(初次解析,请多多指教):
工具:OllyDbg,
查壳:peid,
1,查壳显示为Microsoft Visual C++ 7.0 Method2 [Debug],可知无加壳(当然,一些壳也能伪装这些信息,以OllyDbg入口点为准)。
2,调试MFC程序,需先倒入MFC的LIB文件,否则MFC里面的函数将无法解析。deg--->select import libraries--->MFC42.LIB,MFC71.LIB  ,然后点击process.
3,通过测试,我们知道有字符串提醒,因此可以用字符串下断点,用OllyDbg打开程序后,OPlugins---->Ultra String Reference -->Find AScill,找到Congratulation! Correct Serial Num,do next one,双击查看,即可找到验证代码处,我在004015A0   .  56            push    esi 行处下断点。
4,按F9运行代码,在NAME和SERIAL中随便输入,我这里输入为NAME:zenghw,  SERIAL:5705312 (个人习惯)。点确定,程序中断。
5,以下内容中,//后面内容即为我的注释
004015A0   .  56            push    esi
004015A1   .  57            push    edi
004015A2   .  6A 01         push    1                                           ;  //UpdateDate的参数,为1,即为TRUE
004015A4   .  8BF1          mov     esi, ecx
004015A6   .  E8 6B030000   call    <jmp.&MFC71.#6236_CWnd::UpdateData>         ;  //UpdateDate,结合前面的参数TRUE,可知为把对话框中的内容保存起来
004015AB   .  8D7E 74       lea     edi, dword ptr [esi+74]                     ;  //由信息窗口可知ptr[esi+74]为地址0013FEF8,再由右下角堆栈串口可知0013FEF8为我们输入的假NAME,我这里为zenghw
004015AE   .  8BCF          mov     ecx, edi                                    ;  //把我们输入的假NAME zenghw 赋给 ecx
004015B0   .  FF15 D0314000 call    dword ptr [<&MFC71.#2902_ATL::CSimpleString>;  MFC71.ATL::CSimpleStringT<wchar_t,1>::GetLength
004015B6   .  83F8 06       cmp     eax, 6                                      ;  //在上面跟入可以很明显的知道eax即为返回的string长度值,即假NAME zenghw的长度值
004015B9   .  7D 1A         jge     short 004015D5                              ;  //如果长度值>=6的话就跳,否则往下跑
004015BB   .  817E 78 A0860>cmp     dword ptr [esi+78], 186A0
004015C2   .  7D 11         jge     short 004015D5
004015C4   .  6A 00         push    0
004015C6   .  6A 00         push    0
004015C8   .  68 8C394000   push    0040398C                                    ;  name or serial is too short!
004015CD   .  E8 3E030000   call    <jmp.&MFC71.#1123_AfxMessageBox>
004015D2   >  5F            pop     edi
004015D3   .  5E            pop     esi
004015D4   .  C3            retn
004015D5   >  68 78394000   push    00403978                                    ;  //把字符串"indolentafternoon"推进栈
004015DA   .  8BCF          mov     ecx, edi                                    ;  //从信息窗口和堆栈窗口可知,edi即为假NAME
004015DC   .  FF15 C0314000 call    dword ptr [<&MFC71.#1482_ATL::CStringT<char>;  //Compare,跟进去可知为比较函数,其实看名字不跟也知道大概结果
004015E2   .  85C0          test    eax, eax                                    ;  //eax为返回值,如果相等为0,不等为1
004015E4   .^ 75 EC         jnz     short 004015D2
004015E6   .  817E 78 D7C75>cmp     dword ptr [esi+78], 56C7D7                  ;  //把假SERIAL 5705312 与0X56C7D7比较,即十进制5687255
004015ED   .^ 75 E3         jnz     short 004015D2                              ;  //如果不等的话就跳走
004015EF   .  6A 00         push    0
004015F1   .  6A 00         push    0
004015F3   .  68 44394000   push    00403944                                    ;  congratulation! correct serial num,do next one? :)
004015F8   .  E8 13030000   call    <jmp.&MFC71.#1123_AfxMessageBox>
004015FD   .  8B06          mov     eax, dword ptr [esi]
004015FF   .  5F            pop     edi
00401600   .  8BCE          mov     ecx, esi
00401602   .  5E            pop     esi

再附上我VC里的代码:
{
        // TODO: 在此添加控件通知处理程序代码

        UpdateData(true);

        if(m_Name.GetLength()<6 &&  m_Serial< 100000)
        {
                AfxMessageBox("Name or Serial is too short!");
                return;
        }

        if(m_Name == "IndolentAfternoon" && m_Serial == 5687255)
        {
                AfxMessageBox("Congratulation! Correct Serial Num,do next one? :)");
                OnOK();
        }
        else
                return;
}

CREAKME 0 内容是最简单最初步的,很多程序员也犯这样的错误:明码,不用算法,没有各种防范措施,就直接比较,相同则注册成功。

明天继续贴CREME 1,谢谢大家关注。
------------------------------------------------------------------------------------------------------------------------------------
继续贴CRECKME 1,
1,还是用字符串下断点。运行后,我输入NAME为zenghw,SERIAL为5705312.
2,分析代码:
004013E8   .  55            push    ebp
004013E9   .  56            push    esi
004013EA   .  57            push    edi
004013EB   .  33FF          xor     edi, edi
004013ED   .  6A 01         push    1                                ;  //UpdateDate的参数,为1
004013EF   .  8BF1          mov     esi, ecx
004013F1   .  897C24 18     mov     dword ptr [esp+18], edi
004013F5   .  E8 A2040000   call    <jmp.&MFC71.#6236_CWnd::UpdateDa>;  //UpdateData函数,参数为1时,表示把当前控件内容更新入变量中
004013FA   .  8D6E 78       lea     ebp, dword ptr [esi+78]          ;  //由信息窗口可知ptr[esi+78]为地址0013FEF8,再由右下角堆栈串口可知0013FEF8为我们输入的假NAME,我这里为zenghw,在command中输入D 0013FEF8为什么不能显示?
004013FD   .  8BCD          mov     ecx, ebp
004013FF   .  896C24 18     mov     dword ptr [esp+18], ebp          ;  //把让ptr[esp+18]指针指向假NAME :zenghw
00401403   .  FF15 C0214000 call    dword ptr [<&MFC71.#2902_ATL::CS>;  //获得假NAME的输入长度
00401409   .  83F8 06       cmp     eax, 6
0040140C   .  0F8C 71010000 jl      00401583                         ;   //如果输入长度<6,就跳向失败
00401412   .  83C6 7C       add     esi, 7C                          ;  //做什么用?给参数分配空间吧?
00401415   .  8BCE          mov     ecx, esi                         ;  //由信心窗口可知ESI地址为0013FEFC,由堆栈窗口可知其为假SERIAL:5705312
00401417   .  FF15 C0214000 call    dword ptr [<&MFC71.#2902_ATL::CS>;  //获得假SERIAL的输入长度
0040141D   .  83F8 06       cmp     eax, 6
00401420   .  0F8C 5D010000 jl      00401583                         ;   //如果输入长度<6,就跳向失败
00401426   .  53            push    ebx
00401427   .  8BCE          mov     ecx, esi                         ;  //由上面分析知道ESI为假SERIAL:5705312
00401429   .  897C24 14     mov     dword ptr [esp+14], edi
0040142D   .  FF15 C0214000 call    dword ptr [<&MFC71.#2902_ATL::CS>;  MFC71.ATL::CSimpleStringT<wchar_t,1>::GetLength
00401433   .  85C0          test    eax, eax
00401435   .  7E 4E         jle     short 00401485
00401437   >  6A 01         push    1                                ;  //由后面分析,可知其为mid函数的第二个参数,nCount
00401439   .  8D043F        lea     eax, dword ptr [edi+edi]         ;  //相当于edi x 2
0040143C   .  50            push    eax                              ;  //为mid第一个参数,nFirst,
0040143D   .  8D4C24 20     lea     ecx, dword ptr [esp+20]
00401441   .  51            push    ecx
00401442   .  8BCE          mov     ecx, esi                         ;  //ECX为Mid函数的句柄,如string.mid(0,1)表示从string中从第0个字符开始,提取1个字符
00401444   .  FF15 BC214000 call    dword ptr [<&MFC71.#4109_ATL::CS>;  //由注释知为mid函数,其原型大概为CString Mid( int nFirst, int nCount ),再往回看,可知1为第二个参数,eax为第一个参数
0040144A   .  8BC8          mov     ecx, eax
0040144C   .  FF15 B8214000 call    dword ptr [<&MFC71.#876_ATL::CSi>;  MFC71._CIP<IMoniker,&IID_IMoniker>::operator IMoniker *
00401452   .  50            push    eax                              ; ///eax即为上面mid函数得回的返回值
00401453   .  FF15 B0224000 call    dword ptr [<&MSVCR71.atoi>]      ; \//atoi 函数,int atoi(const char *nptr); 即把字符指针转换为整数,返回其转换后的值
00401459   .  83C4 04       add     esp, 4
0040145C   .  8D4C24 18     lea     ecx, dword ptr [esp+18]
00401460   .  8BE8          mov     ebp, eax                         ;  //eax即为上面mid函数得回的返回值
00401462   .  FF15 68204000 call    dword ptr [<&MFC71.#578_ATL::CSt>;  MFC71.ATL::CSimpleStringT<char,1>::~CSimpleStringT<char,1>
00401468   .  8B5C24 14     mov     ebx, dword ptr [esp+14]
0040146C   .  03DD          add     ebx, ebp                         ;  //有上面分析可知,ebp即为mid函数的返回值
0040146E   .  8BCE          mov     ecx, esi                         ;  //ESI为假SERIAL 5705312,由后面分析可知,赋给ecx是为了得到它的长度
00401470   .  895C24 14     mov     dword ptr [esp+14], ebx          ;  //从00401468,0040146C两行可以知道,ebx为ebx+dword ptr [esp+14]的值,在从本句,可知其为一个累加
00401474   .  47            inc     edi                              ;  //edi加1,可以推测edi为控制累加次数
00401475   .  FF15 C0214000 call    dword ptr [<&MFC71.#2902_ATL::CS>;  //得到假SERIAL 5705312的长度,跟进去知道其用到ecx
0040147B   .  3BF8          cmp     edi, eax                         ;  //EDI 与 假SERIAL 570531的长度比较,如果小于假SERIAL长度,就跳
0040147D   .^ 7C B8         jl      short 00401437                   ;  //从这个JL可推知,这是一个循环,循环以与假码长度的比较为结束,得到累加值赋给ptr [esp+14]
0040147F   .  8B6C24 1C     mov     ebp, dword ptr [esp+1C]          ;  //由信息窗口可知ptr[esp+1c]为地址0013FEF8,再由右下角堆栈串口可知0013FEF8为我们输入的假NAME:zenghw
00401483   .  33FF          xor     edi, edi                         ;  //edi清0
00401485   >  6A 01         push    1
00401487   .  57            push    edi
00401488   .  8D5424 2C     lea     edx, dword ptr [esp+2C]
0040148C   .  52            push    edx
0040148D   .  8BCE          mov     ecx, esi                         ;  //有上面的分析或从堆栈窗口可知esi为假SERIAL:5705312
0040148F   .  FF15 BC214000 call    dword ptr [<&MFC71.#4109_ATL::CS>;  //仍旧为mid函数,由参数可知为SERIAL.MID(0,1)
00401495   .  BB 01000000   mov     ebx, 1
0040149A   .  68 8C264000   push    0040268C                         ;  5
0040149F   .  8BC8          mov     ecx, eax
004014A1   .  897C24 34     mov     dword ptr [esp+34], edi
004014A5   .  895C24 1C     mov     dword ptr [esp+1C], ebx
004014A9   .  FF15 84204000 call    dword ptr [<&MFC71.#1482_ATL::CS>;  //跟进去看看,可知为为字符SERIAL.MID(0,1)与‘5’比较,返回值为eax.
004014AF   .  85C0          test    eax, eax                         ;  //eax即为返回的比较是否相等的标记位,以上程序意思为取假SERAL的第一位,是否=="5"
004014B1   .  75 64         jnz     short 00401517                   ;  //不等则跳向失败
004014B3   .  6A 02         push    2
004014B5   .  6A 04         push    4
004014B7   .  8D4424 28     lea     eax, dword ptr [esp+28]
004014BB   .  50            push    eax
004014BC   .  8BCE          mov     ecx, esi
004014BE   .  FF15 BC214000 call    dword ptr [<&MFC71.#4109_ATL::CS>;  MFC71.ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char> > >::Mid
004014C4   .  895C24 30     mov     dword ptr [esp+30], ebx
004014C8   .  BB 03000000   mov     ebx, 3
004014CD   .  68 88264000   push    00402688                         ;  31
004014D2   .  8BC8          mov     ecx, eax
004014D4   .  895C24 1C     mov     dword ptr [esp+1C], ebx
004014D8   .  FF15 84204000 call    dword ptr [<&MFC71.#1482_ATL::CS>;  MFC71.ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char> > >::Compare
004014DE   .  85C0          test    eax, eax                         ;  //同样道理,以上程序意思为取假SERAL的第4,5两位,是否=="31'"
004014E0   .  75 35         jnz     short 00401517                   ;  //不等则跳向失败
004014E2   .  6A 01         push    1
004014E4   .  6A 01         push    1
004014E6   .  8D4C24 24     lea     ecx, dword ptr [esp+24]
004014EA   .  51            push    ecx
004014EB   .  8BCE          mov     ecx, esi
004014ED   .  FF15 BC214000 call    dword ptr [<&MFC71.#4109_ATL::CS>;  MFC71.ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char> > >::Mid
004014F3   .  8BC8          mov     ecx, eax
004014F5   .  BB 07000000   mov     ebx, 7
004014FA   .  FF15 B8214000 call    dword ptr [<&MFC71.#876_ATL::CSi>;  MFC71._CIP<IMoniker,&IID_IMoniker>::operator IMoniker *
00401500   .  50            push    eax                              ; /s
00401501   .  FF15 B0224000 call    dword ptr [<&MSVCR71.atoi>]      ; \//同样分析,以上程序意思为:取假SERIAL:5705312的第一位(即为7),并atoi转换为整数,由此函数还可知,SERIAL中需为数字,因为如果为字母的话,经函数转换会为0
00401507   .  8B4C24 18     mov     ecx, dword ptr [esp+18]          ;  //ptr [esp+18]即为刚刚那个循环中计算出来的累加值
0040150B   .  83C4 04       add     esp, 4
0040150E   .  3BC8          cmp     ecx, eax                         ;  //把其累加值和假SERIAL的第一位比较
00401510   .  C64424 13 00  mov     byte ptr [esp+13], 0
00401515   .  74 05         je      short 0040151C                   ;  //上面的比较中,相等则跳
00401517   >  C64424 13 01  mov     byte ptr [esp+13], 1
0040151C   >  F6C3 04       test    bl, 4                            ;  //为什么要test bl,4我搞不清楚,请知道的解释下下面这一小段代码!
0040151F   .  74 0D         je      short 0040152E
00401521   .  8D4C24 1C     lea     ecx, dword ptr [esp+1C]
00401525   .  83E3 FB       and     ebx, FFFFFFFB
00401528   .  FF15 68204000 call    dword ptr [<&MFC71.#578_ATL::CSt>;  MFC71.ATL::CSimpleStringT<char,1>::~CSimpleStringT<char,1>
0040152E   >  F6C3 02       test    bl, 2
00401531   .  74 0D         je      short 00401540
00401533   .  8D4C24 20     lea     ecx, dword ptr [esp+20]
00401537   .  83E3 FD       and     ebx, FFFFFFFD
0040153A   .  FF15 68204000 call    dword ptr [<&MFC71.#578_ATL::CSt>;  MFC71.ATL::CSimpleStringT<char,1>::~CSimpleStringT<char,1>
00401540   >  F6C3 01       test    bl, 1
00401543   .  C74424 30 FFF>mov     dword ptr [esp+30], -1
0040154B   .  5B            pop     ebx
0040154C   .  74 0A         je      short 00401558
0040154E   .  8D4C24 20     lea     ecx, dword ptr [esp+20]
00401552   .  FF15 68204000 call    dword ptr [<&MFC71.#578_ATL::CSt>;  //以上代码为对释放字符串
00401558   >  8A4424 0F     mov     al, byte ptr [esp+F]
0040155C   .  84C0          test    al, al
0040155E   .  57            push    edi
0040155F   .  74 08         je      short 00401569
00401561   .  57            push    edi
00401562   >  68 60264000   push    00402660                         ;  name or serial is wrong,try again !
00401567   .  EB 21         jmp     short 0040158A
00401569   >  68 58264000   push    00402658                         ;  zeng
0040156E   .  8BCD          mov     ecx, ebp
00401570   .  FF15 B4214000 call    dword ptr [<&MFC71.#2272_ATL::CS>;  //Find函数 int Find( LPCTSTR lpszSub ) ,在某字符串中查找另一字符串,并返回在第几位找到
00401576   .  85C0          test    eax, eax                         ;  //eax即为返回值,即在第几位找到其字符串
00401578   .  57            push    edi
00401579   .  57            push    edi
0040157A   .^ 7E E6         jle     short 00401562                   ;  //没找到的话,即跳向失败
0040157C   .  68 10264000   push    00402610                         ;  congratulation !   correct serial number,good job,do next one?   :)
00401581   .  EB 07         jmp     short 0040158A
00401583   >  57            push    edi
00401584   .  57            push    edi
00401585   .  68 F4254000   push    004025F4                         ;  name or serial is too short
0040158A   >  E8 07030000   call    <jmp.&MFC71.#1123_AfxMessageBox>
0040158F   .  8B4C24 24     mov     ecx, dword ptr [esp+24]
00401593   .  5F            pop     edi
00401594   .  5E            pop     esi
00401595   .  5D            pop     ebp
00401596   .  64:890D 00000>mov     dword ptr fs:[0], ecx
0040159D   .  83C4 24       add     esp, 24
004015A0   .  C3            retn

再贴上程序中对应代码:
        UpdateData(true);
        if(m_sName.GetLength() < 6 || m_sSerial.GetLength() < 6)
        {
                AfxMessageBox(_T("NAME or SERIAL is too short"));
                return;
        }
        int isum=0,itemp1,itemp2,itemp3;
        for(int i=0;i<m_sSerial.GetLength();i++)
        {
                itemp1 = atoi(m_sSerial.Mid(2*i,1));
                isum += itemp1;
        }
        if(m_sSerial.Mid(0,1) != "5" || m_sSerial.Mid(4,2) != "31" || isum != atoi(m_sSerial.Mid(1,1)))
        {
                AfxMessageBox(_T("NAME or Serial is wrong,Try again !"));
                return;
        }
        if(m_sName.Find("ZENG")>0 )//&& m_sSerial.Mid(3,1) ==itemp1)
        {
                AfxMessageBox(_T("CONGRATULATION !   CORRECT SERIAL NUMBER,GOOD JOB,DO NEXT ONE?   :)"));

        }else
        {
                AfxMessageBox(_T("NAME or Serial is wrong,Try again !"));
        }
                return;

CRECKME 1较CRECKME 0有一点不同就是代码中已经没有出现明码,但是算法仍然不强,且没有防爆破,字符串提醒无加密容易被下断等缺点。

------------------------------------------------------------------------------------------------------------------------------------
继续贴CRECKME 2,

1,用插件查看是否有敏感字符串可下断,无。  ctrl+N 查看,MFC函数太多,对用bpx getwindowitem等敏感API函数下段并无作用。
2,消息下段。F9运行程序,出现注册对话框,切回OllyDbg,点快捷菜单中的W,出现控件窗口,选择到“确定”行--->右键--->选择message breakpoint on classproc--->messages中选择到202 WM_LBOTTONUP,确定。   在注册对话框中输入假NAME:zenghw ,假SERIAL: 5705312,点确定,此时OllyDbg被中断,按ALT+M,调出内存窗口,选择到如下一行,并按下F2,
然后再按F9让程序运行,可见程序运行后又马上中断在CRECKME中,按F8单步走,但是否真正运行在CRECKME 2空间中,如不是,再调出内存窗口,重新下断点,按F9运行,如此反复,知道单步走时的确运行在CRECKME 2空间中,这个时候仔细查找,可以发现已到校验代码处,注意那个函数updatadate(当然了,别人程序不一定有这个函数)。
注:用消息断点跟TRACE跟踪跟方便,请参考http://bbs.pediy.com/showthread.php?t=67866&highlight=OLLYDBG
3,代码分析:
00401581  |.  6A 01         push    1
00401583  |.  8BCB          mov     ecx, ebx
00401585  |.  AA            stos    byte ptr es:[edi]
00401586  |.  E8 CB030000   call    <jmp.&MFC71.#6236_CWnd::Upd>
0040158B  |.  8D4B 74       lea     ecx, dword ptr [ebx+74]     ;  //把有信息窗口可知ptr[ebx+74] = 0013FEF8,由堆栈窗口可知为假NAME:zenghw 赋给ecx
0040158E  |.  FF15 9C314000 call    dword ptr [<&MFC71.#876_ATL>;  //把ecx里面的内容赋给eax
00401594  |.  8D5424 10     lea     edx, dword ptr [esp+10]
00401598  |>  8A08          /mov     cl, byte ptr [eax]         ;  //eax派上用场了
0040159A  |.  40            |inc     eax
0040159B  |.  880A          |mov     byte ptr [edx], cl         ;  //ptr [edx] 初地址为0013F788,注意,后面会用到
0040159D  |.  42            |inc     edx
0040159E  |.  84C9          |test    cl, cl                     ;  //当跳转了到字符串末尾时,为0x00,下面语句就不满足
004015A0  |.^ 75 F6         \jnz     short 00401598             ;  //上面这个循环,为把假NAME:zenghw 字符串赋到ptr[edx]地址
004015A2  |.  8A4424 16     mov     al, byte ptr [esp+16]       ;  //由信息窗口可以看出ptr[esp+16]为地址 0013F78E,由上面循环语句可知此为字符串里的第6个字节(从0字节算起)
004015A6  |.  84C0          test    al, al
004015A8  |.  75 50         jnz     short 004015FA              ;  //如果第6个字节不等于0的话,则跳向失败
004015AA  |.  8A5424 15     mov     dl, byte ptr [esp+15]       ;  //字符串的第5个字节
004015AE  |.  84D2          test    dl, dl
004015B0  |.  74 48         je      short 004015FA              ;  //如果第5个字节==0的话,则跳向失败
004015B2  |.  8B43 78       mov     eax, dword ptr [ebx+78]     ;  //由信息窗口知 此地址值为0x00570E60,即为十进制5705312,为假SERIAL
004015B5  |.  3D A0860100   cmp     eax, 186A0                  ;  与十进制100000比较
004015BA  |.  7C 3E         jl      short 004015FA              ;  //如果小于则跳向失败
004015BC  |.  0FBE7424 12   movsx   esi, byte ptr [esp+12]      ;  //假NAME :zenghw 的第2位 n
004015C1  |.  0FBE4C24 11   movsx   ecx, byte ptr [esp+11]      ;  //假NAME :zenghw 的第1位 e
004015C6  |.  0FBE7C24 14   movsx   edi, byte ptr [esp+14]      ;  //假NAME :zenghw 的第4位 h
004015CB  |.  03CE          add     ecx, esi                    ;  //第2位 加 上第1位,结果放在ecx中
004015CD  |.  0FBE7424 10   movsx   esi, byte ptr [esp+10]      ;  //假NAME :zenghw 的第0位 z
004015D2  |.  03CE          add     ecx, esi                    ;  //把上面加出的结果ecx再加上第0位
004015D4  |.  0FBE7424 13   movsx   esi, byte ptr [esp+13]      ;  //假NAME :zenghw 的第3位 g
004015D9  |.  0FBED2        movsx   edx, dl                     ;  //由004015AA行可知,dl即为/假NAME :zenghw 的第5位 w
004015DC  |.  03F7          add     esi, edi                    ;  //第4位加第3位,结果放在 esi中
004015DE  |.  03F2          add     esi, edx                    ;  //把上行相加的结果再加上第5位
004015E0  |.  99            cdq                                 ;  //扩展,把edx扩展为eax的高位,也就是说变为64位
004015E1  |.  BF E8030000   mov     edi, 3E8                    ;  //0X3E8即为1000
004015E6  |.  F7FF          idiv    edi                         ;  //edx eax /edi  注意右边寄存器的变化,此时edx eax为00570E60,即为假SERIAL:5705312
004015E8  |.  3BC8          cmp     ecx, eax                    ;  //除完的结果,商放在eax,余数放在edx,这里把除得的商与ecx比较。往回看,ecx即为假NAME第0,1,2位之和,这里显示为0X14D,即为333,把SERIAL改为333312(因为上面是/1000的商,所以333后面只跟3位),重来
004015EA  |.  75 0E         jnz     short 004015FA
004015EC  |.  3BF2          cmp     esi, edx                    ;  //余数与esi比较,esi即为上面假NAME 3,4,5位的和,这里显示值为 0x146,即326,因此把SERIAL改为333326,重来,测试通过
004015EE  |.  75 0A         jnz     short 004015FA
004015F0  |.  8B03          mov     eax, dword ptr [ebx]
004015F2  |.  8BCB          mov     ecx, ebx
004015F4  |.  FF90 54010000 call    dword ptr [eax+154]
004015FA  |>  8B8C24 940000>mov     ecx, dword ptr [esp+94]
00401601  |.  E8 43040000   call    00401A49
00401606  |.  5F            pop     edi
00401607  |.  5E            pop     esi
00401608  |.  5B            pop     ebx
00401609  |.  8BE5          mov     esp, ebp
0040160B  |.  5D            pop     ebp
0040160C  \.  C3            retn

附上VC上校验的源代码:
        char cTmep[128] = {0xFF};
        int iCount1 =0,iCount2 =0;
        UpdateData(true);
        int i =0;
              _tcscpy(cTmep, m_Name);       
              if(cTmep[6] != 0x00 || cTmep[5] == 0x00) //用于判断长度,故意不用你API的GETLENGTH
           return;
        if(m_Serial< 100000)//用于判断长度
                return;
        for(i=0;i< 3;i++)
                iCount1 += cTmep[i];       
        for(i= 3;i< 6;i++)
                iCount2 += cTmep[i];       
        if(iCount1 == m_Serial/1000 && iCount2 == (m_Serial % 1000))//随便算法
        {
                OnOK();
        }
        else
                return;

CRECKME 2较CRECKME 1有一点不同就是代码中不用字符串提醒,但是算法仍然偏简单,且没有防爆破,在004015E8 行等处直接用jmp语句或nop语句或更改为jz,都可以直接爆破。

------------------------------------------------------------------------------------------------------------------------------------
继续贴CRECKME 3,


CRECKME 3 较CRECKME 2只是在内存上多做了校验以防止爆破,算法依然简单,因此,在这里就不多讲解了,主要实践下ollydbg的万能断点的下法!
1,用ollydbg加载CRECKME 3.按F9 运行,输入NAME:zenghw,SERIAL:5705312,先不点确定.
2,切回ollydbg,按快捷键ALT+E,在NAME那列随便找到USER32行,选中,按快捷键CTRL+N;
3,选中TranslateMessage行。如下图,按SHIFT F4,如下图设置:

4,切至注册窗口,按“确定”,此时ollydbg中断。
5,切回ollydbg,按快捷键ALT+M打开内存窗口,选中第一行,按CTRL+B打开搜索窗口,在ASCILL行输入NAME或SERIAL内容,我这里输入5705312,点确定,会搜索到5705312在内存中的位置,选中5705312,右键,选择break point -->memory access,按F9运行,ollydbg会马上再被中断6,此时右边寄存器窗口可看到5705312,按F8单步走,再一直按CTRL+F9知道程序跑回CHECK ME的领空,此时往上找一点点即为验证序列号的代码。

附上源代码:
        char cTmep[128] = {0xFF};
        int iCount1 =2,iCount2 =3;
        UpdateData(true);
        int i =0;
        _tcscpy(cTmep, m_Name);       
               for(int i=0;i<7;i++)
              {
           if(cTmep[i] == _T('0'))       
                return;
                }
                if(cTmep[6] != 0x00 || cTmep[5] == 0x00) //用于判断长度,故意不用你API的GETLENGTH
           return;
        if(m_Serial< 100000)
                return;
        for(i=0;i< 3;i++)
                iCount1 *= cTmep[i];       
        for(i= 3;i< 5;i++)
                iCount2 *= cTmep[i];       
        if(iCount1 == m_Serial/100000 && iCount2 == (m_Serial % 100000))
        {
                OnOK();
        }
        else
                return;
关于防爆破代码的解析由于CRECKME 4也有其内容,因此放在CRECKME 4一起讲。
CRECKME 4修正版的注册码有人解析出来了吗? 期盼中!


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

上传的附件:
收藏
免费 7
支持
分享
最新回复 (185)
雪    币: 218
活跃值: (74)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
fps
2
原创支持哈,菜鸟一个,只能爆破了,,等待你的破解流程。
2009-7-20 18:23
0
雪    币: 418
活跃值: (63)
能力值: ( LV12,RANK:260 )
在线值:
发帖
回帖
粉丝
3
大家都破到哪一级了?
2009-7-20 19:18
0
雪    币: 358
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
第6个还加了vm吗?
2009-7-20 21:01
0
雪    币: 141
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
爆破中 0 1 2 被爆破了 但是1有点怪
上传的附件:
2009-7-20 21:21
0
雪    币: 141
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
第4个还不错 爆破就退出 。。。可能是我搞错了
2009-7-20 21:27
0
雪    币: 358
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
CRECKME 0

name:IndolentAfternoon
sn:5687255  
这是明码

//////////////////////春哥纯爷们,铁血真汉子的分割线//////////////////////////
crackme1

name:ffZENG
sn:580031

这个虽然弄出来了但不知道作者想要表达什么意思

//////////////////////春哥纯爷们,铁血真汉子的分割线//////////////////////////

crackme2
name:fengzi
sn:313330

小学乘法

//////////////////////春哥纯爷们,铁血真汉子的分割线//////////////////////////

crackme3
name:fengzi
sn:226644037698
还是小学乘法
2009-7-20 21:52
0
雪    币: 418
活跃值: (63)
能力值: ( LV12,RANK:260 )
在线值:
发帖
回帖
粉丝
8
疯子鱼加油!!
crackme4,crackme5 赶紧出来!!:)
一会我开始贴crackme0--3的解法,班门弄斧,希望大家多多指教!
2009-7-21 08:56
0
雪    币: 261
活跃值: (83)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
等着你的解法呢?! 最好提一些经验性的、普遍性的东西
2009-7-21 09:08
0
雪    币: 261
活跃值: (83)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
等着你的源码和破解流程,这是个有意义的帖子
2009-7-21 09:10
0
雪    币: 388
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
11
crackme0
NAME:        IndolentAfternoon
SERIAL:        5687255
crackme1
NAME:        ceZENG
SERIAL:        580031
crackme2
NAME:        ceyane
SERIAL:        321308
crackme3
NAME:        ceyane
SERIAL: 241975832010
2009-7-21 12:35
0
雪    币: 388
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
crackme4
NAME:        ceyane
SERIAL:        4111553563
2009-7-21 12:56
0
雪    币: 388
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
确实应该好好看看III

  ╮(╯▽╰)╭   工作太忙了,中午休息抽空答题

        多来看雪 少睡懒觉
2009-7-21 13:09
0
雪    币: 418
活跃值: (63)
能力值: ( LV12,RANK:260 )
在线值:
发帖
回帖
粉丝
14
楼上的crackme4 序列号 好像不对!
2009-7-21 13:09
0
雪    币: 388
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
15
不知道为什么LZ那里没有成功

我这里显示成功
上传的附件:
  • 2.png (6.83kb,8305次下载)
2009-7-21 13:39
0
雪    币: 418
活跃值: (63)
能力值: ( LV12,RANK:260 )
在线值:
发帖
回帖
粉丝
16
CREAKME 4 我做得有点BUG,跟我的初衷不符,我需要更新下,你再来 :)
2009-7-21 16:05
0
雪    币: 388
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
17
昨晚跟了一下 crackme4的算法 有点晕, 最后爆破了.....
crackme5 等着讲解了
2009-7-21 19:03
0
雪    币: 318
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
18
vc比c++builder有天然的优势呀,要是用c++builder写差不多的代码,早就被发现算法了
2009-7-21 19:47
0
雪    币: 418
活跃值: (63)
能力值: ( LV12,RANK:260 )
在线值:
发帖
回帖
粉丝
19
CRECKME 4 修正版 和 CRECKME 5 还没有人出来?
不用推出算法,破解的也可以,有吗?
2009-7-21 23:23
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
这个修改过的4改一点跟踪到一个位置就程序错误退出了。VISTA系统,玩不了。
2009-7-22 10:55
0
雪    币: 418
活跃值: (63)
能力值: ( LV12,RANK:260 )
在线值:
发帖
回帖
粉丝
21
CRECKME 4 非修正 的那个版本也有这个问题吗?
因为这两个版本唯一的差别就是我在其中多加了一句话,后面我可以发源程序给您,VC++ MFC,应该不会有系统兼容问题才是。
2009-7-22 10:59
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yjr
22
楼主,对于creak0来说,在判断用户名和密码的长度时,是不是用户名<6或序列号<1000000都算“Name or Serial is too short”。如果是这样,应该&&改成||
if(m_Name.GetLength()<6 ||  m_Serial< 100000)
  {
    AfxMessageBox("Name or Serial is too short!");
    return;
  }
2009-7-22 11:28
0
雪    币: 388
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
23
有把需要的库文件放到程序目录下吗?
放在一起后运行应该就不会出现你说的问题了
2009-7-22 13:36
0
雪    币: 418
活跃值: (63)
能力值: ( LV12,RANK:260 )
在线值:
发帖
回帖
粉丝
24
你说的没错,这个是我程序的失误,但release已经发出去了,所以我就没改不过来,免得反调试和现实程序不符!
谢谢您的提醒!
2009-7-22 13:42
0
雪    币: 388
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
25
贴一下爆破的crackme4 ,crackme5等看结果了.....
请结合LZ提供的库文件,要不会出错的
用户名必须6位,否则无效哦!
上传的附件:
2009-7-22 16:10
0
游客
登录 | 注册 方可回帖
返回
//