吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 10276|回复: 15
收起左侧

[原创] 赚取权限第三贴,一个有意思的keygenMe的逆向分析

[复制链接]
datochan 发表于 2009-2-13 02:05
想去睡觉了,临睡觉前再发个以前的东东,这个比较简单,用IDA分析的,没有调式过,估计用OD更简单吧,大牛飘过,跟我一样菜的朋友能进来指点一下!
正文如下:
    不知道CM的分析应该发到哪个版块,本来以为是CM和RM的那个版块,可是上面写的是活动版块,不敢乱发,就发到这个我比较熟悉的版块吧,如果发错位置了,就麻烦斑斑大哥代劳转移一下!谢谢!
首先感谢PPT同学给我了这个CM,很有意思,它的具体要求如下:
Hi! There!..It's my first keygenme (crackme). Here are some tasks you should do:
-------------------------------------------------------------------------
1. To start searching a key for your name, you need to patch something to enter
   more than 1 symbol, (Patch it to 80 symbols) (no jne, je, & etc jump patching.)

2. Find key generator solution, and write a keygen

3. Patch it, to stop closing after the registration procedure
   (not allowed to patch jne, je, & etc "jump patching". (e.g: "jne" -> "je")
-------------------------------------------------------------------------
Exe file is not protected, and it is very easy to find key & to write a
keygen. But no built-in keygens (injections). As u can see I made it for newbies...

__________________
P.s: Don't forget what i said: "no jump patching". :)
Have fun!!! ;) THanx to crackmes.de


好了,不多说了,进入正题,应该说这个CM确实很容易,完全的明码,根本没有隐藏!很适合象我这样的新人!而且它也确实很有意思!
刚拿到这个CM的时候,直接用OD打开,这个小程序基本上就一目了然了!顺手在GetDlgItemTextA上F2,F9,可是他居然断不下来!
基本上到了DialogBoxParamA这个函数就飞了……

想必没有人会不知道DialogBoxParamA这个函数是干什么的吧!
用资源编辑器找到两个文本框的ID:1001,1002,转换成HEX就是3E9,3EA
.text:0040102E ; =============== S U B R O U T I N E =======================================
.text:0040102E
.text:0040102E ; Attributes: bp-based frame
.text:0040102E
.text:0040102E ; INT_PTR __stdcall DialogFunc(HWND, UINT, WPARAM, LPARAM)
.text:0040102E DialogFunc      proc near               ; DATA XREF: start+13o
.text:0040102E
.text:0040102E hDlg            = dword ptr  8
.text:0040102E arg_4           = dword ptr  0Ch
.text:0040102E arg_8           = dword ptr  10h
.text:0040102E
.text:0040102E                 push    ebp
.text:0040102F                 mov     ebp, esp
.text:00401031                 mov     eax, [ebp+arg_4]
.text:00401034                 cmp     eax, 110h
.text:00401039                 jnz     short loc_401089
.text:0040103B                 mov     eax, [ebp+hDlg]
.text:0040103E                 mov     hDlg, eax
.text:00401043                 push    3E9h            ; nIDDlgItem
.text:00401048                 push    [ebp+hDlg]      ; hDlg
.text:0040104B                 call    GetDlgItem
.text:00401050                 mov     hWnd, eax
.text:00401055                 xor     eax, eax
.text:00401057                 push    3EAh            ; nIDDlgItem
.text:0040105C                 push    [ebp+hDlg]      ; hDlg
.text:0040105F                 call    GetDlgItem
.text:00401064                 mov     dword_403210, eax
.text:00401069                 xor     eax, eax
.text:0040106B                 call    sub_40130D
.text:00401070                 push    13h             ; uFlags
.text:00401072                 push    0               ; cy
.text:00401074                 push    0               ; cx
.text:00401076                 push    0               ; Y
.text:00401078                 push    0               ; X
.text:0040107A                 push    0FFFFFFFFh      ; hWndInsertAfter
.text:0040107C                 push    [ebp+hDlg]      ; hWnd
.text:0040107F                 call    SetWindowPos
.text:00401084                 jmp     loc_401271
GetDlgItem到两个文本框以后,都会跟进.text:0040106B                 call    sub_40130D 这个函数里面去

