首页
社区
课程
招聘
lord_Phoenix's CrackMe #6-一个简单的使用异常验证的crackme
发表于: 2006-6-11 22:00 11839

lord_Phoenix's CrackMe #6-一个简单的使用异常验证的crackme

2006-6-11 22:00
11839

作者: aalloverred
软件: lord_Phoenix's CrackMe
下载: http://bbs.pediy.com/showthread.php?s=&threadid=27188
工具: peid,od,masmEd(用于编写keygen)
声明: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
详细过程
  数学太差,不懂算法,所以看中了这个:
  1.peid=>MASM32 / TASM32
  2.载入到od,看到
  0040198B  |.  6A 00         PUSH 0                                   ; /lParam = NULL
  0040198D  |.  68 471A4000   PUSH crackme_.00401A47                   ; |DlgProc = crackme_.00401A47
  00401992  |.  6A 00         PUSH 0                                   ; |hOwner = NULL
  00401994  |.  68 B2624000   PUSH crackme_.004062B2                   ; |pTemplate = "CRACKME"
  00401999  |.  FF35 20644000 PUSH DWORD PTR DS:[406420]               ; |hInst = NULL
  0040199F  |.  E8 B6090000   CALL <JMP.&user32.DialogBoxParamA>       ; \DialogBoxParamA
  
  还看看对话框的消息处理过程00401A47:
  00401A47   .  55            PUSH EBP
  00401A48   .  8BEC          MOV EBP,ESP
  00401A4A   .  83C4 C0       ADD ESP,-40
  ...往下,来到:
  00401D4D   > \817D 0C 11010>CMP DWORD PTR SS:[EBP+C],111             ;WM_COMMAND
  00401D54   .  0F85 F4010000 JNZ crackme_.00401F4E
  00401D5A   .  837D 10 0D    CMP DWORD PTR SS:[EBP+10],0D
  00401D5E   .  0F85 B6010000 JNZ crackme_.00401F1A
  00401D64   .  6A 1E         PUSH 1E                                  ; /Count = 1E (30.)
  00401D66   .  68 24644000   PUSH crackme_.00406424                   ; |Buffer = crackme_.00406424
  00401D6B   .  6A 0A         PUSH 0A                                  ; |ControlID = A (10.)
  00401D6D   .  FF75 08       PUSH DWORD PTR SS:[EBP+8]                ; |hWnd
  00401D70   .  E8 09060000   CALL <JMP.&user32.GetDlgItemTextA>       ; \GetDlgItemTextA
  00401D75   .  83F8 04       CMP EAX,4
  00401D78   .  73 13         JNB SHORT crackme_.00401D8D
  00401D7A   .  68 C0624000   PUSH crackme_.004062C0                   ; /Text = "Enter 4 or more chars..."
  00401D7F   .  6A 0B         PUSH 0B                                  ; |ControlID = B (11.)
  00401D81   .  FF75 08       PUSH DWORD PTR SS:[EBP+8]                ; |hWnd
  00401D84   .  E8 43060000   CALL <JMP.&user32.SetDlgItemTextA>       ; \SetDlgItemTextA
  00401D89   .  C9            LEAVE
  00401D8A   .  C2 1000       RETN 10
  00401D8D   >  83F8 1E       CMP EAX,1E
  00401D90   .  76 13         JBE SHORT crackme_.00401DA5
  00401D92   .  68 D9624000   PUSH crackme_.004062D9                   ; /Text = "Name too long!"
  00401D97   .  6A 0B         PUSH 0B                                  ; |ControlID = B (11.)
  00401D99   .  FF75 08       PUSH DWORD PTR SS:[EBP+8]                ; |hWnd
  00401D9C   .  E8 2B060000   CALL <JMP.&user32.SetDlgItemTextA>       ; \SetDlgItemTextA
  00401DA1   .  C9            LEAVE
  00401DA2   .  C2 1000       RETN 10
  00401DA5   >  68 A31F4000   PUSH crackme_.00401FA3                   ;  SE 处理程序安装
  00401DAA   .  64:FF35 00000>PUSH DWORD PTR FS:[0]
  00401DB1   .  64:8925 00000>MOV DWORD PTR FS:[0],ESP
  00401DB8   .  60            PUSHAD
  00401DB9   .  6A 1F         PUSH 1F                                  ; /Count = 1F (31.)
  00401DBB   .  68 38644000   PUSH crackme_.00406438                   ; |Buffer = crackme_.00406438
  00401DC0   .  6A 0B         PUSH 0B                                  ; |ControlID = B (11.)
  00401DC2   .  FF75 08       PUSH DWORD PTR SS:[EBP+8]                ; |hWnd
  00401DC5   .  E8 B4050000   CALL <JMP.&user32.GetDlgItemTextA>       ; \GetDlgItemTextA
  00401DCA   .  CC            INT3                                     ;  1
  
  这里主要验证了用户名的长度必须是4-30,看这里:

  00401DA5   >  68 A31F4000   PUSH crackme_.00401FA3                   ;  SE 处理程序安装

  od明确的告诉了我们这句指令的用途,而401DCA处的int 3 指令正好触发异常,所以来到这里:

  00401FA3  /$  55            PUSH EBP                                 ;  结构异常处理程序

  输入合法长度的用户名,并在这里设个断点,F9运行,断下
  
  00401FA3  /$  55            PUSH EBP                                 ;  结构异常处理程序
  00401FA4  |.  8BEC          MOV EBP,ESP
  00401FA6  |.  60            PUSHAD
  00401FA7  |.  8B55 08       MOV EDX,DWORD PTR SS:[EBP+8]             ;  exception_record
  00401FAA  |.  B8 01000000   MOV EAX,1
  00401FAF  |.  813A 03000080 CMP DWORD PTR DS:[EDX],80000003          ;  int 3 exception?
  00401FB5  |.  0F85 7F030000 JNZ <crackme_.ExceptionEnd>
  00401FBB  |.  8B7D 10       MOV EDI,DWORD PTR SS:[EBP+10]            ;  context
  00401FBE  |.  8B1F          MOV EBX,DWORD PTR DS:[EDI]
  00401FC0  |.  8BB7 B8000000 MOV ESI,DWORD PTR DS:[EDI+B8]            ;  exception_eip:crackme_.00401DDC...
  00401FC6  |.  46            INC ESI
  00401FC7  |.  33C0          XOR EAX,EAX
  00401FC9  |.  8947 04       MOV DWORD PTR DS:[EDI+4],EAX             ;  dr0-dr7
  00401FCC  |.  8947 08       MOV DWORD PTR DS:[EDI+8],EAX
  00401FCF  |.  8947 0C       MOV DWORD PTR DS:[EDI+C],EAX
  00401FD2  |.  8947 10       MOV DWORD PTR DS:[EDI+10],EAX
  00401FD5  |.  8947 14       MOV DWORD PTR DS:[EDI+14],EAX
  00401FD8  |.  C747 18 55010>MOV DWORD PTR DS:[EDI+18],155            ;  l3l2l1l0:101010101
  00401FDF  |.  AC            LODS BYTE PTR DS:[ESI]                   ;  eip+1
  
  后面我们还会看到它还会继续读取产生异常处的后面的数据,就拿第一次的处理过程为例:
  第一个int3指令后的数据为:
  03
  04
  00
  01
  B0 00 00 00
  1E 00 00 00
  04
  19 12 AD DE
  写成这样是因为稍微仔细的看一下我们就会发现,紧跟在exception_eip后面的字节(这里为03)决定了后面按什么格式读取后续的
  数据,03代表的格式就是db,db,db,dd,dd,db,dd..(其实我这么分析也没发现什么太大的用途,总体来说这个crackme锻炼的是耐力:D)
  而前面的几个字节(这里是04,00,01)主要用来控制程序的流程,及他们决定了这次异常处理要干什么.
  跟踪以下可以看到第一次它干了些什么,提取的主要起作用的代码为:

  0040209E  |.  AD            LODS DWORD PTR DS:[ESI]                  ;  第一次为DEAD1219
  0040209F  |.  35 3713ADDE   XOR EAX,DEAD1337
  004020A4  |.  0387 B8000000 ADD EAX,DWORD PTR DS:[EDI+B8]
  004020AA  |.  A3 D6644000   MOV DWORD PTR DS:[4064D6],EAX            ;  注册失败对话框的eip
  
  004020AF  |.  A1 CA644000   MOV EAX,DWORD PTR DS:[4064CA]            ;  eax=000000B0
  004020BD  |.  8B0438        MOV EAX,DWORD PTR DS:[EAX+EDI]           ;  异常发生时的eax(此时为注册码的长度)
  
  004020DB  |> \8B1D CE644000 MOV EBX,DWORD PTR DS:[4064CE]            ;  ebx=0000001E
  
  0040217B  |> \3BC3          CMP EAX,EBX
  0040217D  |.  73 09         JNB SHORT crackme_.00402188              ;  注册码长度 <1eh?
  0040217F  |.  8387 B8000000>ADD DWORD PTR DS:[EDI+B8],12             ;  eip+12(=401DDC(第一次时))
  00402186  |.  EB 0B         JMP SHORT crackme_.00402193
  00402188  |>  A1 D6644000   MOV EAX,DWORD PTR DS:[4064D6]
  0040218D  |.  8987 B8000000 MOV DWORD PTR DS:[EDI+B8],EAX            ;  eip=badboy!!!
  00402193  |>  33C0          XOR EAX,EAX
  00402195  |.  E9 A0010000   JMP <crackme_.ExceptionEnd>
  
  
  所以第一次中断程序所完成的可以写成这样:

  mov eax,SerialLength
  cmp eax,1eh
  jnb badboy
  
  这之后程序会执行到新的eip即401DDC:

  00401DDC      CC            INT3                                     ;  2
  00401DDD      04            DB 04
  00401DDE      03            DB 03
  00401DDF      00            DB 00
  00401DE0      00            DB 00
  00401DE1      01            DB 01
  00401DE2      A4            DB A4
  00401DE3      00            DB 00
  00401DE4      00            DB 00
  00401DE5      00            DB 00
  00401DE6      00            DB 00
  00401DE7      00            DB 00
  00401DE8      00            DB 00
  00401DE9      00            DB 00
  00401DEA      CC            INT3   

  可以看到这里是第二次中断,同样的方法分析可以得到第二次的功能是这样的:

  mov ebx,0(xor ebx,ebx)
  
  反反复复的我们可以得到:
  
  lea eax,name;e3
  
  xor edx,edx;e4
  
  mov edx,dword ptr[eax];e5
  and edx,0ffh
  
  add ebx,edx;e6
  
  add eax,1;e7
  
  cmp byte ptr[eax],0;e8
  jz keygen1
  jmp e4
  ;===================
    PUSH EBX
    PUSH 00406324 ;"%u"
    PUSH 00406457
    CALL wsprintfA
    ADD ESP,0C
  ;===================
  cmp eax,01eh;e9
  jz badboy
  
  lea ebx,406457;e10
  
  add ebx,eax;e11
  
  mov eax,dword ptr[name];e12
  
  mov eax,dword ptr[serial];e13
  
  mov ebx,dword ptr[real_serial];e14
  
  cmp eax,ebx;e15
  jnz badboy
  ;====================
    PUSH realserial
    PUSH ourserial
    CALL lstrcmpA
  ;====================
  
  cmp eax,0;e16
  jnz badboy
  
  ;====================
    PUSH crackme_.00406286;"Congratulations..."
    PUSH 0B
    PUSH DWORD PTR SS:[EBP+8]
    CALL <JMP.&user32.SetDlgItemTextA>
  
  jmp exit1;e17
  ;====================
  badboy:
  ;some bad msg:(
  exit1:
  
上面的注释中ex代表第x次异常后的等价代码。
  需要注意的就是e8,e15,e16之后并没有直接返回到下一个异常触发,而是在这之前先执行了一些其他的代码.
  
  比如e8之后返回这里:
  00401E38   ?  53                        PUSH EBX
  00401E39   ?  68 24634000               PUSH crackme_.00406324                   ;  ASCII "%u"
  00401E3E      68 57644000               PUSH crackme_.00406457                   ;  ASCII "123333333"
  00401E43      E8 FA040000               CALL <JMP.&user32.wsprintfA>
  00401E48      83C4 0C                   ADD ESP,0C
  
  所以上面的分析就可以看成是注册码生成过程的真实代码了,此时再写keygen就很简单了:D
  3.keygen
  ;*********************************************************
  ;         keygen for lord_Phoenix's CrackMe #6
  ;                  by aalloverred
  ;                   on 2006-6-11
  ;                      any problem email me:)
  ;*********************************************************
  .386
  .model flat,stdcall
  option casemap:none
  
  include windows.inc
  include kernel32.inc
  include user32.inc
  include comctl32.inc
  
  includelib user32.lib
  includelib kernel32.lib
  includelib comctl32.lib
  
  dlgproc proto :DWORD,:DWORD,:DWORD,:DWORD
  
  .const
  
  .data
  szError db "Error...",0
  szShortname db "name should >= 4 and <= 31",0
  szName db 020h dup (0)
  szFormat db "%u",0
  szFormat1 db "%X",0
  szSerial db 20h dup(0)
  .data?
  hInstance dd ?
  .code
  start:
  invoke GetModuleHandle,NULL
  mov hInstance,eax
  invoke DialogBoxParam,eax,101,NULL,offset dlgproc,0
  invoke ExitProcess,NULL
  
  dlgproc proc hWnd:DWORD,wMsg:DWORD,wParam:DWORD,lParam:DWORD
                  mov        eax,wMsg
                  .if        eax == WM_CLOSE
                          invoke        EndDialog,hWnd,NULL
                  .elseif        eax == WM_INITDIALOG
                          invoke InitCommonControls
                  .elseif        eax == WM_COMMAND
                          mov        eax,wParam
                          .if        eax == 1002
                                  invoke        GetDlgItemText,hWnd,1000,addr szName,1eh
                                  mov ecx,eax
                                  cmp eax,4
                                  jb _shortname
                                  cmp eax,01eh
                                  jg _shortname
                                  xor ebx,ebx
                                  lea esi,szName
                  _nxtchar:               
                                  lods byte ptr [esi]
                                  movzx eax,al
                                  add ebx,eax
                                  dec ecx
                                  jnz _nxtchar
                                  invoke wsprintf,addr szSerial,addr szFormat,ebx
                                  lea edi,szSerial
                                  add edi,eax
                                  mov eax,dword ptr [szName]
                                  invoke wsprintf,edi,addr szFormat1,eax
                                  add   edi,eax
                                mov byte ptr[edi],0
                                invoke SetDlgItemText,hWnd,1001,addr szSerial
                                  jmp _exit1
                  _shortname:
                                  invoke MessageBox,hWnd,addr szShortname,addr szError,MB_OK
                  _exit1:
                 
                          .endif
                  .else
                          mov        eax,FALSE
                          ret
                  .endif                  
                  mov        eax,TRUE
                  ret
  dlgproc endp
  
  end start
  
  
  资源文件:
  101 DIALOG DISCARDABLE  0, 0, 187, 64
  STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
  CAPTION "Key Gen..."
  FONT 10, "System"
  BEGIN
      LTEXT           "Name:",-1,7,7,22,11
      EDITTEXT        1000,35,7,145,12,ES_AUTOHSCROLL
      LTEXT           "Serial:",-1,7,25,23,9
      EDITTEXT        1001,35,25,145,12,ES_AUTOHSCROLL
      PUSHBUTTON      "&KeyGen",1002,68,40,45,13
      LTEXT           "aal on 2006.6.11",-1,126,48,54,9,WS_DISABLED
  END
  
  
  
