-
-
一个清爽整洁的Crackme的逆向兼形式上去除Bug
-
发表于:
2006-7-30 19:55
6694
-
一个清爽整洁的Crackme的逆向兼形式上去除Bug
下载地址:http://www.crackmes.de/users/disip/creakme_001
使用工具:
反编译工具:IDA,Resource Hacker
程序开发包:MASM32
一遍又一遍地说差不多的话总觉得烦,干脆直接贴IDA输出的内容算了
======================================================================
; 这些等值定义是用Resource Hacker分析资源得出来的
; enum Control_IDs
ICO_MAIN = 200
DLG_MAIN = 1000
ID_EXIT = 1001
EDT_NAME = 1002
EDT_SERIAL = 1003
ID_REG = 1004
public start ; 主程序
start proc near
push NULL ; lpModuleName
call GetModuleHandleA
mov hInstance, eax
call InitCommonControls ; 这个函数好象没起作用,
; 不过既然无害,姑且留下它,
; 当然源代码中就应该有包含comctl32.inc(lib)
push 0 ; dwInitParam
push offset _ProcDlgMain ; lpDialogFunc
; (通过这个参数找到对话框过程)
push NULL ; hWndParent
push DLG_MAIN ; lpTemplateName
push hInstance ; hInstance
call DialogBoxParamA
push eax ; uExitCode
call ExitProcess
start endp
; BOOL __stdcall ProcDlgMain(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
_ProcDlgMain proc near ; DATA XREF: start+13o
; 对话框过程
hDlg = dword ptr 8
uMsg = dword ptr 0Ch
wParam = dword ptr 10h
lParam = dword ptr 14h
push ebp
mov ebp, esp
mov eax, [ebp+hDlg]
mov hDlgMain, eax
mov eax, [ebp+uMsg]
cmp eax, WM_INITDIALOG
jnz short loc_40106A
push ICO_MAIN ; lpIconName
push hInstance ; hInstance
call LoadIconA
push eax ; lParam
push ICON_BIG ; wParam
push WM_SETICON ; Msg
push [ebp+hDlg] ; hWnd
call SendMessageA
jmp loc_4010F8
loc_40106A: ; CODE XREF: _ProcDlgMain+13j
cmp eax, WM_COMMAND
jnz short loc_4010E9
mov eax, [ebp+wParam]
cmp eax, ID_EXIT
jnz short loc_401089
push 0 ; lParam
push 0 ; wParam
push WM_CLOSE ; Msg
push [ebp+hDlg] ; hWnd
call SendMessageA
loc_401089: ; CODE XREF: _ProcDlgMain+49j
cmp eax, ID_REG
jnz short loc_4010F8
push 0FFh ; nMaxCount
push offset szSerial ; lpString
push EDT_SERIAL ; nIDDlgItem
push [ebp+hDlg] ; hDlg
call GetDlgItemTextA
push 0FFh ; nMaxCount
push offset szUserName ; lpString
push EDT_NAME ; nIDDlgItem
push [ebp+hDlg] ; hDlg
call GetDlgItemTextA
cmp eax, 3 ; 用户名必须至少3个字符
jb short loc_4010CA
mov uNameLen, eax
jmp short loc_4010E0
loc_4010CA: ; CODE XREF: _ProcDlgMain+91j
push MB_OK ; uType
push offset szCaption ; ".:: DiS[IP] Programer ::."
push offset szTxtTooShort ; "Min 3 Char on Name!!!"
push [ebp+hDlg] ; hWnd
call MessageBoxA
jmp short loc_4010E5
loc_4010E0: ; CODE XREF: _ProcDlgMain+98j
call sub_4010FE ; 算法过程
loc_4010E5: ; CODE XREF: _ProcDlgMain+AEj
xor eax, eax
jmp short loc_4010F8
loc_4010E9: ; CODE XREF: _ProcDlgMain+3Fj
cmp eax, WM_CLOSE
jnz short loc_4010F8
push 0 ; nResult
push [ebp+hDlg] ; hDlg
call EndDialog
loc_4010F8: ; CODE XREF: _ProcDlgMain+35j
; _ProcDlgMain+5Ej _ProcDlgMain+B7j
; _ProcDlgMain+BCj
xor eax, eax
leave
retn 10h
_ProcDlgMain endp
sub_4010FE proc near ; CODE XREF: _ProcDlgMain:loc_4010E0p
xor eax, eax
xor ebx, ebx
xor ecx, ecx
mov uLoopCount, eax
loc_401109: ; CODE XREF: sub_4010FE+4Fj
xor ebx, ebx
mov bl, szUserName[eax]
cmp bl, 'Z' ; 如果等于'Z','z','9'之一则减一
jnz short loc_401118
dec bl
loc_401118: ; CODE XREF: sub_4010FE+16j
cmp bl, 'z'
jnz short loc_40111F
dec bl
loc_40111F: ; CODE XREF: sub_4010FE+1Dj
cmp bl, '9'
jnz short loc_401126
dec bl
loc_401126: ; CODE XREF: sub_4010FE+24j
push eax
add eax, 'a'
add bh, al ; bh == al + 'a'
pop eax
add bl, 1 ; 这里加了1
mov cx, word ptr szSerial[eax*2]
inc eax
push eax
cmp bx, cx
jnz short badboy
add uLoopCount, 1
pop eax
cmp eax, uNameLen
jnz short loc_401109
push eax
mov eax, uLoopCount ; 这里设个变量似乎没必要,
; 因为eax与uLoopCount的初始值是相等的,
; 唯一让他们不相等的可能就是在inc eax以后
; 还没给uLoopCount加1就退出循环,
; 换言之只可能在cmp bx, cx以后跳转时发生这种事情,
; 但这个跳转直接跳到了错误信息分支,
; 与等到验证eax与uLoopCount不等后跳到的地方是一样的,
; 所以说uLoopCount这个变量显得多此一举
cmp eax, uNameLen
pop eax
jnz short badboy
push MB_OK ; uType
push offset szCaption ; ".:: DiS[IP] Programer ::."
push offset aRegisterCompli ; "Register complite!!!"
push hDlgMain ; hWnd
call MessageBoxA
retn
badboy: ; CODE XREF: sub_4010FE+3Fj
; sub_4010FE+5Ej
pop eax
push MB_OK ; uType
push offset szCaption ; ".:: DiS[IP] Programer ::."
push offset aNameOrPassword ; "Name or Password is BAD!!"
push hDlgMain ; hWnd
call MessageBoxA
retn
sub_4010FE endp
; 常数数据段
; char aNameOrPassword[]
aNameOrPassword db 'Name or Password is BAD!!',0 ; DATA XREF: sub_4010FE+80o
; char aRegisterCompli[]
aRegisterCompli db 'Register complite!!!',0 ; DATA XREF: sub_4010FE+67o
; char szCaption[]
szCaption db '.:: DiS[IP] Programer ::.',0 ; DATA XREF: _ProcDlgMain+9Co
; sub_4010FE+62o sub_4010FE+7Bo
; char szTxtTooShort[]
szTxtTooShort db 'Min 3 Char on Name!!!',0 ; DATA XREF: _ProcDlgMain+A1o
align 10h
; 数据段
; HWND hDlgMain
hDlgMain dd 0 ; DATA XREF: _ProcDlgMain+6w
; sub_4010FE+6Cr sub_4010FE+85r
; char szUserName[]
szUserName db 6 dup(0) ; DATA XREF: _ProcDlgMain+7Co
; sub_4010FE+Dr
; CHAR szSerial
szSerial db 0Bh dup(0) ; DATA XREF: _ProcDlgMain+65o
; sub_4010FE+32r
uNameLen dd 0 ; DATA XREF: _ProcDlgMain+93w
; sub_4010FE+49r sub_4010FE+57r
uLoopCount dd 0 ; DATA XREF: sub_4010FE+6w
; sub_4010FE+41w sub_4010FE+52r
; HINSTANCE hInstance
hInstance dd 0 ; DATA XREF: start+7w start+1Fr
; _ProcDlgMain+1Ar
align 200h
end start
======================================================================
上面提到的Bug在什么地方呢?注意标号loc_401089下的两个GetDlgItemText调用,分别是
取序列号和用户名的,在最后一个参数nMaxCount中给的值都是0FFh,这就意味着如果用户
的输入足够长,在文本框中取得的字符数目最大可以达到255;但另一方面szUserName和
szSerial这两个预留缓冲区都只有寥寥几个到十几个字节长,这就造成这样的情况:一旦
输入的用户名长度超过5或者输入的序列号长度超过10,由GetDlgItemText获得的字符串就
会与其他有用内存单元发生交叠,从而导致有用数据被覆盖之类的错误。在源代码里我把
这两个缓冲区的长度都改成了100h(所谓的“形式修正Bug”),但是大家在尝试注册这个
程序时,还是不要输入超过5字符的用户名或者超过10字符的序列号,以免产生意想不到的
后果。
注册算法很简单:首先是用户名必须至少3个字符,把用户名串中的字符'z','Z',
'9'保持不变,其他字符都加上1;然后在串的每个字符之后依次插入另一个字符,插入的字
符等于原字符在串中的位置序号加上60h,也就是说第一个字符后插入'a',第二个字符后插
入'b',依次类推。例如:
用户名: PEDIY
除'z','Z','9'外各字符加1: QFEJZ
插入代表顺序的字符: QaFbEcJdZe ---------序列号
用户名: Z9zz
序列号: Za9bzczd
上传文件内容:Noname3.asm 源代码
1.rc 资源脚本
Icon_1.ico 图标
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!