飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 9847|回复: 17

[原创] Nisy's CrackMe 算法分析+VB注册机源码(0.4版)

    [复制链接]
  • TA的每日心情
    郁闷
    2019-5-27 22:42
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    发表于 2010-5-29 17:35:51 | 显示全部楼层 |阅读模式
    破解声明:我只是一只小菜鸟,这是我第一次弄分析算法并且写注册机,如有失误的地方还望各位纠正.
    破解过程:
            有壳,尝试脱壳机脱,失败.OD载入运行,随便输入注册码发现是由标签提示,说来也巧,以前在看雪看过用钩子触发注册码校验的,于是想到试一下,重新载入然后在SetWindowsHookExA下断,运行发现 除开MFC自己的钩子,程序还会安装一个键盘钩子
       ; [GetCurrentThreadId
    00401905  |.  50            push    eax                              ; /ThreadID
    00401906  |.  56            push    esi                              ; |hModule
    00401907  |.  68 50184000   push    00401850                         ; |Hookproc = cm_nf_v0.00401850
    0040190C  |.  6A 02         push    2                                ; |HookType = WH_KEYBOARD
    0040190E  |.  FF15 1C244200 call    dword ptr [42241C]               ; \SetWindowsHookExA
    然后直接看钩子函数:
    004018BE   .  66:3D 5A00    cmp     ax, 5A
    004018C2   .  77 05         ja      short 004018C9
    004018C4   .  E8 A7FEFFFF   call    00401770                ;校验注册码 键盘输入注册码的时候输入的是大写字母就会进入


    这里是进行注册码校验:
    004017B1   .  50            push    eax
    004017B2   .  B8 E8030000   mov     eax, 3E8
    004017B7   .  83C0 02       add     eax, 2
    004017BA   .  50            push    eax
    004017BB   .  FF35 D4F04200 push    dword ptr [42F0D4]
    004017C1   .  B8 CC174000   mov     eax, 004017CC
    004017C6   .  50            push    eax
    004017C7   .^ E9 84FDFFFF   jmp     00401550                         ;  直接跳到GetDlgItemTextA+2 取注册码
    004017CC   .  50            push    eax
    004017CD   .  8F45 FC       pop     dword ptr [ebp-4]
    004017D0   .  C785 F8FEFFFF>mov     dword ptr [ebp-108], 0
    004017DA   .  EB 0F         jmp     short 004017EB
    004017DC   >  8B85 F8FEFFFF mov     eax, dword ptr [ebp-108]
    004017E2   .  83C0 01       add     eax, 1
    004017E5   .  8985 F8FEFFFF mov     dword ptr [ebp-108], eax
    004017EB   >  83BD F8FEFFFF>cmp     dword ptr [ebp-108], 8
    004017F2   .  73 1E         jnb     short 00401812
    004017F4   .  8B8D F8FEFFFF mov     ecx, dword ptr [ebp-108]
    004017FA   .  0FBE940D FCFE>movsx   edx, byte ptr [ebp+ecx-104]
    00401802   .  8B85 F4FEFFFF mov     eax, dword ptr [ebp-10C]
    00401808   .  03C2          add     eax, edx
    0040180A   .  8985 F4FEFFFF mov     dword ptr [ebp-10C], eax
    00401810   .^ EB CA         jmp     short 004017DC                   ;  取注册码前八个字符的和
    00401812   >  81BD F4FEFFFF>cmp     dword ptr [ebp-10C], 272
    0040181C   .  75 2A         jnz     short 00401848                   ;  如果总和=0x270
    0040181E   .  0FBE8D FCFEFF>movsx   ecx, byte ptr [ebp-104]
    00401825   .  83F9 4E       cmp     ecx, 4E
    00401828   .  75 1E         jnz     short 00401848                   ;  第一个字符必须是 N
    0040182A   .  8D95 FCFEFFFF lea     edx, dword ptr [ebp-104]
    00401830   .  52            push    edx
    00401831   .  E8 5AFEFFFF   call    00401690                         ;  用注册码做KEY 解密代码
    00401836   .  83C4 04       add     esp, 4
    00401839   .  8D85 FCFEFFFF lea     eax, dword ptr [ebp-104]
    0040183F   .  50            push    eax
    00401840   .  E8 ABFDFFFF   call    004015F0                         ;  执行解密后的代码

    00401690部分代码:
    004016DC  |> /8B4C24 1C     /mov     ecx, dword ptr [esp+1C]
    004016E0  |. |8A06          |mov     al, byte ptr [esi]
    004016E2  |. |8A51 01       |mov     dl, byte ptr [ecx+1]            ;  取注册码的第二个字符为KEY
    004016E5  |. |32C2          |xor     al, dl                          ;  异或
    004016E7  |. |8D5424 14     |lea     edx, dword ptr [esp+14]
    004016EB  |. |884424 13     |mov     byte ptr [esp+13], al
    004016EF  |. |52            |push    edx
    004016F0  |. |8D4424 17     |lea     eax, dword ptr [esp+17]
    004016F4  |. |6A 01         |push    1
    004016F6  |. |50            |push    eax
    004016F7  |. |56            |push    esi
    004016F8  |. |FFD5          |call    ebp                             ;  kernel32.GetCurrentProcess
    004016FA  |. |50            |push    eax
    004016FB  |. |FFD3          |call    ebx                             ;  WriteProcessMemory 写回解密后的代码
    004016FD  |. |46            |inc     esi
    004016FE  |. |8D0C37        |lea     ecx, dword ptr [edi+esi]
    00401701  |. |83F9 78       |cmp     ecx, 78                         ;  解密0x78个字节
    00401704  |.^\7C D6         \jl      short 004016DC
    00401706  |.  6A 00         push    0                                ; /pOldProtect = NULL
    00401708  |.  6A 40         push    40                               ; |NewProtect = PAGE_EXECUTE_READWRITE
    0040170A  |.  6A 55         push    55                               ; |Size = 55 (85.)
    0040170C  |.  68 60154000   push    00401560                         ; |Address = cm_nf_v0.00401560
    00401711  |.  FF15 80224200 call    dword ptr [422280]               ; \VirtualProtect
    00401717  |.  BE 60154000   mov     esi, 00401560
    0040171C  |.  BF F1FFFFFF   mov     edi, -0F
    00401721  |.  83C6 0F       add     esi, 0F
    00401724  |.  81EF 60154000 sub     edi, 00401560
    0040172A  |>  8B5424 1C     /mov     edx, dword ptr [esp+1C]
    0040172E  |.  8A0E          |mov     cl, byte ptr [esi]
    00401730  |.  8A42 02       |mov     al, byte ptr [edx+2]            ;  取注册码第三个字符作为KEY 解密第二部分代码
    00401733  |.  8D5424 13     |lea     edx, dword ptr [esp+13]
    00401737  |.  32C1          |xor     al, cl
    00401739  |.  8D4C24 14     |lea     ecx, dword ptr [esp+14]
    0040173D  |.  51            |push    ecx
    0040173E  |.  6A 01         |push    1
    00401740  |.  52            |push    edx
    00401741  |.  56            |push    esi
    00401742  |.  884424 23     |mov     byte ptr [esp+23], al
    00401746  |.  FFD5          |call    ebp                             ;  kernel32.GetCurrentProcess
    00401748  |.  50            |push    eax
    00401749  |.  FFD3          |call    ebx                             ;  WriteProcessMemory 写回解密后的代码
    0040174B  |.  46            |inc     esi
    0040174C  |.  8D0437        |lea     eax, dword ptr [edi+esi]
    0040174F  |.  83F8 55       |cmp     eax, 55
    00401752  |.^ 7C D6         \jl      short 0040172A

    00401690这里会解密00401560和004015F0这两个函数里面的代码 注册码不对的话会解密成错误的代码,使程序出错.有意思的是在00401560和004015F0里面还进行了一次注册码的校验,如果注册码没完全正确的话那个对话框是不会出来的了
    解密后004015F0里面的代码:
    0040165C  |. /75 26         jnz     short 00401684                   ;  注册码第十个必须为X
    0040165E  |. |8078 0A 54    cmp     byte ptr [eax+A], 54
    00401662  |. |75 20         jnz     short 00401684                   ;  注册码第十一个必须为T
    00401664  |. |8D5424 10     lea     edx, dword ptr [esp+10]
    00401668  |. |C74424 10 000>mov     dword ptr [esp+10], 0
    00401670  |. |52            push    edx                              ; /pThreadId
    00401671  |. |6A 00         push    0                                ; |CreationFlags = 0
    00401673  |. |6A 00         push    0                                ; |pThreadParm = NULL
    00401675  |. |68 60154000   push    00401560                         ; |ThreadFunction = cm_nf_v0.00401560
    0040167A  |. |6A 00         push    0                                ; |StackSize = 0
    0040167C  |. |6A 00         push    0                                ; |pSecurity = NULL
    0040167E  |. |FF15 90224200 call    dword ptr [422290]               ; \CreateThread 弹出对话框  这里里面还会比较注册码

    解密后00401560里面的代码:
    00401570   .  68 FF000000   push    0FF                              ; /Count = FF (255.)
    00401575   .  50            push    eax                              ; |Buffer
    00401576   .  68 EA030000   push    3EA                              ; |ControlID = 3EA (1002.)
    0040157B   .  51            push    ecx                              ; |hWnd => 001309B8 ('N's FirsT cRack Me V0.4',class='#32770')
    0040157C   .  FF15 30244200 call    dword ptr [422430]               ; \GetDlgItemTextA 取注册码
    00401582   .  A1 D0F04200   mov     eax, dword ptr [42F0D0]
    00401587   .  85C0          test    eax, eax
    00401589   .  74 58         je      short 004015E3
    0040158B   .  0FBE5424 07   movsx   edx, byte ptr [esp+7]            ;  取第八个字符
    00401590   .  0FBE4424 03   movsx   eax, byte ptr [esp+3]            ;  取第四个字符
    00401595   .  03D0          add     edx, eax                         ;  相加 是否等于0x84
    00401597   .  81FA 84000000 cmp     edx, 84
    0040159D   .  75 44         jnz     short 004015E3
    0040159F   .  57            push    edi
    004015A0   .  8D7C24 04     lea     edi, dword ptr [esp+4]
    004015A4   .  83C9 FF       or      ecx, FFFFFFFF
    004015A7   .  33C0          xor     eax, eax
    004015A9   .  F2:AE         repne   scas byte ptr es:[edi]
    004015AB   .  F7D1          not     ecx
    004015AD   .  49            dec     ecx
    004015AE   .  5F            pop     edi
    004015AF   .  83F9 0C       cmp     ecx, 0C                          ;  注册码长度是否为是12
    004015B2   .  75 2F         jnz     short 004015E3
    004015B4   .  807C24 0B 5A  cmp     byte ptr [esp+B], 5A             ;  最后一个字母是否为Z
    004015B9   .  75 28         jnz     short 004015E3
    004015BB   .  807C24 08 41  cmp     byte ptr [esp+8], 41             ;  第九个字符是否小于A
    004015C0   .  7D 21         jge     short 004015E3
    004015C2   .  6A 00         push    0                                ; /Style = MB_OK|MB_APPLMODAL
    004015C4   .  68 CCB04200   push    0042B0CC                         ; |Title = "Great!"
    004015C9   .  68 C0B04200   push    0042B0C0                         ; |Text = "Good Job!"
    004015CE   .  6A 00         push    0                                ; |hOwner = NULL
    004015D0   .  FF15 34244200 call    dword ptr [422434]               ; \MessageBoxA
    004015D6   .  8B0D D0F04200 mov     ecx, dword ptr [42F0D0]
    004015DC   .  51            push    ecx                              ; /hHook => 01430B21
    004015DD   .  FF15 38244200 call    dword ptr [422438]               ; \UnhookWindowsHookEx

    根据如上算法 用VB写了如下拙劣的KeyGen:

    Private Sub cmdKEY_Click()
            Dim key As String
            Do
                    key = getkey()
            Loop While key = ""
            txtText1.Text = key
    End Sub

    Function getkey() As String
            Dim tmpstr As String
            Dim L As Integer
            Dim K As Integer
            K = (78 + 76 + &H43 + Asc("P")) + &H41
            I = 0
            Do While K <= 626
                    If I = 5 Then
                            L = &H41
                    Else
                            L = Int(Rnd() * (90 - 65 + 1) + 65)
                    End If
                    K = K + L
                    tmpstr = tmpstr + Chr(L)
                    If 626 - K >= 65 And 626 - K <= 90 Then
                            L = 626 - K
                            tmpstr = tmpstr + Chr(L - 2)
                            getkey = "NLP" & Chr(&H43) & tmpstr & "A0XTZ"
                            Exit Do
                    End If
                    I = I + 1
            Loop
    End Function
    Private Sub Form_Load()
    Randomize (Now())
    End Sub

    小节:总的说来,这个crackme代码是非常清晰的,算法也非常简单的(复杂的话就没本菜鸟的机会了),在没有脱壳的时候,可以把算法部分直接拿到IDA里面F5就看到代码了,这点是本菜鸟在昨天晚上想到的办法(呵呵 有点投机取巧 要把汇编详细的还原成原始代码,本人还是非常非常困难的).整个程序有三个校验注册码的地方,而且都是相互关联,必须解决了第一个才能进行下一步,而且在解密代码的部分的时候是难倒人了的,没办法,我是只有穷举了.

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有账号?加入我们

    x

    评分

    参与人数 1威望 +40 收起 理由
    月之精灵 + 40 您的贴子很精彩,希望能再次分享!

    查看全部评分

    PYG19周年生日快乐!

    该用户从未签到

    发表于 2010-5-29 17:40:47 | 显示全部楼层
    /:013 强大 ~~

    这个CM的猥琐的地方就在于 如何反推出KEY的第二位和第三位 这里老兄是咋搞定的哦

    :loveliness:  my first keygen
    PYG19周年生日快乐!
  • TA的每日心情
    无聊
    2016-3-31 17:15
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2010-5-29 17:42:06 | 显示全部楼层
    好好学习天天向上
    PYG19周年生日快乐!
  • TA的每日心情
    郁闷
    2019-5-27 22:42
  • 签到天数: 3 天

    [LV.2]偶尔看看I

     楼主| 发表于 2010-5-29 17:44:55 | 显示全部楼层
    回复 2# Nisy
    呵呵 ~还有什么简单的方法么???只有穷举呗 我是一个字母一个字母的试出来的(呵呵~~好像听了有点无语...).
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2010-5-29 17:47:01 | 显示全部楼层
    呵呵。  IDA 里面找的吗?
    PYG19周年生日快乐!
  • TA的每日心情
    无聊
    2017-5-31 13:17
  • 签到天数: 5 天

    [LV.2]偶尔看看I

    发表于 2010-5-29 17:50:31 | 显示全部楼层
    /:013學習了
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2010-5-29 17:52:31 | 显示全部楼层
    我是这样找二三位的:

    大胆猜测这个68与注册码第二位异或后也是24.
    验证得出是L,0x4C
    再加上接下来的解码也只用到了第三位.而且八位数的平均值要等于4E,那么4Eh*2-4Ch=50h.

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有账号?加入我们

    x

    评分

    参与人数 1威望 +8 飘云币 +8 收起 理由
    飘云 + 8 + 8 思路源于不断大胆想象、尝试,加油!

    查看全部评分

    PYG19周年生日快乐!

    该用户从未签到

    发表于 2010-5-29 17:56:55 | 显示全部楼层
    太强大了 学习一下 ~

    我搞的话 这样来测试应该是最简单的了 这次是模仿了之前的一个东东 也是这样的 用4个字节去做的抑或处理 就是大约碰撞 50*50*50*50 次 遂放弃之 ……

    所以这次给大家留了一个可玩的点 O(∩_∩)O~

    贴源码了 ~~

    看来我搞错了函数 处理了一下GetDlgItemTextA 还应该再处理一下SetWindowHookEx  等再有时间再写一个防止调试的CM出来

    HHOOK g_HookKeyBoard = NULL;
    HWND g_Hwnd = NULL;
    DWORD g_GetDlgText = NULL;

    void  __declspec(naked) HookGetDlgItemText()
    {
       
        __asm
        {
            MOV  EAX,g_GetDlgText
            ADD  EAX,2
            jmp  EAX
        }
    }


    DWORD WINAPI MyCheckThread(LPVOID lpThreadParameter)
    {
        char szBuffer[MAXBYTE];
        GetDlgItemText(g_Hwnd,IDC_EDIT1,szBuffer,MAXBYTE);   
        if (g_HookKeyBoard && (szBuffer[3] + szBuffer[7]  == 132 )
            && (strlen(szBuffer) == 12 ) && szBuffer[11] == 'Z'
            && szBuffer[8] < 'A')
        {
            MessageBox(NULL,"Good Job!","Great!",MB_OK);
            UnhookWindowsHookEx(g_HookKeyBoard);
        }
        return NULL;
    }

    BOOL bIsDec = FALSE;

    void BeGreat(char * szBuffer)
    {
        BYTE szOk[] = {0x17,0x2b,0x22,0x37,0x64,0x30,0x63,0x0c,0x28,0x62,0};
       
        for (int i = 0; i < 10 ; i++ )
        {
            szOk[i] = szOk[i] ^ 0x43;
        }
        ::SetDlgItemText(g_Hwnd,IDC_OK,(LPCTSTR)szOk);

        if ( szBuffer[9] == 'X' && szBuffer[10] == 'T')
        {      
            DWORD dwThreadID = 0;
            HANDLE hThread = CreateThread(NULL,0,
                            (LPTHREAD_START_ROUTINE)MyCheckThread,NULL,
                            NULL,&dwThreadID);
        }

    }


    VOID DecryptCode(char * szBuffer)
    {
        if (bIsDec)
        {
            return;
        }
        PCHAR pAddr = (PCHAR)BeGreat;
        BYTE ch;
        DWORD dwWrite = 0;
        VirtualProtect( (LPVOID)pAddr, 120, PAGE_EXECUTE_READWRITE, NULL);
        for (int i = 0; i < 120 ; i ++)
        {
            ch = pAddr[ i + 20] ^ szBuffer[1]; //0x50;
            WriteProcessMemory(GetCurrentProcess(),(pAddr+i+20),&ch,1,&dwWrite);
        }
        // 第二段被和谐的代码
        pAddr = (PCHAR)MyCheckThread;
        VirtualProtect( (LPVOID)pAddr, 85,PAGE_EXECUTE_READWRITE, NULL);  
        for (i = 0; i < 85 ; i ++)
        {
            ch = pAddr[ i + 15] ^ szBuffer[2]; //0x50  这个是L;
            WriteProcessMemory(GetCurrentProcess(),(pAddr+i+15),&ch,1,&dwWrite);
        }
        bIsDec = TRUE;
    }


    void ChechCode()
    {
        char szBuffer[MAXBYTE] = {0};
        DWORD dwLen = 1 ;
        int nCount = 2;
        __asm
        {
            push MAXBYTE

            MOV EAX,EBP
            sub EAX,256+4
            push EAX

            mov eax,1000
            add eax,2
            push eax

            push g_Hwnd

            mov EAX, offset OVER
            PUSH EAX

            jmp HookGetDlgItemText
        }
    OVER:   
            __asm push eax
            __asm pop dwLen


            for ( unsigned int i = 0 ; i < 8 ;i ++)
            {
                nCount += szBuffer[i];
            }


            
            if (nCount == 626 && szBuffer[0] == 'N')
            {
                DecryptCode(szBuffer);
                BeGreat(szBuffer);
            }

            return;
    }

    LRESULT CALLBACK KeyboardProc(int code,       // hook code
                                  WPARAM wParam,  // virtual-key code
                                  LPARAM lParam   // keystroke-message information
                                    )
    {

        BYTE szKeyState[256] = {0};
        if ( ((int)lParam) < 0 )
        {
            
            //16–23 Specifies the scan code
            //得到键盘状态
            GetKeyboardState(szKeyState);
            
            WORD wChar = 0;
            
            //根据键盘状态,虚拟键码,扫描码,得到输入的字符
            int nRet = ToAscii(wParam,(lParam>>16) & 0xFF,szKeyState,&wChar,0);
            
            if ( nRet == 1 && (wChar >= 'A' &&wChar <= 'Z') )
            {
                //AfxMessageBox((LPCTSTR)&wChar);  
                ChechCode();
            }
         }
       
        return CallNextHookEx(g_HookKeyBoard,code,wParam,lParam);
    }

    void CSetHookDlg::OnSethook()
    {
            // TODO: Add your control notification handler code here
        g_HookKeyBoard = ::SetWindowsHookEx( WH_KEYBOARD,
            KeyboardProc,
            AfxGetInstanceHandle(),
            GetCurrentThreadId()
            );
       
        if (!g_HookKeyBoard)
        {
            AfxMessageBox("Hook Error!");
        }
       
        g_Hwnd = GetSafeHwnd();

        BYTE szProc[] = {0x04,0x26,0x37,0x07,0x2F,0x24,0x0A,0x37,0x26,0x2e,
                        0x17,0x26,0x3b,0x37,0x02,0};
       
        for (unsigned int i = 0; i < 15 ; i++ )
        {
            szProc[i] = szProc[i] ^ 0x43;
        }

        HMODULE hModule = GetModuleHandle("user32.dll");
        g_GetDlgText = (DWORD)GetProcAddress(hModule,(LPCTSTR)szProc);

    }
    PYG19周年生日快乐!
  • TA的每日心情
    郁闷
    2019-5-27 22:42
  • 签到天数: 3 天

    [LV.2]偶尔看看I

     楼主| 发表于 2010-5-29 18:00:13 | 显示全部楼层
    本帖最后由 专业路过 于 2010-5-29 18:04 编辑

    回复 7# wuhanqi
    非常学习了.呵呵 我是穷举出来的,既然所有注册码都是大写字母 那肯定这个KEY也是所有大写字母中的一个,我是在26个字母中一个一个试出来的
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2010-5-29 18:07:55 | 显示全部楼层
    回复 9# 专业路过


        人肉功底深厚啊!/:good
        要是Nisy抽的字节多些,恐怕这种小聪明就不好使咯..哈哈
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表