参考:  
第一次深入的看到异常是如何应用在crackme中的,参考了:

  hume的DRx寄存器的使用(精华6):
  http://www.pediy.com/bbshtml/BBS6/pediy6751.htm

  forgot的加壳技术--DRx解码阻止调试
  http://www.pediy.com/bbshtml/BBS6/pediy6653.htm

版权: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (10)
雪    币: 370
活跃值: (78)
能力值: ( LV9,RANK:970 )
在线值:
发帖
回帖
粉丝
2
aalloverred最好翻译成英文,对方是越南人。
2006-6-11 22:07
0
雪    币: 342
活跃值: (318)
能力值: ( LV12,RANK:740 )
在线值:
发帖
回帖
粉丝
3
晕,版主好快的动作。pfpf。
aalloverred最好翻译成英文,对方是越南人。

没有什么技术含量,算了!(其实光看代码就行了:D)
2006-6-11 22:10
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
congratulations!

hm.. that's nice aalloverred =)
btw, tute translated with google is unreadable in some places.. maybe u can make english comments?
2006-6-11 22:31
0
雪    币: 342
活跃值: (318)
能力值: ( LV12,RANK:740 )
在线值:
发帖
回帖
粉丝
5
有些bug,嗯,今天晚上吧,争取在球赛开始前修正一下。