我们也进去看看吧~
.text:0040130D ; =============== S U B R O U T I N E =======================================
.text:0040130D
.text:0040130D
.text:0040130D sub_40130D      proc near               ; CODE XREF: DialogFunc+3Dp
.text:0040130D                 push    eax
.text:0040130E                 xor     eax, eax
.text:00401310                 mov     eax, hDlg
.text:00401315                 push    0               ; lParam
.text:00401317                 push    1               ; wParam
.text:00401319                 push    0C5h            ; Msg
.text:0040131E                 push    3E9h            ; nIDDlgItem
.text:00401323                 push    eax             ; hDlg
.text:00401324                 call    SendDlgItemMessageA
.text:00401329                 xor     eax, eax
.text:0040132B                 mov     eax, hDlg
.text:00401330                 push    0               ; lParam
.text:00401332                 push    1               ; wParam
.text:00401334                 push    0C5h            ; Msg
.text:00401339                 push    3EAh            ; nIDDlgItem
.text:0040133E                 push    eax             ; hDlg
.text:0040133F                 call    SendDlgItemMessageA
.text:00401344                 pop     eax
.text:00401345                 retn
.text:00401345 sub_40130D      endp
看到了吧,这里向两个文本框发送消息了做文本个数的限制了,要取消它,就把上面的两个PUSH 1改成PUSH 50就OK了!
这样输入字符字数的限制就取消了!

再看取消信息提示后失败的方法,找到MessageBoxA我们来看看(它有两个MessageBoxA,我想大家不会找错的!)
.text:0040111B ; ---------------------------------------------------------------------------
.text:0040111B
.text:0040111B loc_40111B:                             ; CODE XREF: DialogFunc+A8j
.text:0040111B                                         ; DialogFunc+ADj ...
.text:0040111B                 xor     eax, eax
.text:0040111D                 cmp     ecx, 1
.text:00401120                 jnz     short loc_401130
.text:00401122                 xor     ecx, ecx
.text:00401124                 mov     ecx, offset aCongratulation ; "Congratulations!!!"
.text:00401129                 mov     eax, offset aWellDonePlease ; "Well, done, please, submit your solutio"...
.text:0040112E                 jmp     short loc_40113C
.text:00401130 ; ---------------------------------------------------------------------------
.text:00401130
.text:00401130 loc_401130:                             ; CODE XREF: DialogFunc+F2j
.text:00401130                 xor     ecx, ecx
.text:00401132                 mov     eax, offset aSomethingWrong ; "Something wrong with serial, or name."
.text:00401137                 mov     ecx, offset aError__ ; "Error.."
.text:0040113C
.text:0040113C loc_40113C:                             ; CODE XREF: DialogFunc+100j
.text:0040113C                 push    0               ; uType
.text:0040113E                 push    ecx             ; lpCaption
.text:0040113F                 push    eax             ; lpText
.text:00401140                 push    [ebp+hDlg]      ; hWnd
.text:00401143                 call    MessageBoxA
.text:00401148                 mov     eax, 0
.text:0040114D                 cmp     eax, 0
.text:00401150                 jnz     loc_401255
.text:00401156                 jmp     loc_exit
我想大家在看到这个函数后面的代码的时候,谁都不会没有感觉的:
.text:00401148                 mov     eax, 0
.text:0040114D                 cmp     eax, 0
.text:00401150                 jnz     loc_401255
这里的JNZ基本上就是被NOP掉了,怎么改才能让它正常也不用我说了吧,把赋值的0改成1就OK了~
好了,下面我们来分析一下他的这个程序的算法流程!
.text:004010F1                 mov     eax, offset aISaidItIsVeryE ; "=== I said it is very easy, for newbies"...
.text:004010F6                 xor     eax, eax
.text:004010F8                 xor     ecx, ecx
.text:004010FA                 xor     ebx, ebx
.text:004010FC                 xor     edx, edx

