首页
社区
课程
招聘
[原创](灌水)__imp__keygenme3分析
发表于: 2007-10-24 12:47 8977

[原创](灌水)__imp__keygenme3分析

2007-10-24 12:47
8977

【文章标题】: (灌水)__imp__keygenme3分析
【文章作者】: 峰回路转
【作者邮箱】: killbug2004@gmail.com
【软件名称】: __imp__keygenme3
【软件大小】: 9k
【下载地址】: http://www.crackmes.de/users/imp/keygenme_3/download
【编写语言】: asm
【使用工具】: peid,od,ida,ultraedit
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  这个crackme在www.crackmes.de上挂了几天,估计是太容易的,大家没去看。我就下下来
  试着玩一下,这里记录一下我的一种注册的方法。估计还有别的注册方法,我没有仔细的看。

  先用peid查一下加壳没,结果是TASM / MASM。
  接着用OD载入,F8大体的走了一下,很快就发现有花指令
  0040103B   /EB FF           jmp     short 0040103C
  0040103D    D068 24         shr     byte ptr [eax+24], 1
  00401040    34 40           xor     al, 40
  00401042    00FF            add     bh, bh
  00401044    35 00374000     xor     eax, 403700
  00401049    E8 120B0000     call    <jmp.&kernel32.GetProcAddress>
  这种隐藏函数调用的花比较常见,可以将40103B地址处的第一个字节nop掉,程序中还有几处
  也用了这种方法,可以手工改,我比较懒,写了
  一句话 REPL 401000,#ebffd0#,#90ffd0#,1000,用脚本跑一下,程序代码就清晰多了,把
  程序dump出来,丢到 IDA中,程序比较小,很快就分析好了。
  用IDA分析时发现程序中还有几处用了比较常见的anti-debug的方法
  1、使用NtSetInformationThread这个Native API,地址是401138、40163c、401330
     401A7B,用OD调试时可以直接在这些地址调用的地方nop掉,也可以使用插件。
  2、CC检测
   
     004015C8    56              push    esi
     004015C9    1E              push    ds
     004015CA    0E              push    cs
     004015CB    1F              pop     ds
     004015CC    8B76 02         mov     esi, dword ptr [esi+2]
     004015CF    8B36            mov     esi, dword ptr [esi]
     004015D1    8BC6            mov     eax, esi
     004015D3    25 FF000000     and     eax, 0FF
     004015D8    3C CC           cmp     al, 0CC;检测是否下断点
     004015DA    B8 4A000000     mov     eax, 4A
     004015DF    74 02           je      short 004015E3
     004015E1    33C0            xor     eax, eax
     004015E3    1F              pop     ds
     004015E4    5E              pop     esi
     004015E5    C3              retn
     程序在调用GetDlgItem函数前检测是否被下断点,OD调试时nop掉检测函数调用或不在这
     个API处下断点即可
  3、利用PEB检测
     004019DB    64:A1 30000000  mov     eax, dword ptr fs:[30]
     004019E1    8B40 18         mov     eax, dword ptr [eax+18]
     004019E4    8B40 10         mov     eax, dword ptr [eax+10]
     004019E7    85C0            test    eax, eax
     004019E9  ^ 0F85 A4F6FFFF   jnz     00401093;检测到调试器就退出
     004019EF    C3              retn
     可以直接把40109F处的调用nop掉
  
  再粗看了一下程序的流程,大体如下:
  1、.text:0040106F   call    eax ; Open_File ; 从"custom.ev"中读入数据到buffer中
  2、.text:00401077   call    eax ; Check_Data ; 判断文件中的数据是否是有效的
  3、利用SEH调到.text:0040108E  call    WinMain_401099处创建对话框
  
  现在的关键就是分析消息处理中对注册消息的处理,在消息处理分支中找到如下
  .text:00401323                 cmp     [ebp+wParam], 3EBh ; checkBTN
  .text:0040132A                 jnz     loc_4013D2
  .text:00401330                 call    anti_debug2
  .text:00401335                 mov     Message_Lenght, 0 ; 初始化
  .text:0040133C                 mov     ecx, 30h
  .text:00401341                 xor     eax, eax
  .text:00401343                 mov     edi, offset Message_Buffer
  .text:00401348                 rep stosb               ; 初始化字符数组
  .text:0040134A                 lea     esi, GetDlgItemTextA
  .text:00401350                 call    antiBP_4015C8
  .text:00401355                 cmp     eax, 4Ah
  .text:00401358                 jz      loc_401492      ; 调走就over!
  .text:0040135E                 push    3Fh
  .text:00401360                 push    offset sz_Name
  .text:00401365                 push    3E9h            ; NameEdit
  .text:0040136A                 push    [ebp+hWnd]
  .text:0040136D                 call    esi             ; GetDlgItemTextA
  .text:0040136F                 test    eax, eax
  .text:00401371                 jnz     short loc_401397
  来到
  .text:00401397 loc_401397:                   ; CODE XREF: WndProc_401301+70j
  .text:00401397                 cmp     eax, 32h; 判断名字的长度是否大于32h(50D)
  .text:0040139A                 jle     short loc_4013C0
  名字长度合法来到
  .text:004013C0 loc_4013C0:        ; CODE XREF: WndProc_401301+99j
  .text:004013C0                 mov     NameLength, eax
  .text:004013C5                 mov     eax, offset CreateThread_401A7B
  .text:004013CA                 nop
  .text:004013CB                 call    eax ; CreateThread_401A7B ; 创建线程
  .text:004013CB
  来到线程的进入函数处.text:00401919 ; DWORD __stdcall StartAddress(LPVOID),可以
  看到一大段对输入用户名的处理,先弄清楚处理流程,暂且不细看,接着出现
  .text:0040198A                 push    0               ; lParam
  .text:0040198C                 push    0               ; wParam
  .text:0040198E                 push    511h            ; Msg
  .text:00401993                 push    hDlg            ; hWnd
  .text:00401999                 call    PostMessageA
  这里发送自定义消息,我们又来到开始的消息处理函数处,找到处理Msg为511h的分支
  .text:004013F8 loc_4013F8:                   ; CODE XREF: WndProc_401301+1Cj
  .text:004013F8                 cmp     [ebp+Msg], 511h ; 自定义消息
  .text:004013FF                 jnz     loc_4014C5
  .text:00401405                 movq    mm0, qword_403450
  .text:0040140C                 lea     esi, GetDlgItemTextA
  .text:00401412                 call    antiBP_4015C8
  .text:00401417                 cmp     eax, 4Ah
  .text:0040141A                 jz      short loc_401492 ; 如果下了断点就跳走
  .text:0040141A
  .text:0040141C                 push    3Fh
  .text:0040141E                 push    offset s_Key
  .text:00401423                 push    3EAh
  .text:00401428                 push    [ebp+hWnd]
  .text:0040142B                 call    esi ;获取Key
  .text:0040142D                 cmp     Flag, 0
  .text:00401434                 jnz     short loc_401492
  .text:00401436                 test    eax, eax
  .text:00401438                 jz      short loc_40144A ; 如果没有输入key就跳走
  .text:0040143A                 cmp     eax, 13h        ; Key的长度要为13h (19D)
  .text:0040143D                 jnz     short loc_40146E
  .text:0040143F                 call    Check_Key;检验key字符是否合法
  .text:00401444                 test    eax, eax
  .text:00401446                 jnz     short loc_40146E
  .text:00401448                 jmp     short loc_401492
  如果输入的key合法我们就来到下面的注册验证的过程
  .text:00401492 loc_401492:                   ; CODE XREF: WndProc_401301+57j
  .text:00401492                 xor     esi, esi
  .text:00401494                 mov     esi, 2
  .text:00401499                 jmp     short loc_4014AC
  .text:00401499
  .text:0040149B ; ------------------------------------------------------------
  .text:0040149B loc_40149B:                   ; CODE XREF: WndProc_401301+1B2j
  .text:0040149B                 movzx   eax, FileData[esi]
  .text:004014A2                 push    eax
  .text:004014A3                 lea     eax, Serial_Check
  .text:004014A9                 nop
  .text:004014AA                 call    eax ;此处进行校验
  .text:004014AC loc_4014AC:                 ; CODE XREF: WndProc_401301+198j
  .text:004014AC                 cmp     si, FileData_Length
  .text:004014B3                 jbe     short loc_40149B
  我们跟进Serial_Check这个函数,可以发现许多的分支对参数处理,不过在这么多的分支中有
  一个我们希望看到运行的
  .text:0040189D                 cmp     [ebp+arg_0], 12h
  .text:004018A1                 jnz     short loc_4018CA
  .text:004018A3                 cmp     Flag, 0
  .text:004018AA                 jnz     short loc_4018C6
  .text:004018AC                 push    0               ; uType
  .text:004018AE                 push    offset s_WellDone ; "Well done :)"
  .text:004018B3                 push    offset s_YouVeOvercome ; "You've
                                        overcome it!\r\n\r\nNow muster your "...
  .text:004018B8                 push    hDlg            ; hWnd
  .text:004018BE                 call    MessageBoxA
  这个对话框是注册成功的标志,我们找到能走到这个流程的输入参数为12h,只要输入参数为
  12h还有Flag不为零,就注册成功
  
  
  现在弄清楚了处理流程我们就仔细看这些参数是怎么来的,在IDA中利用交叉引用可以知道,
  FileData中的数据是从文件"custom.ev"中读取的,如果不存在文件,FileData中的数据就
  是程序中初始化的。而Flag的值只有在
  .text:004015B0          cmp     byte ptr unk_4034A3, 'M' ; 如果我们没有         

                              ;构建custom.ev文件,那么,这里为假,Flag一直都为零
  .text:004015B7          jnz     short loc_4015BF
  .text:004015B9          inc     Flag
  这里会被改变,所以现在我们只要使FileData中存在 12h,并且上面的代码能执行,使Flag不
  为零,基本就可以注册成功
  
  现在关键就是构建合法custom.ev文件,来到文件数据检测函数
  Check_Data      proc near               ; CODE XREF: start+77p
  .text:00401574                                         ; DATA XREF: start+71o
  .text:00401574                 xor     esi, esi
  .text:00401576                 mov     al, FileData[esi]
  .text:0040157C                 inc     esi
  .text:0040157D                 cmp     al, '#'
  .text:0040157F                 jnz     short loc_401576
  .text:00401581                 dec     esi          ; 判断FileData中是否只有#号
  .text:00401582                 jz      short loc_401596
  .text:00401584                 cmp     FileData, 'E'
  .text:0040158B                 jnz     short loc_401596
  .text:0040158D                 cmp     byte ptr unk_4034A2, 'V'
  .text:00401594                 jz      short loc_4015B0
  .text:00401596                 push    10h             ; uType
  .text:00401598                 push    offset Caption  ; "Something's wrong...