to lord_Phoenix:

Will email you some comments in english very soon.
2006-6-12 13:02
0
雪    币: 342
活跃值: (318)
能力值: ( LV12,RANK:740 )
在线值:
发帖
回帖
粉丝
6
00401D64   .  6A 1E         PUSH 1E                                  ; /Count = 1E (30.)
  00401D66   .  68 24644000   PUSH crackme_.00406424                   ; |Buffer = crackme_.00406424
  00401D6B   .  6A 0A         PUSH 0A                                  ; |ControlID = A (10.)
  00401D6D   .  FF75 08       PUSH DWORD PTR SS:[EBP+8]                ; |hWnd
  00401D70   .  E8 09060000   CALL <JMP.&user32.GetDlgItemTextA>       ; \GetDlgItemTextA

  00401DB8   .  60            PUSHAD
  00401DB9   .  6A 1F         PUSH 1F                                  ; /Count = 1F (31.)
  00401DBB   .  68 38644000   PUSH crackme_.00406438                   ; |Buffer = crackme_.00406438
  00401DC0   .  6A 0B         PUSH 0B                                  ; |ControlID = B (11.)
  00401DC2   .  FF75 08       PUSH DWORD PTR SS:[EBP+8]                ; |hWnd
  00401DC5   .  E8 B4050000   CALL <JMP.&user32.GetDlgItemTextA>       ; \GetDlgItemTextA