.text:004010FE
.text:004010FE loc_4010FE:                             ; CODE XREF: DialogFunc+150j
.text:004010FE                 inc     ebx             ;这里把EBX+1
.text:004010FF                 push    ebx
.text:00401100                 push    hWnd            ; hWnd
.text:00401106                 call    GetWindowTextLengthA
.text:0040110B                 xor     ecx, ecx
.text:0040110D                 mov     ebx, eax
.text:0040110F                 mov     eax, offset Buffer
.text:00401114                 call    sub_Name        ;这里检测我们输入的用户名
.text:00401119                 jmp     short loc_40115B
我们跟进这个sub_Name函数看看吧!
{
  .text:004012D1 ; =============== S U B R O U T I N E =======================================
  .text:004012D1
  .text:004012D1
  .text:004012D1 sub_Name        proc near               ; CODE XREF: DialogFunc+E6p
  .text:004012D1                                         ; sub_Name +17j
  .text:004012D1                 mov     dl, [eax]
  .text:004012D3                 imul    ecx, 48h
  .text:004012D6                 sub     ecx, edx
  .text:004012D8                 sub     ecx, 6Fh
  .text:004012DB                 mov     edx, ecx
  .text:004012DD                 xor     ecx, 0BACAFh
  .text:004012E3                 inc     eax
  .text:004012E4                 dec     ebx
  .text:004012E5                 cmp     ebx, 0
  .text:004012E8                 jnz     short sub_4012D1
  .text:004012EA                 retn
  .text:004012EA sub_Name        endp
}
继续,我们来到loc_40115B来看一下
.text:0040115B ; ---------------------------------------------------------------------------
.text:0040115B
.text:0040115B loc_40115B:                             ; CODE XREF: DialogFunc+EBj
.text:0040115B                 pop     ebx
.text:0040115C                 cmp     ebx, 2          ;看到没?如果处理用户名时这里是1,处理完了程序就再跳回loc_4010FE把EBX+1再就处理注册码了!
.text:0040115F                 jz      short loc_401183
.text:00401161                 xor     edx, edx
.text:00401163                 mov     dword_403218, ecx
.text:00401169                 xor     eax, eax
.text:0040116B                 push    offset pcbBuffer ; pcbBuffer
.text:00401170                 push    offset Buffer   ; lpBuffer
.text:00401175                 call    GetUserNameA
.text:0040117A                 xor     edx, edx
.text:0040117C                 xor     ecx, ecx
.text:0040117E                 jmp     loc_4010FE
:
好我们到loc_401183看看:
; ---------------------------------------------------------------------------
.text:00401183
.text:00401183 loc_401183:                             ; CODE XREF: DialogFunc+131j
.text:00401183                 xor     edx, edx
.text:00401185                 mov     dword_40321C, ecx
.text:0040118B                 push    dword_40321C
.text:00401191                 push    dword_403218
.text:00401197                 call    sub_40127A
来到sub_40127A,我不多注释了,大家可以边用OD跟这个CM边看我帖出来的代码,这里程序条理代码如下
{
  .text:0040127A
  .text:0040127A ; =============== S U B R O U T I N E =======================================
  .text:0040127A
  .text:0040127A ; Attributes: bp-based frame
  .text:0040127A
  .text:0040127A sub_40127A      proc near               ; CODE XREF: DialogFunc+169p
  .text:0040127A
  .text:0040127A arg_0           = dword ptr  8
  .text:0040127A arg_4           = dword ptr  0Ch
  .text:0040127A
  .text:0040127A                 push    ebp
  .text:0040127B                 mov     ebp, esp
  .text:0040127D                 xor     eax, eax
  .text:0040127F                 xor     ebx, ebx
  .text:00401281                 mov     eax, [ebp+arg_0]
  .text:00401284                 mov     ebx, [ebp+arg_4]
  .text:00401287                 mov     edx, eax
  .text:00401289                 xor     edx, ebx
  .text:0040128B                 xor     edx, 0FFACh
  .text:00401291                 xor     ebx, 553h
  .text:00401297                 add     eax, ebx
  .text:00401299                 add     ebx, edx
  .text:0040129B                 dec     ebx
  .text:0040129C                 add     eax, ebx
  .text:0040129E                 leave
  .text:0040129F                 retn    8
  .text:0040129F sub_40127A      endp
  .text:0040129F
}
.text:0040119C                 mov     dword_403220, eax
.text:004011A1                 call    sub_401346
{
  .text:00401346
  .text:00401346 ; =============== S U B R O U T I N E =======================================
  .text:00401346
  .text:00401346
  .text:00401346 sub_401346      proc near               ; CODE XREF: DialogFunc+173p
  .text:00401346                 mov     eax, dword_40321C
  .text:0040134B                 mov     ebx, dword_403218
  .text:00401351                 cmp     eax, ebx
  .text:00401353                 jnz     short locret_401363
  .text:00401355                 xor     ebx, 8Eh
  .text:0040135B                 add     ebx, eax
  .text:0040135D                 mov     dword_403218, ebx
  .text:00401363
  .text:00401363 locret_401363:                          ; CODE XREF: sub_401346+Dj
  .text:00401363                 retn
  .text:00401363 sub_401346      endp
  .text:00401363

}