:("
  .text:0040159D                 push    offset s_InvalidInput ; "Invalid

input!"
  .text:004015A2                 push    0               ; hWnd
  .text:004015A4                 call    MessageBoxA
  .text:004015A9                 push    0FFFFFFFFh      ; uExitCode
  .text:004015AB                 call    ExitProcess
  .text:004015B0                 cmp     byte ptr unk_4034A3, 'M' ; 如果我们没有
                               ;构建custom.ev文件,那么,这里为假,Flag一直都为零
  .text:004015B7                 jnz     short loc_4015BF
  .text:004015B9                 inc     Flag
  .text:004015BF                 dec     esi
  .text:004015C0                 mov     FileData_Length, si
  .text:004015C7                 retn
  从上面的代码中可以看出我们在构建custom.ev文件是开始必须为EV,要想Flag不为零,第三个
  字节为M,最后我构建的custom.ev文件中的二进制内容为
  00000000h: 45 56 6D 12 23                                  ; EVm.#
  为了顺利的走到注册成功的地方,只需要输入的用户名长度不大于32h(50D),注册码长度为13h
  (19D),并且注册码的字符满足
  .text:004019F0 Check_Key  这个子函数的要求:
  1、key的长度为13h(19D)
  2、合法的字符是0-9、A-G和a-g
  3、注册码的5、10和15位可以为字符'-'.
  
  根据以上分析构建custom.ev,使用原始的程序输入合法的用户名和key,可以实现成功注册的
  对话框,不知这是作者的初衷,还是一个bug,因为不构建custom.ev文件涉及比较复杂的处理
  我没有细看。
  
  另外这个crackme中有hide message,可以写一个程序向crackme发送一定特定的WM_CHAR消息
  ,就可以显示出来,验证算法如下:
  .text:004014FA loc_4014FA:                 
  .text:004014FA                 mov     ah, byte_4036D1[ecx] ; 处理发送的消息
  .text:00401500                 xor     Message_Buffer[ecx], ah
  .text:00401506                 dec     ecx
  .text:00401507                 jns     short loc_4014FA
  .text:00401507
  .text:00401509                 mov     ecx, 7
  .text:0040150E                 xor     eax, eax
  .text:00401510                 mov     ebx, dword_4036F2
  .text:00401510
  .text:00401516
  .text:00401516 loc_401516:                  
  .text:00401516                 mov     esi, dword ptr Message_Buffer[ecx*4]
  .text:0040151D                 xor     eax, esi
  .text:0040151F                 mov     edx, 1Fh
  .text:0040151F
  .text:00401524
  .text:00401524 loc_401524:                             
  .text:00401524                 bt      eax, 1Fh
  .text:00401528                 jnb     short loc_40152E ; eax最高位为0时跳
  .text:00401528
  .text:0040152A                 shl     eax, 1
  .text:0040152C                 xor     eax, ebx        ;
  .text:0040152C                                       
  .text:0040152C
  .text:0040152E
  .text:0040152E loc_40152E:                             
  .text:0040152E                 shl     eax, 1
  .text:00401530                 dec     edx
  .text:00401531                 jns     short loc_401524
  .text:00401531
  .text:00401533                 dec     ecx
  .text:00401534                 jns     short loc_401516
  .text:00401534
  .text:00401536                 cmp     eax, 1100010010110101010101001001000b
  .text:0040153B                 jnz     short loc_401551
  .text:0040153B
  .text:0040153D                 push    0               ; uType
  .text:0040153F                 push    offset s_TheHiddenMess ; "The hidden   

                                                             ;message is:"
  .text:00401544                 push    offset Message_Buffer ; lpText
  .text:00401549                 push    [ebp+hWnd]      ; hWnd
  .text:0040154C                 call    MessageBoxA
  我功力尚浅,无法分析出应该发送的字符序列,只能想到用穷举的方法,自己的电脑比较烂,
  就没有跑,不知哪位高手可以分析出来,请告知。
  
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2007年10月24日 下午


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (13)
雪    币: 1844
活跃值: (35)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
文写的不错啊,支持一下
2007-10-25 10:46
0
雪    币: 405
活跃值: (10)
能力值: ( LV9,RANK:1130 )
在线值:
发帖
回帖
粉丝
3
哇靠,分析的这么详细,楼主真是辛苦了,这么多字
2007-10-25 15:44
0
雪    币: 647
活跃值: (564)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
4
谢谢楼上的支持,可惜我这边文章太烂了,没有仔细分析,达到作者的要求,最近一直忙于笔试面试,等有时间再仔细的分析一下,对不住各位了
2007-10-25 19:41
0
雪    币: 29235
活跃值: (7759)
能力值: ( LV15,RANK:3306 )
在线值:
发帖
回帖
粉丝
5
CM的作者。。尴尬。。
2007-10-25 20:06
0
雪    币: 1969
活跃值: (46)
能力值: (RANK:550 )
在线值:
发帖
回帖
粉丝
6
一哭
2007-10-25 20:28
0
雪    币: 226
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7

详细!大大的支持!
2007-10-26 09:09
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
LZ,教教我,跟我说说花指令,我不懂这个,
2007-10-26 10:32
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
学习 学习 在学习
2007-10-26 11:13
0
雪    币: 29235
活跃值: (7759)
能力值: ( LV15,RANK:3306 )
在线值:
发帖
回帖
粉丝
10
fenjianren
4683-1D47-9FCA-9DDB
执行流程:
1.  [403448]=MM0;根据用户名计算出来的
    [403450]=MM1;注册码
    esi++;

11.  tmp=[403448];//8个字节  从高到低12 34 56 78
     [403448]=tmp换位后的; // 从高到低34 78 12 56
     esi++;

15.  [403448]=[403448] 循环右移5位;
     esi++;

11.  tmp=[403448];//8个字节  从高到低12 34 56 78
     [403448]=tmp换位后的; // 从高到低34 78 12 56
     esi++;

1F.  [40343E]=[403450];  //[40343E]←注册码
     esi++;

19.  [403446]=[ESI+4034A2]; //[403446]←0x3D
     esi+=3;

21.  [40343E]=[40343E] 循环右移0x0B位;
     esi++;//这一过程也可逆

29.  MM0=[40343E]; //8个字节
     //按照[403461]处的表从最后一个字节开始取MM0的每一位组成一个新的Qword
     [40343E]=新的Qword;
     esi++;//这一过程可逆

0B.  [403458]=0;
     [403446]--;
     if ([403446]==0) [403458]++;//这句执行后循环结束
     esi++;

3D.  [403458]--;
     if ([403458]!=0) si+=[ESI+4034A2],esi-=3;
     esi+=3;//只执行这一句的话循环结束
循环回21.,次数为3D

30.  if (flag!=0) something's wrong;
     else {
       [403458]=0;
       if ([40343E]==[403448]) [403458]=1;//这里要执行
       esi++;
     }

3D.  [403458]--;
     if ([403458]!=0) si+=[ESI+4034A2],esi-=3;//不执行
     esi+=3;

34.  si+=[ESI+4034A2];

12.  if (flag==0) success;
     else something's wrong;
2007-10-26 21:20
0
雪    币: 29235
活跃值: (7759)
能力值: ( LV15,RANK:3306 )
在线值:
发帖
回帖
粉丝
11
①00403461处的表改为
0x05,0x38,0x22,0x39,0x27,0x02,0x1B,0x30,0x07,0x3C,0x17,0x1C,0x00,0x15,0x03,0x37,
0x1A,0x32,0x19,0x12,0x33,0x0A,0x29,0x23,0x24,0x3B,0x3A,0x31,0x08,0x35,0x01,0x06,
0x3E,0x16,0x25,0x2A,0x2B,0x0C,0x11,0x04,0x2D,0x26,0x3D,0x20,0x09,0x14,0x0F,0x0B,
0x1F,0x18,0x28,0x13,0x21,0x34,0x3F,0x2C,0x0D,0x2F,0x2E,0x10,0x36,0x1D,0x0E,0x1E

②004034A1  45 56 01 11 15 11 1F 19 3D 00 21 29 0B 3D FD FF中的
21 29 0B改为29 21 0B
③0x40-0x0B=0x35;
00401814  PUSH 0B   改为  push 35

0040172C  MOVQ MM3,QWORD PTR DS:[403450]
改为0040172C  MOVQ MM3,QWORD PTR DS:[403448]

以上4步改完后在401871下断点
00401871   .  0F6F25 3E3440>MOVQ MM4,QWORD PTR DS:[40343E]

[40343E]即为用户名对应的注册码
哪位有兴趣可以给它弄个MessageBox
2007-10-26 21:22
0
雪    币: 647
活跃值: (564)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
12
风间仁兄正解,不知那hidemessage可以弄出来不,说句心里话,现在看到那循环配上移位的,潜意识中就感觉不可逆
2007-10-27 07:51
0
雪    币: 29235
活跃值: (7759)
能力值: ( LV15,RANK:3306 )
在线值:
发帖
回帖
粉丝
13
不太清楚。
2007-10-27 09:02
0
雪    币: 740
活跃值: (952)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
14
厉害,这可是VERY HARD级别的....

作者居然说是日本论坛......真....侮辱啊..
2008-1-22 19:12
0
游客
登录 | 注册 方可回帖
返回
//