原来是这里,两次读取编辑框的内容存储地址00406424和00406438之间只差014h(20.),所以用户名超过20的部分会被所输入的序列号覆盖,不知道是crackme的bug还是作者有意的。如果这是crackme的一部分,以后再修正。

另外,不太理解作者要求什么样的patch,这样不知道可不可以:
00401E38      53            PUSH EBX
00401E39      68 24634000   PUSH crackme_.00406324                   ;  ASCII "%u"
patch成为:
00401E38     /E9 A5000000   JMP crackme_.00401EE2
00401E3D     |90            NOP

2006-6-12 20:50
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
got english comments =)
and crackme is solved by deroko/ARTeam too

thnx for paying time to my little crackme ;)
2006-6-14 16:18
0
雪    币: 97697
活跃值: (200834)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
8
最初由 lord_Phoenix 发布
got english comments =)
and crackme is solved by deroko/ARTeam too

thnx for paying time to my little crackme ;)


Welcome you to come us the forum.

I frequently in CRACKL@B.
2006-6-14 16:25
0
雪    币: 342
活跃值: (318)
能力值: ( LV12,RANK:740 )
在线值:
发帖
回帖
粉丝
9
对这个crackme有兴趣的看看deroko/ARTeam的正解 我只是小打小闹

to lord_Phoenix:I read the deroko's solution and got the real idea of your crackme.That is great.
2006-6-15 00:03
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
soon #7 will come =)
2006-6-15 17:43
0
雪    币: 97697
活跃值: (200834)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
11
THANKS!
2006-6-15 17:45
0
游客
登录 | 注册 方可回帖
返回
//