.text:004011A6                 push    dword_40321C
.text:004011AC                 push    offset unk_403224
.text:004011B1                 call    sub_4012A2
{
  .text:004012A2
  .text:004012A2 ; =============== S U B R O U T I N E =======================================
  .text:004012A2
  .text:004012A2 ; Attributes: bp-based frame
  .text:004012A2
  .text:004012A2 sub_4012A2      proc near               ; CODE XREF: DialogFunc+183p
  .text:004012A2                                         ; DialogFunc+199p ...
  .text:004012A2
  .text:004012A2 arg_0           = dword ptr  8
  .text:004012A2 arg_4           = dword ptr  0Ch
  .text:004012A2
  .text:004012A2                 push    ebp
  .text:004012A3                 mov     ebp, esp
  .text:004012A5                 mov     esi, [ebp+arg_0]
  .text:004012A8                 add     esi, 8
  .text:004012AB                 mov     eax, [ebp+arg_4]
  .text:004012AE                 mov     ecx, 10h
  .text:004012B3                 xor     ebx, ebx
  .text:004012B5
  .text:004012B5 loc_4012B5:                             ; CODE XREF: sub_4012A2+29j
  .text:004012B5                 xor     edx, edx
  .text:004012B7                 div     ecx
  .text:004012B9                 dec     esi
  .text:004012BA                 add     dl, 30h
  .text:004012BD                 cmp     dl, 3Ah
  .text:004012C0                 jb      short loc_4012C5
  .text:004012C2                 add     dl, 7
  .text:004012C5
  .text:004012C5 loc_4012C5:                             ; CODE XREF: sub_4012A2+1Ej
  .text:004012C5                 mov     [esi], dl
  .text:004012C7                 inc     ebx
  .text:004012C8                 cmp     ebx, 8
  .text:004012CB                 jnz     short loc_4012B5
  .text:004012CD                 leave
  .text:004012CE                 retn    8
  .text:004012CE sub_4012A2      endp
  .text:004012CE
}
.text:004011A6                 push    dword_40321C
.text:004011AC                 push    offset unk_403224
.text:004011B1                 call    sub_4012A2
.text:004011B6                 mov     eax, offset unk_403224
.text:004011BB                 add     eax, 8
.text:004011BE                 push    dword_403220
.text:004011C4                 lea     eax, [eax]
.text:004011C6                 push    eax
.text:004011C7                 call    sub_4012A2
.text:004011CC                 mov     eax, offset unk_403224
.text:004011D1                 add     eax, 10h
.text:004011D4                 push    dword_403218
.text:004011DA                 lea     eax, [eax]
.text:004011DC                 push    eax
.text:004011DD                 call    sub_4012A2
.text:004011E2                 mov     eax, dword_403210
在这里反复的调用sub_4012A2函数,只要稍微用OD跟一下,我想即使不懂汇编也能明白,它这是在把我们在上面三个函数中得到的值连接起来!
下面的代码就很明显了,获取我们输入的注册码!
.text:004011E7                 push    eax             ; hWnd
.text:004011E8                 call    GetWindowTextLengthA    ;获取我们输入的注册码的长度,
.text:004011ED                 mov     ecx, 0
.text:004011F2                 cmp     eax, 18h
.text:004011F5                 jnz     loc_40111B      ;如果小于24位,就直接提示失败了!
.text:004011FB                 inc     eax
.text:004011FC                 push    eax             ; cchMax
.text:004011FD                 push    offset String   ; lpString
.text:00401202                 push    3EAh            ; nIDDlgItem
.text:00401207                 push    [ebp+hDlg]      ; hDlg
.text:0040120A                 call    GetDlgItemTextA      ;获取我们输入的假码
.text:0040120F                 mov     eax, offset String
.text:00401214                 call    sub_4012EB      ;这里是转换得到真码了!我们跟进这个CALL看看去,双击sub_4012EB

