【文章標題】: 失業的娛樂-IDA逆向工程入門(三)-匯編程序(2)
【文章作者】: layper
【作者郵箱】: layper@yahoo.comcn
【作者主頁】: http://blog.csdn.net/layper/
【下載地址】: 自己搜索下載
【編寫語言】: asm
【使用工具】: IDA\reshack\radasm\
【作者聲明】: 只是感興趣,沒有其他目的。失誤之處敬請諸位大俠賜教!
--------------------------------------------------------------------------------
【詳細過程】
多謝大家的支持,特別是fly還關心我的工作問題,無已回報,只能繼續寫些小文供大家批評了!!!
上一篇我們所逆的是非常簡單的win32匯編,總共才兩個api函數,一個消息框和ExitProcess函數,這篇我們就涉及一個真正的窗口
程序firstwindows,我學匯編是看了羅云彬的《windows環境下匯編語言程序設計》才入門的,我直接拿里面的例子來講吧,如果作
者覺得不合適,我會刪去的!!!!!
順便講一下學習逆向工程的方法,這個跟學脫殼方法類似,你先用一種語言寫一個程序(剛開始比較簡單的),編譯后用IDA或者
其他工具反匯編,觀察源代碼和反匯編代碼有什么異同,想辦法在逆向代碼中逐漸靠近源代碼,最后再把他整理到編譯工具中不
斷編譯,在編譯器中看那里出錯,逐步修改,直至成功,最后總結經驗,這樣就會逐步提高了.
限于篇幅,我只把完整源碼貼出來,未修改的反匯編在壓縮包內的1.asm,請自行查看
firstwindows源碼
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Sample code for < Win32ASM Programming >
; by 羅云彬, http://asm.yeah.net
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; FirstWindow.asm
; 窗口程序的模板代碼
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令進行編譯和鏈接:
; ml /c /coff FirstWindow.asm
; Link /subsystem:windows FirstWindow.obj
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.386
.model flat,stdcall
option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定義
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include gdi32.inc
includelib gdi32.lib
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 數據段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
hInstance dd ?
hWinMain dd ?
.const
szClassName db 'MyClass',0
szCaptionMain db 'My first Window !',0
szText db 'Win32 Assembly, Simple and powerful !',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代碼段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 窗口過程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcWinMain proc uses ebx edi esi,hWnd,uMsg,wParam,lParam
local @stPs:PAINTSTRUCT
local @stRect:RECT
local @hDc
mov eax,uMsg
;********************************************************************
.if eax == WM_PAINT
invoke BeginPaint,hWnd,addr @stPs
mov @hDc,eax
invoke GetClientRect,hWnd,addr @stRect
invoke DrawText,@hDc,addr szText,-1,\
addr @stRect,\
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd,addr @stPs
;********************************************************************
.elseif eax == WM_CLOSE
invoke DestroyWindow,hWinMain
invoke PostQuitMessage,NULL
;********************************************************************
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
;********************************************************************
xor eax,eax
ret
_ProcWinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain proc
local @stWndClass:WNDCLASSEX
local @stMsg:MSG
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
;********************************************************************
; 注冊窗口類
;********************************************************************
invoke LoadCursor,0,IDC_ARROW
mov @stWndClass.hCursor,eax
push hInstance
pop @stWndClass.hInstance
mov @stWndClass.cbSize,sizeof WNDCLASSEX
mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW
mov @stWndClass.lpfnWndProc,offset _ProcWinMain
mov @stWndClass.hbrBackground,COLOR_WINDOW + 1
mov @stWndClass.lpszClassName,offset szClassName
invoke RegisterClassEx,addr @stWndClass
;********************************************************************
; 建立并顯示窗口
;********************************************************************
invoke CreateWindowEx,WS_EX_CLIENTEDGE,offset szClassName,offset szCaptionMain,\
WS_OVERLAPPEDWINDOW,\
100,100,600,400,\
NULL,NULL,hInstance,NULL
mov hWinMain,eax
invoke ShowWindow,hWinMain,SW_SHOWNORMAL
invoke UpdateWindow,hWinMain
;********************************************************************
; 消息循環
;********************************************************************
.while TRUE
invoke GetMessage,addr @stMsg,NULL,0,0
.break .if eax == 0
invoke TranslateMessage,addr @stMsg
invoke DispatchMessage,addr @stMsg
.endw
ret
_WinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
call _WinMain
invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start
在radasm編譯通過.
用IAD反匯編載入完成后,點擊文件-創建文件-創建asm文件就得到未經修改的反匯編后得到的1.asm文件(有點繞口:)),直接用
radasm打開,在radasm中ctrl+f5構建并運行看看結果怎樣,呵呵,出錯了.
因為一步一步來講比較長,我先把操作過程寫下來,在慢慢解釋,1.asm修改如下:
(一)增加模式定義\options語句\還原include語句
.686p
.mmx
.model flat,stdcall
option casemap:none
include WINDOWS.INC
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
(二)刪除結構MSG\POINT\PAINTSTRUCT\RECT,并把余下的結構移動到 includelib user32.lib之后,即第一步之后,
然后做如下修改:
tagMSG struc ; (sizeof=0x1C, standard type)
hwnd dd ? ; offset
message dd ?
wParam dd ?
lParam dd ?
time dd ?
pt POINT ? ;這里修改為pt POINT <>
tagMSG ends
tagPAINTSTRUCT struc ; (sizeof=0x40, standard type)
hdc dd ? ; offset
fErase dd ?
rcPaint RECT ? ;這里修改為rcPaint RECT <>
fRestore dd ?
fIncUpdate dd ?
rgbReserved db 32 dup(?)
tagPAINTSTRUCT ends
(三)對函數的局部變量進行修改
一共三個函數start\sub_401000和sub_401089,修改如下
sub_401089:
sub_401089 proc near ; CODE XREF: startp
Msg = MSG ptr -4Ch
var_30 = WNDCLASSEXA ptr -30h
修改為:
sub_401089 proc near ; CODE XREF: startp
LOCAL Msg:MSG
LOCAL var_30:WNDCLASSEXA
sub_401000:
sub_401000 proc near ; DATA XREF: sub_401089+43o
hDC = dword ptr -54h
Rect = tagRECT ptr -50h
Paint = PAINTSTRUCT ptr -40h
hWnd = dword ptr 8
Msg = dword ptr 0Ch
wParam = dword ptr 10h
lParam = dword ptr 14h
修改為:
sub_401000 proc uses ebx edi esi ,hWnd,Msg,wParam,lParam ; DATA XREF: sub_401089+43o
LOCAL hDC
LOCAL Rect:tagRECT
LOCAL Paint:PAINTSTRUCT
(四)_text段修改
刪除
在_text段前增加.code
_text segment para public 'CODE' use32
assume cs:_text
;org 401000h
assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
和
_text ends
注意:中間的代碼不要刪除!!!
(五)刪除align 40h
(六)移動修改_data段
在.code前增加.data,并且把_data段移動到這里
把
_data segment para public 'DATA' use32
assume cs:_data
;org 403000h
和 ; sub_401089+A6r
_data ends
刪除
注意:中間的代碼不要刪除!!!
(七)修改sub_401000的hWnd,只要出現有的都修改為hWnd1.
(八)刪除_idata段
(九)
把函數含有[ebp+變量]的代碼全部修改為變量
sub_401089的代碼
[ebp+var_30] 改為 var_30
[ebp+var_30.hCursor] 改為 var_30.hCursor
[ebp+var_30.hInstance] 改為 var_30.hInstance
[ebp+var_30.cbSize] 改為 var_30.cbSize
[ebp+var_30.style] 改為 var_30.style
[ebp+var_30.lpfnWndProc] 改為 var_30.lpfnWndProc
[ebp+var_30.hbrBackground] 改為 var_30.hbrBackground
[ebp+var_30.lpszClassName] 改為 var_30.lpszClassName
[ebp+Msg] 改為 Msg
sub_401000的代碼
[ebp+hDC] 改為 hDC
[ebp+Rect] 改為 Rect
[ebp+Paint] 改為 Paint
[ebp+hWnd1] 改為 hWnd1
[ebp+Msg] 改為 Msg
[ebp+wParam] 改為 wParam
[ebp+lParam] 改為 lParam
(十)刪掉函數多余的開頭
sub_401089處:
sub_401089 proc near ; CODE XREF: startp
LOCAL Msg:MSG
LOCAL var_30:WNDCLASSEXA
push ebp ;刪掉
mov ebp, esp 刪掉
add esp, 0FFFFFFB4h ;刪掉
sub_401000處:
sub_401000 proc near uses ebx edi esi ,hWnd1,Msg,wParam,lParam ; DATA XREF: sub_401089+43o
LOCAL hDC
LOCAL Rect:tagRECT
LOCAL Paint:PAINTSTRUCT
push ebp ;刪掉
mov ebp, esp ;刪掉
add esp, 0FFFFFFACh ;刪掉
push ebx ;刪掉
push edi ;刪掉
push esi ;刪掉
--------------------------------------------------------------------------------
【經驗總結】
其實只要你把反編譯的代碼按照radasm的提示一步一步修改就可以了.
解釋:
(一)
這一步我在上篇已經解釋的比較明白了.因為我們匯編開頭就是那么幾句代碼.
include語句加回去這個是因為我們編譯的是匯編程序,這樣肯定要用到庫.如果IDA使用生成的_data段
就非常容易出錯.畢竟它只是"識別"而不是源碼!!!!!!!
(二)
(1)刪除結構體MSG\POINT\PAINTSTRUCT\RECT
我們進行了第一步操作后,用radasm進行構建,就會提示我們
D:\masm32\Include\WINDOWS.INC(7873) : error A2163: : POINT
D:\masm32\Include\WINDOWS.INC(7874) : error A2163: : POINT
D:\masm32\Include\WINDOWS.INC(8841) : error A2163: : MSG
D:\masm32\Include\WINDOWS.INC(8842) : error A2163: : MSG
D:\masm32\Include\WINDOWS.INC(8843) : error A2163: : MSG
D:\masm32\Include\WINDOWS.INC(8844) : error A2163: : MSG
D:\masm32\Include\WINDOWS.INC(8845) : error A2163: : MSG
D:\masm32\Include\WINDOWS.INC(8846) : error A2163: : MSG
D:\masm32\Include\WINDOWS.INC(8846) : fatal error A1016:
構建時發生錯誤.
總共編譯時間 271 毫秒
這個這個意思說我們的庫文件出錯,這個可能嗎?當然也有可能,但我想你首先應該想到是你的反匯編代碼錯.
先查詢一下windows.inc"出錯"的到底是什么
POINT STRUCT
x DWORD ? ;7873行
y DWORD ? ;7874行
POINT ENDS
MSG STRUCT
hwnd DWORD ? ;8841
message DWORD ? ;8842
wParam DWORD ? ;8843
lParam DWORD ? ;8844
time DWORD ? ;8845
pt POINT <> ;8846
MSG ENDS
呵呵,你再看看反匯編代碼開頭
MSG struc ; (sizeof=0x1C, standard type)
hwnd dd ? ; offset
message dd ?
wParam dd ?
lParam dd ?
time dd ?
pt POINT ?
MSG ends
; ---------------------------------------------------------------------------
POINT struc ; (sizeof=0x8, standard type)
x dd ?
y dd ?
POINT ends
明白怎么是這樣了吧?我們反匯編代碼重復定義了結構msg,point所以要把他們刪除.同理PAINTSTRUCT\RECT也刪除了.
(2)移動剩余結構到include語句后.
這一步我是為了省事,剩余三個結構
tagMSG struc ; (sizeof=0x1C, standard type)
hwnd dd ? ; offset
message dd ?
wParam dd ?
lParam dd ?
time dd ?
pt POINT ?
tagMSG ends
; ---------------------------------------------------------------------------
WNDCLASSEXA struc ; (sizeof=0x30, standard type)
cbSize dd ?
style dd ?
lpfnWndProc dd ? ; offset
cbClsExtra dd ?
cbWndExtra dd ?
hInstance dd ? ; offset
hIcon dd ? ; offset
hCursor dd ? ; offset
hbrBackground dd ? ; offset
lpszMenuName dd ? ; offset
lpszClassName dd ? ; offset
hIconSm dd ? ; offset
WNDCLASSEXA ends
; ---------------------------------------------------------------------------
tagRECT struc ; (sizeof=0x10, standard type)
left dd ?
top dd ?
right dd ?
bottom dd ?
tagRECT ends
; ---------------------------------------------------------------------------
tagPAINTSTRUCT struc ; (sizeof=0x40, standard type)
hdc dd ? ; offset
fErase dd ?
rcPaint RECT ?
fRestore dd ?
fIncUpdate dd ?
rgbReserved db 32 dup(?)
tagPAINTSTRUCT ends
其中tagMSG和tagPAINTSTRUCT結構分別用到了POINT結構和RECT結構,剛才我們刪了,只有windows.inc中有
所以直接把他們剪切到這里省去出錯的機會.
(3)修改結構
tagMSG結構和tagPAINTSTRUCT結構修改,我是參照windows.inc結構定義方法.結構中用結構<> :)
這個不一定完全正確,想研究這方面多閱讀.inc文件
(三)函數修改
在反匯編代碼中只要出現proc的,到現在為止我都看成是函數!!!
IDA反匯編都它的函數都變成這個樣子
sub_401000 proc near ; DATA XREF: sub_401089+43o
hDC = dword ptr -54h ;注意這里是減(-)
Rect = tagRECT ptr -50h
Paint = PAINTSTRUCT ptr -40h
hWnd = dword ptr 8 ;這里其實是加(+)
Msg = dword ptr 0Ch
wParam = dword ptr 10h
lParam = dword ptr 14h
push ebp
mov ebp, esp
add esp, 0FFFFFFACh
push ebx
push edi
push esi
mov eax, [ebp+Msg]
這里就會出現一個問題.我們先前又刪結構又改結構,而這里又用到結構,不修改編譯也會出錯的.
我們改成比較正規的win32匯編程序格式.
剛才我提示加減的地方,總結一條規律給大家:
函數開頭 xx = 結構 - xxh 這個就是函數的局部變量,可用local xx:結構替換.
函數開頭 xx = dword ptr xxh 這個是函數的參數,函數可改為
函數名 proc xx
(四)_text段修改
代碼段
_text segment para public 'CODE' use32
assume cs:_text
;org 401000h
assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
和
_text ends
IDA這種段寫法有很大的弊端,也是引起我們修改后的代碼編譯不通過的一個很重要原因.(具體我還說不上來,我還很菜)
(五)刪除align 40h
align是反匯編代碼不通過編譯的一種常見錯誤.
(六)移動修改_data段
一般來說_data段是我們的數據段,一般我們放在前面.(呵呵,代碼順序也很重要)
(七)在數據段中
hWnd dd ? ; DATA XREF: sub_401000+54r
; sub_401089+94w sub_401089+9Br
; sub_401089+A6r
提示hWnd是函數sub_401089的,并不是sub_401000,所以要重命名他們.
(八)刪除_idata段
include語句已經有了函數定義,再保留這里就會出錯.
(九)
把函數含有[ebp+變量]的代碼全部修改為變量
[ebp+]這個是編譯器加上去的,我們直接用的話,編譯后會變成[ebp+ebp+變量],容易出錯.
(十)刪掉函數多余的開頭
反匯編代碼中,編譯器為你加上象這樣的代碼
push ebp
mov ebp, esp
add esp, 0FFFFFFB4h
如果你直接編譯的話代碼就變成了:
push ebp
mov ebp, esp
add esp, 0FFFFFFB4h
push ebp
mov ebp, esp
add esp, 0FFFFFFB4h
重新編譯也容易出錯,所以要刪去.
同理,要注意函數結束地方看看是否要刪去.
(十一)
這里說一點跟上一篇不同的是沒有刪除_rdata,因為這里有我們程序要的數據,所以沒刪除.如
果你還想優化自己弄了!!!
呵呵,終于弄完這篇了,把它整理好花了好大工夫.錯誤難免,請多包涵!!!!
--------------------------------------------------------------------------------
【版權聲明】: 本文原創于看雪技術論壇, 轉載請注明作者并保持文章的完整, 謝謝!
2007年03月04日 12:21:20