• 破解 “PEDIY CrackMe 2007” 之 k4n


    系统 : Windows xp

    程序 : k4n

    程序下载地址 :http://pan.baidu.com/s/1dEallrb

    要求 : 注册机编写

    使用工具 : IDA Pro & OD

    “PEDIY CrackMe 2007”中关于此程序的破文标题为“一个CRACKME的算法分析”,可在“搜索”标签下查找。

    使用IDA载入程序,根据成功/失败的字符串提示找到关键代码,本程序中关键代码在401085处:

    00401085  |.  6A 66         push    66                               ; /ControlID = 66 (102.)
    00401087  |.  53            push    ebx                              ; |hWnd
    00401088  |.  E8 159C0000   call    <jmp.&USER32.GetDlgItem>         ; GetDlgItem
    0040108D  |.  6A 64         push    64                               ; /Count = 64 (100.)
    0040108F  |.  8D95 48FFFFFF lea     edx, dword ptr [ebp-B8]          ; |
    00401095  |.  52            push    edx                              ; |Buffer
    00401096  |.  50            push    eax                              ; |hWnd
    00401097  |.  E8 129C0000   call    <jmp.&USER32.GetWindowTextA>     ; GetWindowTextA
    0040109C  |.  6A 68         push    68                               ; /ControlID = 68 (104.)
    0040109E  |.  53            push    ebx                              ; |hWnd
    0040109F  |.  E8 FE9B0000   call    <jmp.&USER32.GetDlgItem>         ; GetDlgItem
    004010A4  |.  6A 64         push    64                               ; /Count = 64 (100.)
    004010A6  |.  8D8D E4FEFFFF lea     ecx, dword ptr [ebp-11C]         ; |
    004010AC  |.  51            push    ecx                              ; |Buffer
    004010AD  |.  50            push    eax                              ; |hWnd
    004010AE  |.  E8 FB9B0000   call    <jmp.&USER32.GetWindowTextA>     ; GetWindowTextA
    004010B3  |.  6A 67         push    67                               ; /ControlID = 67 (103.)
    004010B5  |.  53            push    ebx                              ; |hWnd
    004010B6  |.  E8 E79B0000   call    <jmp.&USER32.GetDlgItem>         ; GetDlgItem
    004010BB  |.  8BF0          mov     esi, eax
    004010BD  |.  8D85 48FFFFFF lea     eax, dword ptr [ebp-B8]
    004010C3  |.  50            push    eax
    004010C4  |.  E8 67050000   call    00401630
    004010C9  |.  59            pop     ecx
    004010CA  |.  8945 D8       mov     dword ptr [ebp-28], eax
    004010CD  |.  8D95 E4FEFFFF lea     edx, dword ptr [ebp-11C]
    004010D3  |.  52            push    edx
    004010D4  |.  E8 57050000   call    00401630
    004010D9  |.  59            pop     ecx
    004010DA  |.  68 EAB04000   push    0040B0EA
    004010DF  |.  E8 4C050000   call    00401630
    004010E4  |.  59            pop     ecx
    004010E5  |.  68 0EB14000   push    0040B10E
    004010EA  |.  E8 41050000   call    00401630
    004010EF  |.  59            pop     ecx
    004010F0  |.  837D D8 03    cmp     dword ptr [ebp-28], 3            ;  字符串是否小于等于3
    004010F4  |.  7E 7B         jle     short 00401171                   ;  是则出错
    004010F6  |.  90            nop
    004010F7  |.  90            nop
    004010F8  |.  90            nop
    004010F9  |.  90            nop
    004010FA  |.  33C9          xor     ecx, ecx
    004010FC  |.  33D2          xor     edx, edx
    004010FE  |.  33DB          xor     ebx, ebx
    00401100  |.  33C0          xor     eax, eax
    00401102  |.  837D D8 32    cmp     dword ptr [ebp-28], 32           ;  字符串是否大于等于50?
    00401106  |.  7D 69         jge     short 00401171                   ;  是则出错
    00401108  |.  90            nop
    00401109  |.  90            nop
    0040110A  |.  90            nop
    0040110B  |.  90            nop
    0040110C  |>  0FBE840D 48FF>/movsx   eax, byte ptr [ebp+ecx-B8]      ;  逐个取出字符串中的字符
    00401114  |.  41            |inc     ecx                             ;  循环变量自增
    00401115  |.  33C1          |xor     eax, ecx                        ;  字符与循环变量异或
    00401117  |.  03D8          |add     ebx, eax                        ;  累加异或后的字符数据
    00401119  |.  3B4D D8       |cmp     ecx, dword ptr [ebp-28]         ;  字符串是否处理完?
    0040111C  |.^ 75 EE         jnz     short 0040110C
    0040111E  |.  6BC0 06       imul    eax, eax, 6                      ;  eax *= 6
    00401121  |.  C1E3 07       shl     ebx, 7                           ;  左移7位,空位填0
    00401124  |.  03C3          add     eax, ebx
    00401126  |.  8945 C8       mov     dword ptr [ebp-38], eax
    00401129  |.  FF75 C8       push    dword ptr [ebp-38]               ;  eax + ebx的值入栈
    0040112C  |.  68 38B44000   push    0040B438                         ;  ASCII "%lX"
    00401131  |.  8D8D 80FEFFFF lea     ecx, dword ptr [ebp-180]
    00401137  |.  51            push    ecx                              ;  缓冲区
    00401138  |.  E8 873D0000   call    00404EC4
    0040113D  |.  83C4 0C       add     esp, 0C
    00401140  |.  8D85 80FEFFFF lea     eax, dword ptr [ebp-180]
    00401146  |.  50            push    eax                              ; /String2
    00401147  |.  8D95 E4FEFFFF lea     edx, dword ptr [ebp-11C]         ; |
    0040114D  |.  52            push    edx                              ; |String1
    0040114E  |.  E8 339C0000   call    <jmp.&KERNEL32.lstrcmpA>         ; lstrcmpA
    00401153  |.  85C0          test    eax, eax
    00401155  |.  75 0D         jnz     short 00401164
    00401157  |.  68 3CB44000   push    0040B43C                         ; /Text = "Congratulations! IF this number comes *FROM YOUR* keygen, Write a tutorial dude ;)."
    0040115C  |.  56            push    esi                              ; |hWnd
    0040115D  |.  E8 289B0000   call    <jmp.&USER32.SetWindowTextA>     ; SetWindowTextA
    00401162  |.  EB 18         jmp     short 0040117C
    00401164  |>  68 90B44000   push    0040B490                         ; /Text = "This serial is *NOT* Valid!! Try again... : UNREGISTERED"
    00401169  |.  56            push    esi                              ; |hWnd
    0040116A  |.  E8 1B9B0000   call    <jmp.&USER32.SetWindowTextA>     ; SetWindowTextA
    0040116F  |.  EB 0B         jmp     short 0040117C
    00401171  |>  68 C9B44000   push    0040B4C9                         ; /Text = "Name must contain more than 4 chars and less than 50 chars !!"
    00401176  |.  56            push    esi                              ; |hWnd
    00401177  |.  E8 0E9B0000   call    <jmp.&USER32.SetWindowTextA>     ; SetWindowTextA

    这里分析得出结论,程序通过对用户名字串进行操作,然后将之与密钥对比,得出结果。关键算法都差不多了解,现在只需探索下地址为00404EC4的子程序进行了什么操作:

    00404EC4  /$  55            push    ebp
    00404EC5  |.  8BEC          mov     ebp, esp
    00404EC7  |.  8B45 08       mov     eax, dword ptr [ebp+8]           ;  缓冲区地址放入eax
    00404ECA  |.  8D4D 08       lea     ecx, dword ptr [ebp+8]           ;  ecx指向缓冲区地址
    00404ECD  |.  C600 00       mov     byte ptr [eax], 0                ;  向缓冲区填0
    00404ED0  |.  8D45 10       lea     eax, dword ptr [ebp+10]
    00404ED3  |.  50            push    eax                              ;  经处理过的数值入栈
    00404ED4  |.  8B55 0C       mov     edx, dword ptr [ebp+C]
    00404ED7  |.  52            push    edx                              ;  ‘%IX’入栈
    00404ED8  |.  51            push    ecx                              ;  缓冲区地址入栈
    00404ED9  |.  68 9C4E4000   push    00404E9C
    00404EDE  |.  E8 B5010000   call    00405098
    00404EE3  |.  83C4 10       add     esp, 10
    00404EE6  |.  5D            pop     ebp
    00404EE7  .  C3            retn

    咦?这里的函数调用和上一步好像没什么明显的不同?继续深入地址为405098的子程序看看。。。。

    接着就发现一大堆乱七八糟的汇编指令。。。这么简单的CM怎么会有如此复杂的处理流程?

    不如转换下思维好了,回到一开始的流程再想想。我们可以看见,调用完405098之后,ecx中就保存了密钥:

    我们一边执行405098子程序,一边关注寄存器和堆栈的变化,慢慢搜索。

    终于,在这一行发现了密钥的踪迹:

    看到了吗?ebp+10的位置里存放着密钥“116E8”,往上翻,一定在不远处存放着处理字串的关键代码:

    00407484  |. /EB 17         jmp     short 0040749D
    00407486  |> |4B            /dec     ebx
    00407487  |. |8A03          |mov     al, byte ptr [ebx]
    00407489  |. |3C 0A         |cmp     al, 0A
    0040748B  |. |7D 08         |jge     short 00407495
    0040748D  |. |83C0 30       |add     eax, 30
    00407490  |. |8806          |mov     byte ptr [esi], al
    00407492  |. |46            |inc     esi
    00407493  |. |EB 08         |jmp     short 0040749D
    00407495  |> |0245 1C       |add     al, byte ptr [ebp+1C]
    00407498  |. |04 F6         |add     al, 0F6
    0040749A  |. |8806          |mov     byte ptr [esi], al
    0040749C  |. |46            |inc     esi
    0040749D  |> 8D55 BC        lea     edx, dword ptr [ebp-44]
    004074A0  |.  3BDA          |cmp     ebx, edx
    004074A2  |.^ 75 E2         jnz     short 00407486

    这几行汇编指令将一组数据以十六进制的格式转换为相应的字符串,用代码表示就是wsprintf( str,"%lX",res );

    正如程序中调用00404EC4子程序时的方式一样:

    00401129  |.  FF75 C8       push    dword ptr [ebp-38]               ;  eax + ebx的值入栈
    0040112C  |.  68 38B44000   push    0040B438                         ;  ASCII "%lX"
    00401131  |.  8D8D 80FEFFFF lea     ecx, dword ptr [ebp-180]
    00401137  |.  51            push    ecx                              ;  缓冲区
    00401138  |.  E8 873D0000   call    00404EC4

    这里发现以上代码地址都是004开头(处于应用程序领空),也就是说以上流程处理都是CM作者自己实现的,虽然根据参数可以下判断

    这就是个转换格式的函数,但如果CM作者对于转换的过程“动手动脚”,该怎么判断呢?

    比如:本次的运算结果是116E8,转换成字符串"116E8"是没错,但万一程序中发现0FH数据,把它直接转换成字符1怎么办呢?

    如果CM作者这样编码,我们写出的注册机将没有“通用性”。所以,发现手动实现的子程序,还是进入确认下关键算法比较稳妥。

    好了,算法到这里分析完毕,我们根据加密算法开始编写注册机吧!

    复制一份http://www.cnblogs.com/ZRBYYXDM/p/5002789.html中搭建的MFC窗口程序,打开并修改OnOk函数如下:

    void CSerialNumber_KeygenDlg::OnOK() 
    {
        // TODO: Add extra validation here
        CString str;
        GetDlgItem( IDC_EDIT_NAME )->GetWindowText( str );        //获取用户名
    
        int len = str.GetLength();                                //获取长度
    
        if ( len <= 3 || len >= 50 )    
            MessageBox( "用户名必须长度大于3、小于50!" );
        else
        {
            int res = 0,sum = 0;                    //储存累加、运算结果.
            CString Temp = str;
            for ( int i = 0 ; i < len ; i++ ){
                sum += ( Temp[i] ^ (i + 1) );        //字符与循环变量异或,并累加。
                if ( (i + 1) == len )                //当前处理的是最后一个元素时,
                    res = ( Temp[i] ^ (i + 1) );    //保存字符与循环变量异或的值。
            }
    
            res *= 6;
            sum = sum << 7;
            res += sum;
            
            char* str1 = new char[50];
            wsprintf( str1,"%lX",res );        //以十六进制存储.
    
    
            GetDlgItem( IDC_EDIT_Number )->SetWindowText( str1 );
        }
    
        //CDialog::OnOK();                //屏蔽基类OnOk函数
    }

    再在OnInitDialog中添加此代码修改标题:SetWindowText(_T("k4n_Keygen"));

    运行程序,并将解密得到的序列号黏贴至k4n程序中,单击check。。。

    效果拔群:

    我们一路奋战,不是为了改变世界,而是不让世界改变我们 ——《熔炉》
  • 相关阅读:
    vue init webpack projectName命令运行报错 解决方法
    DIV实际高度小于设置高度的问题
    openlayers 地图要素的多种高亮方式 Demo(可直接运行)
    加载wkt到地图 Demo (可直接运行)
    openlayers 框选地图得到选框范围(坐标)Demo(可直接运行)
    element+vue可远程搜索可懒加载的下拉框组件
    Android-使用约束布局(ConstraintLayout)构建灵活的UI【译】
    Mysql explain 执行计划详解(转)
    Managing Hierarchical Data in MySQL(邻接表模型)[转载]
    无限极分类原理与实现(转)
  • 原文地址:https://www.cnblogs.com/ZRBYYXDM/p/5059159.html
Copyright © 2020-2023  润新知