专业路过 发表于 2010-5-29 17:35:51

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

破解声明:我只是一只小菜鸟,这是我第一次弄分析算法并且写注册机,如有失误的地方还望各位纠正.
破解过程:
        有壳,尝试脱壳机脱,失败.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                ; \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
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
004017D0   .C785 F8FEFFFF>mov   dword ptr , 0
004017DA   .EB 0F         jmp   short 004017EB
004017DC   >8B85 F8FEFFFF mov   eax, dword ptr
004017E2   .83C0 01       add   eax, 1
004017E5   .8985 F8FEFFFF mov   dword ptr , eax
004017EB   >83BD F8FEFFFF>cmp   dword ptr , 8
004017F2   .73 1E         jnb   short 00401812
004017F4   .8B8D F8FEFFFF mov   ecx, dword ptr
004017FA   .0FBE940D FCFE>movsx   edx, byte ptr
00401802   .8B85 F4FEFFFF mov   eax, dword ptr
00401808   .03C2          add   eax, edx
0040180A   .8985 F4FEFFFF mov   dword ptr , eax
00401810   .^ EB CA         jmp   short 004017DC                   ;取注册码前八个字符的和
00401812   >81BD F4FEFFFF>cmp   dword ptr , 272
0040181C   .75 2A         jnz   short 00401848                   ;如果总和=0x270
0040181E   .0FBE8D FCFEFF>movsx   ecx, byte ptr
00401825   .83F9 4E       cmp   ecx, 4E
00401828   .75 1E         jnz   short 00401848                   ;第一个字符必须是 N
0040182A   .8D95 FCFEFFFF lea   edx, dword ptr
00401830   .52            push    edx
00401831   .E8 5AFEFFFF   call    00401690                         ;用注册码做KEY 解密代码
00401836   .83C4 04       add   esp, 4
00401839   .8D85 FCFEFFFF lea   eax, dword ptr
0040183F   .50            push    eax
00401840   .E8 ABFDFFFF   call    004015F0                         ;执行解密后的代码

00401690部分代码:
004016DC|> /8B4C24 1C   /mov   ecx, dword ptr
004016E0|. |8A06          |mov   al, byte ptr
004016E2|. |8A51 01       |mov   dl, byte ptr             ;取注册码的第二个字符为KEY
004016E5|. |32C2          |xor   al, dl                        ;异或
004016E7|. |8D5424 14   |lea   edx, dword ptr
004016EB|. |884424 13   |mov   byte ptr , al
004016EF|. |52            |push    edx
004016F0|. |8D4424 17   |lea   eax, dword ptr
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
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                ; \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
0040172E|.8A0E          |mov   cl, byte ptr
00401730|.8A42 02       |mov   al, byte ptr             ;取注册码第三个字符作为KEY 解密第二部分代码
00401733|.8D5424 13   |lea   edx, dword ptr
00401737|.32C1          |xor   al, cl
00401739|.8D4C24 14   |lea   ecx, dword ptr
0040173D|.51            |push    ecx
0040173E|.6A 01         |push    1
00401740|.52            |push    edx
00401741|.56            |push    esi
00401742|.884424 23   |mov   byte ptr , 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
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 , 54
00401662|. |75 20         jnz   short 00401684                   ;注册码第十一个必须为T
00401664|. |8D5424 10   lea   edx, dword ptr
00401668|. |C74424 10 000>mov   dword ptr , 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                ; \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                ; \GetDlgItemTextA 取注册码
00401582   .A1 D0F04200   mov   eax, dword ptr
00401587   .85C0          test    eax, eax
00401589   .74 58         je      short 004015E3
0040158B   .0FBE5424 07   movsx   edx, byte ptr             ;取第八个字符
00401590   .0FBE4424 03   movsx   eax, byte ptr             ;取第四个字符
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
004015A4   .83C9 FF       or      ecx, FFFFFFFF
004015A7   .33C0          xor   eax, eax
004015A9   .F2:AE         repne   scas byte ptr es:
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 5Acmp   byte ptr , 5A             ;最后一个字母是否为Z
004015B9   .75 28         jnz   short 004015E3
004015BB   .807C24 08 41cmp   byte ptr , 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                ; \MessageBoxA
004015D6   .8B0D D0F04200 mov   ecx, dword ptr
004015DC   .51            push    ecx                              ; /hHook => 01430B21
004015DD   .FF15 38244200 call    dword ptr                ; \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就看到代码了,这点是本菜鸟在昨天晚上想到的办法(呵呵 有点投机取巧 要把汇编详细的还原成原始代码,本人还是非常非常困难的).整个程序有三个校验注册码的地方,而且都是相互关联,必须解决了第一个才能进行下一步,而且在解密代码的部分的时候是难倒人了的,没办法,我是只有穷举了.

Nisy 发表于 2010-5-29 17:40:47

/:013 强大 ~~

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

:loveliness:my first keygen

yangyucun 发表于 2010-5-29 17:42:06

好好学习天天向上

专业路过 发表于 2010-5-29 17:44:55

回复 2# Nisy
呵呵 ~还有什么简单的方法么???只有穷举呗 我是一个字母一个字母的试出来的(呵呵~~好像听了有点无语...).

极点 发表于 2010-5-29 17:47:01

呵呵。IDA 里面找的吗?

cjteam 发表于 2010-5-29 17:50:31

/:013學習了

wuhanqi 发表于 2010-5-29 17:52:31

我是这样找二三位的:

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

Nisy 发表于 2010-5-29 17:56:55

太强大了 学习一下 ~

我搞的话 这样来测试应该是最简单的了 这次是模仿了之前的一个东东 也是这样的 用4个字节去做的抑或处理 {:3_171:} 就是大约碰撞 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
    {
      MOVEAX,g_GetDlgText
      ADDEAX,2
      jmpEAX
    }
}


DWORD WINAPI MyCheckThread(LPVOID lpThreadParameter)
{
    char szBuffer;
    GetDlgItemText(g_Hwnd,IDC_EDIT1,szBuffer,MAXBYTE);   
    if (g_HookKeyBoard && (szBuffer + szBuffer== 132 )
      && (strlen(szBuffer) == 12 ) && szBuffer == 'Z'
      && szBuffer < '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 = szOk ^ 0x43;
    }
    ::SetDlgItemText(g_Hwnd,IDC_OK,(LPCTSTR)szOk);

    if ( szBuffer == 'X' && szBuffer == '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; //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; //0x50这个是L;
      WriteProcessMemory(GetCurrentProcess(),(pAddr+i+15),&ch,1,&dwWrite);
    }
    bIsDec = TRUE;
}


void ChechCode()
{
    char szBuffer = {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;
      }


      
      if (nCount == 626 && szBuffer == '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 = {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 = szProc ^ 0x43;
    }

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

}

专业路过 发表于 2010-5-29 18:00:13

本帖最后由 专业路过 于 2010-5-29 18:04 编辑

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

wuhanqi 发表于 2010-5-29 18:07:55

回复 9# 专业路过


    人肉功底深厚啊!/:good
    要是Nisy抽的字节多些,恐怕这种小聪明就不好使咯..哈哈
页: [1] 2
查看完整版本: Nisy's CrackMe 算法分析+VB注册机源码(0.4版)