作者: 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
版权: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!