真正出现注册码的CALL在这里sub_4012EB:
{
  .text:004012EB
  .text:004012EB ; =============== S U B R O U T I N E =======================================
  .text:004012EB
  .text:004012EB
  .text:004012EB sub_4012EB      proc near               ; CODE XREF: DialogFunc+1E6p
  .text:004012EB                 mov     edx, offset unk_SN
  .text:004012F0                 add     edx, 4
  .text:004012F3                 mov     byte ptr [edx], 2Dh
  .text:004012F6                 add     edx, 0Ah
  .text:004012F9                 mov     byte ptr [edx], 2Dh
  .text:004012FC                 mov     edx, offset unk_SN
  .text:00401301                 mov     byte ptr [edx], 4Bh
  .text:00401304                 inc     edx
  .text:00401305                 mov     byte ptr [edx], 4Fh
  .text:00401308                 inc     edx
  .text:00401309                 mov     byte ptr [edx], 53h
  .text:0040130C                 retn
  .text:0040130C sub_4012EB      endp
}
好了,知道了这个,下面就剩下写注册机了~
怎么给程序打补丁我就不说了,如果大家有不知道的可以看我的源程序!
这里我只列出我的KEYGEN的算法部分!(程序本来就是用MASM32写的,所以我也就不翻译成C了,直接用内联汇编来写了,代码直接用IDA考出来时候,省时省力,嘿嘿~!)
DWORD CKeygenDlg::CLName(char *Name)
{
  DWORD result;
  int len = strlen(Name);
  _asm{
      mov    eax,Name
      mov    ebx,len
      xor    edx,edx
      xor     ecx,ecx
    L000:
      mov     dl, byte ptr [eax]
      imul    ecx, ecx, 0x48
      sub     ecx, edx
      sub     ecx, 0x6F
      mov     edx, ecx
      xor     ecx, 0x0BACAF
      inc     eax
      dec     ebx
      cmp     ebx, 0
      jnz     L000
      mov    result,ecx
  }
  return result;
}

DWORD CKeygenDlg::CkSn1(DWORD code)
{
  DWORD result;
  _asm{
    xor     eax, eax
    xor     ebx, ebx
    mov     eax, code
    mov     ebx, code
    mov     edx, eax
    xor     edx, ebx
    xor     edx, 0x0FFAC
    xor     ebx, 0x553
    add     eax, ebx
    add     ebx, edx
    dec     ebx
    add     eax, ebx
    mov    result,eax
  }
  return result;
}

DWORD CKeygenDlg::CkSn2(DWORD code)
{
  DWORD  result;
  _asm{
    mov     eax, code
    mov     ebx, code
    xor     ebx, 0x8E
    add     ebx, eax
    mov     result, ebx
  }
  return result;
}

void CKeygenDlg::CkSn3(char *_addr,DWORD _code)
{
  itoa(_code,_addr,16);    //这个用汇编反而麻烦了,直接用C就OK了~
}

char * CKeygenDlg::GetSerial(DWORD _addr)
{
  char *Serial = NULL;
  _asm{
    mov     edx, _addr
    add     edx, 4
    mov     byte ptr [edx], 0x2D
    add     edx, 0x0A
    mov     byte ptr [edx], 0x2D
    mov     edx, _addr
    mov     byte ptr [edx], 0x4B
    inc     edx
    mov     byte ptr [edx], 0x4F
    inc     edx
    mov    byte ptr [edx], 0x53
  }
  Serial = (char *)_addr;
  return Serial;
}



void CKeygenDlg::OnKey() 
{
  //查找CM的窗口,确定程序是否已经启动!

……(省略,详见附件)
  //先PATCH掉它对输入字符的个数限制
  
……(省略,详见附件)

  //再PATCH掉它提示后就退出

……(省略,详见附件)

  //再写一下它的算法了

  const int nBufSize = 128; 
  TCHAR chBuf[nBufSize]; 
  ZeroMemory(chBuf,nBufSize); 
  DWORD t_rst;
  DWORD dwRet = nBufSize; 
  if (::GetUserName(chBuf,&dwRet))
  {
    DWORD  tmpsn = 0;
    char  *tmpserial = new char[24];

    m_Name = chBuf;
    tmpsn = CLName(chBuf);  //这里得到0x30A2E5B8
    CkSn3(tmpserial,tmpsn);
    tmpserial += 8;
    t_rst = CkSn1(tmpsn);  //得到0x91E9A739
    CkSn3(tmpserial,t_rst);
    tmpserial += 8;
    t_rst = CkSn2(tmpsn);  //得到0x6145CAEE
    CkSn3(tmpserial,t_rst);
    tmpserial -= 16;
    m_Serial = GetSerial((DWORD )tmpserial);
  }
  m_Serial.MakeUpper();
  UpdateData(FALSE);
}
哈哈,第一次用IDA分析程序,真的很别扭,但是,不得不承认IDA功能的强大!
这个程序我从分析到写KEYGEN,一共用了将近4个小时,希望这个能对大家有所帮助!

[ 本帖最后由 bester 于 2009-2-13 02:18 编辑 ]

KeygenMeNo1.rar

48.03 KB, 下载次数: 18, 下载积分: 吾爱币 -1 CB

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

冷血书生 发表于 2009-2-13 06:33
这篇不错,学习了!!!!!!!!!!!
wgz001 发表于 2009-2-13 07:23
tianxj 发表于 2009-2-13 07:40
北京大学 发表于 2009-2-13 08:09
学习一下,谢谢
zhoudongzhou 发表于 2009-2-13 13:24
能用IDA做的都是大牛级别的
楼主也应该是这个级别的
nofriend 发表于 2009-2-13 18:32
學習了。謝謝
shsww 发表于 2009-2-13 18:54
直接看不懂啊!!学习
cjteam 发表于 2009-2-17 17:56
被忽悠了,大牛级别的产品
七夜血 发表于 2009-2-17 18:48
拜膜下 IDA一直弄不懂饿
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 13:23

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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