彙編ring3下實現HOOK API
導讀:
標 題:彙編ring3下實現HOOK API【原創】
作 者:非安全
時 間:2006-07-12,18:39
鏈 接:http://bbs.pediy.com/showthread.php?t=28895
彙編ring3下實現HOOK API(二次修改版)
【文章標題】彙編ring3下實現HOOK API
【文章作者】nohacks(非安全,hacker0058)
【作者主頁】hacker0058.ys168.com
【文章出處】看雪論壇(bbs.pediy.com)
===========================[ 彙編ring3下實現HOOK API ]=======================
Author: nohacks
Emil: [email protected]
Version: 1.1
Date: 7.18.2006
=====[ 1. 內容 ]=============================================
1. 內容
2. 介紹
2.1 什麼叫Hook API?
2.2 API Hook的應用介紹
2.3 API Hook的原則
3. 掛鉤方法
3.1 改寫IAT導入表法
3.2 改寫內存地址JMP法
4. 彙編實現
4.1. 代碼
4.2. 分析
5. 結束語
=====[ 2. 介紹 ]================================================
這篇文章是有關在OS Windows下掛鉤API函數的方法。所有例子都在基於NT技術的Windows版本NT4.0
及以上有效(Windows NT 4.0, Windows 2000, Windows XP)。可能在其它Windows系統也會有效。
你應該比較熟悉Windows下的進程、彙編器、和一些API函數,才能明白這篇文章裏的內容。
=====[2.1 什麼叫Hook API?]=================================
所謂Hook就是鉤子的意思,而API是指Windows開放給程序員的編程接口,使得在用戶級別下可
以對操作系統進行控制,也就是一般的應用程序都需要調用API來完成某些功能,Hook API的意思
就是在這些應用程序調用真正的系統API前可以先被截獲,從而進行一些處理再調用真正的API來完
成功能。
====[2.2 API Hook的應用介紹]=================================
API Hook技術應用廣泛,常用於屏幕取詞,網絡防火牆,病毒木馬,加殼軟件,串口紅外通訊,遊戲外
掛,internet通信等領域API HOOK的中文意思就是鉤住API,對API進行預處理,先執行我們的函數,例
如我們用API Hook技術掛接ExitWindowsEx API函數,使關機失效,掛接ZwOpenProcess函數(如:老王的
EncryptPE),隱藏進程等等......
====[2.3 API Hook的原則]=====================================
HOOK API有一個原則,這個原則就是:被HOOK的API的原有功能不能受到任何影響。就象醫生救人,
如果把病人身體裏的病毒殺死了,病人也死了,那麼這個“救人”就沒有任何意義了。如果你HOOK API
之後,你的目的達到了,但API的原有功能失效了,這樣不是HOOK,而是REPLACE,操作系統的正常功能
就會受到影響,甚至會崩潰。
====[ 3. 掛鉤方法 ]==============================================
總的來說,常用的掛鉤API方法有以下兩種:
3.1 改寫IAT導入表法
修改可執行文件的IAT表(即輸入表)因爲在該表中記錄了所有調用API的函數地址,則只需將這些
地址改爲自己函數的地址即可,但是這樣有一個侷限,因爲有的程序會加殼,這樣會隱藏真實的IAT表
,從而使該方法失效。
3.2 改寫內存地址JMP法
直接跳轉,改變API函數的入口或出口的幾個字節,使程序跳轉到自己的函數,該方法不受程序加殼
的限制。這種技術,說起來也不復雜,就是改變程序流程的技術。在CPU的指令裏,有幾條指令可以改變
程序的流程:JMP,CALL,INT,RET,RETF,IRET等指令。理論上只要改變API入口和出口的任何機器碼
,都可以HOOK,下面我就說說常用的改寫API入口點的方法:
因爲工作在Ring3模式下,我們不能直接修改物理內存,只能一個一個打開修改,但具體的方法又分成
好幾種,我給大家介紹幾種操作思路:
<1>首先改寫API首字節,要實現原API的功能需要調用API時先還原被修改的字節,然後再調用原API,調
用完後再改回來,這樣實現有點麻煩,但最簡單,從理論上說有漏HOOK的可能,因爲我們先還原了API,如果
在這之前程序調用了API,就有可能逃過HOOK的可能!
(2)把被覆蓋的彙編代碼保存起來,在替代函數裏模擬被被覆蓋的功能,然後調用原函數(原地址+被覆
蓋長度).但這樣會產生一個問題,不同的彙編指令長度是不一樣的(比如說我們寫入的JMP指令佔用5個字
節,而我們寫入的這5個字節佔用的位置不一定正好是一個或多個完整的指令,有可能需要保存7個字節,
纔不能打亂程序原有的功能,需要編寫一個龐大的判斷體系來判斷指令長度,網上已經有這樣的彙編程序
(Z0MBiE寫的LDE32),非常的複雜!
(3)把被HOOK的函數備份一下,調用時在替代函數裏調用備份函數.爲了避免麻煩,可以直接備份整個
DLL缺點就是太犧牲內存,一般不推薦使用這種方法!
=====[ 4. 彙編實現 ]==============================================
本文就是建立在第2種方法之上的!本着先易後難的原則,今天我們先來說說它的第1種操作思路.
我們拿API函數ExitWindowsEx來說明,下面是我在OD裏攔下的ExitWindowsEx原入口部分
77D59E2D $ 8BFF mov edi,edi
77D59E2F . 55 push ebp
77D59E30 . 8BEC mov ebp,esp
77D59E32 . 83EC 18 sub esp,18
......
如果我們把ExitWindowsEx的入口點改爲下面的,會出現什麼情況?
77D59E2D B8 00400000 mov eax,4000
77D59E32 FFE0 jmp eax
......
我們可想而知,程序執行到77D59E32處就會改變流程跳到00400000的地方
如果我們的00400000處是這樣的子程:
=======================
MyAPI proc bs:DWORD ,dwReserved:DWORD ;和ExitWindowsEx一樣帶2個參數
做你想做的事
......
這裏放API入口點改回原機器碼的代碼
如果你是備份的整個DLL,就直接調用備份API,不用改來改去了,不會有漏勾API的可能!
invoke ExitWindowsEx,bs,dwReserved
這裏放HOOK API的代碼
.endif
mov eax,TRUE
ret
=======================
這裏的MyAPI是和ExitWindowsEx參數一樣的的子程,因爲程序是在API的入口部分跳轉的,根據
stdcall約定(參數數據從右向左依次壓棧,恢復堆棧的工作交由被調用者),此時堆棧還沒有恢復,我們
在子程裏取出的參數數據依然有效,我們可以在這裏執行自己的代碼,你可以決定是否繼續按原參數或改
變參數後再調用原API,也可以什麼都不做,當然在調用之前,我們要先還原我們修改過的API(可以事先用
API函數ReadProcessMemory讀出原API的前幾個字節備份之),調用完後再改回來繼續HOOK API,不過這種
方法有漏API的可能(原因前面已經說了),你如果覺得這個方法不妥,因爲一般系統DLL都不大,你可以備
份整個DLL.
下面我就列出ring3下HOOK API的幾個步驟:
1.得到要掛勾API的入口點
2.修改API的入口點所在頁的頁面保護爲可讀寫模式
3.用ReadProcessMemory讀出API的入口點開始的幾字節備份
4.用WriteProcessMemory修改API的入口點象這樣的形式:
mov eax,4000
jmp eax
其中的4000要用和原API參數一樣的子程序地址代替
在這個子程序裏我們決定用什麼參數再調用原API,不過調用之前要用備份的前8字節改回來
調用之後在掛勾,如此反覆.
=====[ 4.1. 代碼 ]==============================================
前面所講的是本進程掛勾,我們要掛勾所有進程,可以用全局勾子,需要單獨的一個DLL,我們可
以在DLL的DLL_PROCESS_ATTACH事件裏來HOOK API
=================================hookdll.dll==========================
.486
.model flat,stdcall ;參數的傳遞約定是stdcall(從右到左,恢復堆棧的工作交由被調用者)
option casemap:none
include /masm32/include/windows.inc
include /masm32/include/kernel32.inc
includelib /masm32/lib/kernel32.lib
include /masm32/include/user32.inc
includelib /masm32/lib/user32.lib
HOOKAPI struct
a byte ?
PMyapi DWORD ?
d BYTE ?
e BYTE ?
HOOKAPI ends
子程序聲明
WriteApi proto :DWORD ,:DWORD,:DWORD,:DWORD
MyAPI proto :DWORD ,:DWORD
GetApi proto :DWORD,:DWORD
已初始化數據
.data
hInstance dd 0
WProcess dd 0
hacker HOOKAPI <>
CommandLine LPSTR ?
Papi1 DWORD ?
Myapi1 DWORD ?
ApiBak1 db 10 dup(?)
DllName1 db "user32.dll",0
ApiName1 db "ExitWindowsEx",0
mdb db "下面的程序想關閉計算機,要保持阻止嗎?",0
未初始化數據
.data?
hHook dd ?
hWnd dd ?
程序代碼段
.code
DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD
.if reason==DLL_PROCESS_ATTACH ;當DLL加載時產生此事件
push hInst
pop hInstance
invoke GetCommandLine
mov CommandLine,eax ;取程序命令行
初始化
mov hacker.a,0B8h ;mov eax,
mov hacker.d PMyapi ;0x000000
mov hacker.d,0FFh ;jmp
mov hacker.e, 0E0h ;eax
invoke GetCurrentProcess ;取進程僞句柄
mov WProcess ,eax
invoke GetApi,addr DllName1,addr ApiName1 ;取API地址
mov Papi1,eax ;保存API地址
invoke ReadProcessMemory,WProcess,Papi1,addr ApiBak1,8,NULL ;備份原API的前8字節
mov hacker.PMyapi,offset MyAPI ;0x0000,這裏設置替代API的函數地址
invoke WriteApi,WProcess,Papi1, addr hacker ,size HOOKAPI ;HOOK API
.endif
.if reason==DLL_PROCESS_DETACH
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8 ;還原API
.endif
mov eax,TRUE
ret
DllEntry Endp
GetMsgProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
invoke CallNextHookEx,hHook,nCode,wParam,lParam
mov eax,TRUE
ret
GetMsgProc endp
InstallHook proc
invoke SetWindowsHookEx,WH_GETMESSAGE,addr GetMsgProc,hInstance,NULL
mov hHook,eax
ret
InstallHook endp
UninstallHook proc
invoke UnhookWindowsHookEx,hHook
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8
ret
UninstallHook endp
GetApi proc DllNameAddress:DWORD,ApiNameAddress:DWORD
invoke GetModuleHandle,DllNameAddress ;取DLL模塊句柄
.if eax==NULL
invoke LoadLibrary ,DllNameAddress ;加載DLL
.endif
invoke GetProcAddress,eax,ApiNameAddress ;取API地址
mov eax,eax
ret
GetApi endp
============================下面是核心部分=========================
WriteApi proc Process:DWORD ,Papi:DWORD,Ptype:DWORD,Psize:DWORD
LOCAL mbi:MEMORY_BASIC_INFORMATION
LOCAL msize:DWORD
返回頁面虛擬信息
invoke VirtualQueryEx,Process, Papi,addr mbi,SIZEOF MEMORY_BASIC_INFORMATION
修改爲可讀寫模式
invoke VirtualProtectEx,Process, mbi.BaseAddress,8h,PAGE_EXECUTE_READWRITE,addr
mbi.Protect
開始寫內存
invoke WriteProcessMemory,Process, Papi, Ptype,Psize ,NULL
PUSH eax
改回只讀模式
invoke VirtualProtectEx,Process,mbi.BaseAddress,8h,PAGE_EXECUTE_READ,addr mbi.Protect
pop eax
ret
WriteApi endp
替代的API,參數要和原來一樣
MyAPI proc bs:DWORD ,dwReserved:DWORD
invoke MessageBox, NULL, CommandLine, addr mdb, MB_YESNO ;彈出信息框選擇是否阻止
.if eax==7 ;如果選擇否
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8 ;先還原API
invoke ExitWindowsEx,bs,dwReserved ;再調用API
invoke WriteApi,WProcess,Papi1, addr hacker ,sizeof HOOKAPI ;調用完後再改回來
.endif
mov eax,TRUE
ret
MyAPI endp
End DllEntry
===============================hookdll.def=============================
LIBRARY hookdll
EXPORTS InstallHook
EXPORTS UninstallHook
=====[ 4.2. 分析 ]==============================================
HOOKAPI struct
a byte ?
PMyapi DWORD ?
d BYTE ?
e BYTE ?
HOOKAPI ends
爲了便於理解和使用,我定義了一個結構:這個結構有4個成員,第一個成員a,是個字節型,我用來放
0B8h(mov eax),PMyapi一個整數型,用來放我們的替代API函數的地址(0X000),第3個和第4個成員我分別
用來放JMP和EAX(jmp eax)那麼連起來就是 mov,0X0000 ; jmp eax
.if reason==DLL_PROCESS_ATTACH
push hInst
pop hInstance
invoke GetCommandLine
mov CommandLine,eax
初始化
mov hacker.a,0B8h ;mov eax,
mov hacker.d PMyapi ;0x0000
mov hacker.d,0FFh ;jmp
mov hacker.e, 0E0h ;eax
invoke GetCurrentProcess
mov WProcess ,eax
當DLL加載時,我們先保存模塊句柄,讀取程序命令行,然後初始化HOOKAPI結構,寫入我們要寫到內存的
指令(PMyapi以後寫入)並調用GetCurrentProcess取出進程僞句柄方便以後寫內存.
invoke GetApi,addr DllName1,addr ApiName1
mov Papi1,eax
invoke ReadProcessMemory,WProcess,Papi1,addr ApiBak1,8,NULL
mov hacker.PMyapi,offset MyAPI ;0x0000
invoke WriteApi,WProcess,Papi1, addr hacker ,size HOOKAPI ;HOOK API
接下來用子程GetApi取出要掛勾API的入口點,並用ReadProcessMemory讀出入口點8字節備份之,寫入
PMyapi調用子程WriteApi改寫API的入口點,這個子程我不準備詳細說了,它非常的簡單,無非就是幾個
API的調用.它的核心就是通過WriteProcessMemory改寫內存.
.if reason==DLL_PROCESS_DETACH
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8
.endif
mov eax,TRUE
ret
如果這個DLL被卸載了,那麼那個在DLL裏的替代函數(MyAPI)將是無效的,如果這個時候程序再調用這
個API,將出現非法操作,因此在DLL卸載前,我們必須還原API.
總結一下,現在只要程序加載這個DLL,這個程序的ExitWindowsEx就會被我們勾住,接下來要怎樣才能
讓所有的程序都加載這個DLL呢?這就需要安裝全局勾子:
InstallHook proc
invoke SetWindowsHookEx,WH_GETMESSAGE,addr GetMsgProc,hInstance,NULL
invoke WriteApi,WProcess,Papi1, addr hacker ,sizeof HOOKAPI
mov hHook,eax
ret
InstallHook endp
通過SetWindowsHookEx安裝勾子,最後一個參數可以決定該鉤子是局部的還是系統範圍的。如果該值
爲NULL,那麼該鉤子將被解釋成系統範圍內的,那它就可以監控所有的進程及它們的線程。
如果該函數調用成功的話,將在eax中返回鉤子的句柄,否則返回NULL。我們必須保存該句柄,因爲後
面我們還要它來卸載鉤子,可以看出,我們創建的Hook類型是WH_CALLWNDPROC類型,該類型的Hook在進程
與系統一通信時就會被加載到進程空間,從而調用dll的初始化函數完成真正的Hook,值得一提的是:因
爲要調用SetWindowsHookEx來安裝鉤子,我們GUI程序的這個DLL不會被
UnhookWidowHookEx卸載,也就只有一次DLL_PROCESS_ATTACH事件,因此這裏再要
HOOK API一次!
我們回頭來看看鉤子回調函數:
GetMsgProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
invoke CallNextHookEx,hHook,nCode,wParam,lParam
mov eax,TRUE
ret
GetMsgProc endp
可以看到這裏只是調用CallNextHookEx將消息交給Hook鏈中下一個環節處理,因爲這裏API函數
SetWindowsHookEx的唯一作用就是讓進程加載我們的dll。
UninstallHook proc
invoke UnhookWindowsHookEx,hHook
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8
ret
UninstallHook endp
要卸載一個鉤子時調用UnhookWidowHookEx函數,該函數僅有一個參數,就是欲卸載的鉤子的句柄。鉤
子卸載後我們也要還原我們GUI程序的API.
LIBRARY hookdll
EXPORTS InstallHook
EXPORTS UninstallHook
我們公開DLL裏的InstallHook和UninstallHook函數,方便程序調用,這樣我們只要在另外的程序中調
用InstallHook便可安裝全局勾子,勾住所有程序中的API:ExitWindowsEx,執行我們自定的子程!
如果不需要了,可以調用UninstallHook卸載全局勾子.
請注意:對於遠程鉤子,鉤子函數必須放到DLL中,它們將從DLL中映射到其它的進程空間中去。當
WINDOWS映射DLL到其它的進程空間中去時,不會把數據段也進行映射。簡言之,所有的進程僅共享DLL
的代碼,至於數據段,每一個進程都將有其單獨的拷貝。這是一個很容易被忽視的問題。您可能想當然
的以爲,在DLL中保存的值可以在所有映射該DLL的進程之間共享。在通常情況下,由於每一個映射該
DLL的進程都有自己的數據段,所以在大多數的情況下您的程序運行得都不錯。但是鉤子函數卻不是如
此。對於鉤子函數來說,要求DLL的數據段對所有的進程也必須相同。這樣您就必須把數據段設成共享
的:
一般來說, 目標文件有三個段, 分別是 text/data/bss 段.
.text 段放置代碼, 是隻讀且可運行段
.data 段放置靜態數據, 這些數據會被放置入 exe 文件. 這個段是可讀寫, 但是不能運行的.
.bss 段放置動態數據, 這些數據不被放入 exe 文件, 在exe文件被加載入內存後才分配的空間.
你可以通過在鏈接開關中指定段的屬性來實現:
/SECTION:name,[E][R][W][S][D][K][L][P][X]
其中S表示共享,已初期化的段名是.data,未初始化的段名是.bss。假如您想要寫一個包含鉤子函數的
DLL,而且想使它的未初始化的數據段在所有進程間共享,您必須這麼做:
link /section:.bss[S] /DLL /SUBSYSTEM:WINDOWS ..........
否則,您的全局勾子將不能正常工作!
=====[ 5. 結束語 ]================================================
我歡迎任何人提出更多的這裏沒有提到的掛鉤方法,我肯定那會有很多。同樣歡迎補充我介紹得不
是很詳細的方法。也可以把我懶得寫的其它方法完成,把源代碼發給我。這篇文檔的目的是演示掛鉤技
術的細節,我希望我做到了。
============================[ End ]========================
http://phlashback.blog.hexun.com/12573252_d.html
標 題:彙編ring3下實現HOOK API【原創】
作 者:非安全
時 間:2006-07-12,18:39
鏈 接:http://bbs.pediy.com/showthread.php?t=28895
彙編ring3下實現HOOK API(二次修改版)
【文章標題】彙編ring3下實現HOOK API
【文章作者】nohacks(非安全,hacker0058)
【作者主頁】hacker0058.ys168.com
【文章出處】看雪論壇(bbs.pediy.com)
===========================[ 彙編ring3下實現HOOK API ]=======================
Author: nohacks
Emil: [email protected]
Version: 1.1
Date: 7.18.2006
=====[ 1. 內容 ]=============================================
1. 內容
2. 介紹
2.1 什麼叫Hook API?
2.2 API Hook的應用介紹
2.3 API Hook的原則
3. 掛鉤方法
3.1 改寫IAT導入表法
3.2 改寫內存地址JMP法
4. 彙編實現
4.1. 代碼
4.2. 分析
5. 結束語
=====[ 2. 介紹 ]================================================
這篇文章是有關在OS Windows下掛鉤API函數的方法。所有例子都在基於NT技術的Windows版本NT4.0
及以上有效(Windows NT 4.0, Windows 2000, Windows XP)。可能在其它Windows系統也會有效。
你應該比較熟悉Windows下的進程、彙編器、和一些API函數,才能明白這篇文章裏的內容。
=====[2.1 什麼叫Hook API?]=================================
所謂Hook就是鉤子的意思,而API是指Windows開放給程序員的編程接口,使得在用戶級別下可
以對操作系統進行控制,也就是一般的應用程序都需要調用API來完成某些功能,Hook API的意思
就是在這些應用程序調用真正的系統API前可以先被截獲,從而進行一些處理再調用真正的API來完
成功能。
====[2.2 API Hook的應用介紹]=================================
API Hook技術應用廣泛,常用於屏幕取詞,網絡防火牆,病毒木馬,加殼軟件,串口紅外通訊,遊戲外
掛,internet通信等領域API HOOK的中文意思就是鉤住API,對API進行預處理,先執行我們的函數,例
如我們用API Hook技術掛接ExitWindowsEx API函數,使關機失效,掛接ZwOpenProcess函數(如:老王的
EncryptPE),隱藏進程等等......
====[2.3 API Hook的原則]=====================================
HOOK API有一個原則,這個原則就是:被HOOK的API的原有功能不能受到任何影響。就象醫生救人,
如果把病人身體裏的病毒殺死了,病人也死了,那麼這個“救人”就沒有任何意義了。如果你HOOK API
之後,你的目的達到了,但API的原有功能失效了,這樣不是HOOK,而是REPLACE,操作系統的正常功能
就會受到影響,甚至會崩潰。
====[ 3. 掛鉤方法 ]==============================================
總的來說,常用的掛鉤API方法有以下兩種:
3.1 改寫IAT導入表法
修改可執行文件的IAT表(即輸入表)因爲在該表中記錄了所有調用API的函數地址,則只需將這些
地址改爲自己函數的地址即可,但是這樣有一個侷限,因爲有的程序會加殼,這樣會隱藏真實的IAT表
,從而使該方法失效。
3.2 改寫內存地址JMP法
直接跳轉,改變API函數的入口或出口的幾個字節,使程序跳轉到自己的函數,該方法不受程序加殼
的限制。這種技術,說起來也不復雜,就是改變程序流程的技術。在CPU的指令裏,有幾條指令可以改變
程序的流程:JMP,CALL,INT,RET,RETF,IRET等指令。理論上只要改變API入口和出口的任何機器碼
,都可以HOOK,下面我就說說常用的改寫API入口點的方法:
因爲工作在Ring3模式下,我們不能直接修改物理內存,只能一個一個打開修改,但具體的方法又分成
好幾種,我給大家介紹幾種操作思路:
<1>首先改寫API首字節,要實現原API的功能需要調用API時先還原被修改的字節,然後再調用原API,調
用完後再改回來,這樣實現有點麻煩,但最簡單,從理論上說有漏HOOK的可能,因爲我們先還原了API,如果
在這之前程序調用了API,就有可能逃過HOOK的可能!
(2)把被覆蓋的彙編代碼保存起來,在替代函數裏模擬被被覆蓋的功能,然後調用原函數(原地址+被覆
蓋長度).但這樣會產生一個問題,不同的彙編指令長度是不一樣的(比如說我們寫入的JMP指令佔用5個字
節,而我們寫入的這5個字節佔用的位置不一定正好是一個或多個完整的指令,有可能需要保存7個字節,
纔不能打亂程序原有的功能,需要編寫一個龐大的判斷體系來判斷指令長度,網上已經有這樣的彙編程序
(Z0MBiE寫的LDE32),非常的複雜!
(3)把被HOOK的函數備份一下,調用時在替代函數裏調用備份函數.爲了避免麻煩,可以直接備份整個
DLL缺點就是太犧牲內存,一般不推薦使用這種方法!
=====[ 4. 彙編實現 ]==============================================
本文就是建立在第2種方法之上的!本着先易後難的原則,今天我們先來說說它的第1種操作思路.
我們拿API函數ExitWindowsEx來說明,下面是我在OD裏攔下的ExitWindowsEx原入口部分
77D59E2D $ 8BFF mov edi,edi
77D59E2F . 55 push ebp
77D59E30 . 8BEC mov ebp,esp
77D59E32 . 83EC 18 sub esp,18
......
如果我們把ExitWindowsEx的入口點改爲下面的,會出現什麼情況?
77D59E2D B8 00400000 mov eax,4000
77D59E32 FFE0 jmp eax
......
我們可想而知,程序執行到77D59E32處就會改變流程跳到00400000的地方
如果我們的00400000處是這樣的子程:
=======================
MyAPI proc bs:DWORD ,dwReserved:DWORD ;和ExitWindowsEx一樣帶2個參數
做你想做的事
......
這裏放API入口點改回原機器碼的代碼
如果你是備份的整個DLL,就直接調用備份API,不用改來改去了,不會有漏勾API的可能!
invoke ExitWindowsEx,bs,dwReserved
這裏放HOOK API的代碼
.endif
mov eax,TRUE
ret
=======================
這裏的MyAPI是和ExitWindowsEx參數一樣的的子程,因爲程序是在API的入口部分跳轉的,根據
stdcall約定(參數數據從右向左依次壓棧,恢復堆棧的工作交由被調用者),此時堆棧還沒有恢復,我們
在子程裏取出的參數數據依然有效,我們可以在這裏執行自己的代碼,你可以決定是否繼續按原參數或改
變參數後再調用原API,也可以什麼都不做,當然在調用之前,我們要先還原我們修改過的API(可以事先用
API函數ReadProcessMemory讀出原API的前幾個字節備份之),調用完後再改回來繼續HOOK API,不過這種
方法有漏API的可能(原因前面已經說了),你如果覺得這個方法不妥,因爲一般系統DLL都不大,你可以備
份整個DLL.
下面我就列出ring3下HOOK API的幾個步驟:
1.得到要掛勾API的入口點
2.修改API的入口點所在頁的頁面保護爲可讀寫模式
3.用ReadProcessMemory讀出API的入口點開始的幾字節備份
4.用WriteProcessMemory修改API的入口點象這樣的形式:
mov eax,4000
jmp eax
其中的4000要用和原API參數一樣的子程序地址代替
在這個子程序裏我們決定用什麼參數再調用原API,不過調用之前要用備份的前8字節改回來
調用之後在掛勾,如此反覆.
=====[ 4.1. 代碼 ]==============================================
前面所講的是本進程掛勾,我們要掛勾所有進程,可以用全局勾子,需要單獨的一個DLL,我們可
以在DLL的DLL_PROCESS_ATTACH事件裏來HOOK API
=================================hookdll.dll==========================
.486
.model flat,stdcall ;參數的傳遞約定是stdcall(從右到左,恢復堆棧的工作交由被調用者)
option casemap:none
include /masm32/include/windows.inc
include /masm32/include/kernel32.inc
includelib /masm32/lib/kernel32.lib
include /masm32/include/user32.inc
includelib /masm32/lib/user32.lib
HOOKAPI struct
a byte ?
PMyapi DWORD ?
d BYTE ?
e BYTE ?
HOOKAPI ends
子程序聲明
WriteApi proto :DWORD ,:DWORD,:DWORD,:DWORD
MyAPI proto :DWORD ,:DWORD
GetApi proto :DWORD,:DWORD
已初始化數據
.data
hInstance dd 0
WProcess dd 0
hacker HOOKAPI <>
CommandLine LPSTR ?
Papi1 DWORD ?
Myapi1 DWORD ?
ApiBak1 db 10 dup(?)
DllName1 db "user32.dll",0
ApiName1 db "ExitWindowsEx",0
mdb db "下面的程序想關閉計算機,要保持阻止嗎?",0
未初始化數據
.data?
hHook dd ?
hWnd dd ?
程序代碼段
.code
DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD
.if reason==DLL_PROCESS_ATTACH ;當DLL加載時產生此事件
push hInst
pop hInstance
invoke GetCommandLine
mov CommandLine,eax ;取程序命令行
初始化
mov hacker.a,0B8h ;mov eax,
mov hacker.d PMyapi ;0x000000
mov hacker.d,0FFh ;jmp
mov hacker.e, 0E0h ;eax
invoke GetCurrentProcess ;取進程僞句柄
mov WProcess ,eax
invoke GetApi,addr DllName1,addr ApiName1 ;取API地址
mov Papi1,eax ;保存API地址
invoke ReadProcessMemory,WProcess,Papi1,addr ApiBak1,8,NULL ;備份原API的前8字節
mov hacker.PMyapi,offset MyAPI ;0x0000,這裏設置替代API的函數地址
invoke WriteApi,WProcess,Papi1, addr hacker ,size HOOKAPI ;HOOK API
.endif
.if reason==DLL_PROCESS_DETACH
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8 ;還原API
.endif
mov eax,TRUE
ret
DllEntry Endp
GetMsgProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
invoke CallNextHookEx,hHook,nCode,wParam,lParam
mov eax,TRUE
ret
GetMsgProc endp
InstallHook proc
invoke SetWindowsHookEx,WH_GETMESSAGE,addr GetMsgProc,hInstance,NULL
mov hHook,eax
ret
InstallHook endp
UninstallHook proc
invoke UnhookWindowsHookEx,hHook
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8
ret
UninstallHook endp
GetApi proc DllNameAddress:DWORD,ApiNameAddress:DWORD
invoke GetModuleHandle,DllNameAddress ;取DLL模塊句柄
.if eax==NULL
invoke LoadLibrary ,DllNameAddress ;加載DLL
.endif
invoke GetProcAddress,eax,ApiNameAddress ;取API地址
mov eax,eax
ret
GetApi endp
============================下面是核心部分=========================
WriteApi proc Process:DWORD ,Papi:DWORD,Ptype:DWORD,Psize:DWORD
LOCAL mbi:MEMORY_BASIC_INFORMATION
LOCAL msize:DWORD
返回頁面虛擬信息
invoke VirtualQueryEx,Process, Papi,addr mbi,SIZEOF MEMORY_BASIC_INFORMATION
修改爲可讀寫模式
invoke VirtualProtectEx,Process, mbi.BaseAddress,8h,PAGE_EXECUTE_READWRITE,addr
mbi.Protect
開始寫內存
invoke WriteProcessMemory,Process, Papi, Ptype,Psize ,NULL
PUSH eax
改回只讀模式
invoke VirtualProtectEx,Process,mbi.BaseAddress,8h,PAGE_EXECUTE_READ,addr mbi.Protect
pop eax
ret
WriteApi endp
替代的API,參數要和原來一樣
MyAPI proc bs:DWORD ,dwReserved:DWORD
invoke MessageBox, NULL, CommandLine, addr mdb, MB_YESNO ;彈出信息框選擇是否阻止
.if eax==7 ;如果選擇否
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8 ;先還原API
invoke ExitWindowsEx,bs,dwReserved ;再調用API
invoke WriteApi,WProcess,Papi1, addr hacker ,sizeof HOOKAPI ;調用完後再改回來
.endif
mov eax,TRUE
ret
MyAPI endp
End DllEntry
===============================hookdll.def=============================
LIBRARY hookdll
EXPORTS InstallHook
EXPORTS UninstallHook
=====[ 4.2. 分析 ]==============================================
HOOKAPI struct
a byte ?
PMyapi DWORD ?
d BYTE ?
e BYTE ?
HOOKAPI ends
爲了便於理解和使用,我定義了一個結構:這個結構有4個成員,第一個成員a,是個字節型,我用來放
0B8h(mov eax),PMyapi一個整數型,用來放我們的替代API函數的地址(0X000),第3個和第4個成員我分別
用來放JMP和EAX(jmp eax)那麼連起來就是 mov,0X0000 ; jmp eax
.if reason==DLL_PROCESS_ATTACH
push hInst
pop hInstance
invoke GetCommandLine
mov CommandLine,eax
初始化
mov hacker.a,0B8h ;mov eax,
mov hacker.d PMyapi ;0x0000
mov hacker.d,0FFh ;jmp
mov hacker.e, 0E0h ;eax
invoke GetCurrentProcess
mov WProcess ,eax
當DLL加載時,我們先保存模塊句柄,讀取程序命令行,然後初始化HOOKAPI結構,寫入我們要寫到內存的
指令(PMyapi以後寫入)並調用GetCurrentProcess取出進程僞句柄方便以後寫內存.
invoke GetApi,addr DllName1,addr ApiName1
mov Papi1,eax
invoke ReadProcessMemory,WProcess,Papi1,addr ApiBak1,8,NULL
mov hacker.PMyapi,offset MyAPI ;0x0000
invoke WriteApi,WProcess,Papi1, addr hacker ,size HOOKAPI ;HOOK API
接下來用子程GetApi取出要掛勾API的入口點,並用ReadProcessMemory讀出入口點8字節備份之,寫入
PMyapi調用子程WriteApi改寫API的入口點,這個子程我不準備詳細說了,它非常的簡單,無非就是幾個
API的調用.它的核心就是通過WriteProcessMemory改寫內存.
.if reason==DLL_PROCESS_DETACH
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8
.endif
mov eax,TRUE
ret
如果這個DLL被卸載了,那麼那個在DLL裏的替代函數(MyAPI)將是無效的,如果這個時候程序再調用這
個API,將出現非法操作,因此在DLL卸載前,我們必須還原API.
總結一下,現在只要程序加載這個DLL,這個程序的ExitWindowsEx就會被我們勾住,接下來要怎樣才能
讓所有的程序都加載這個DLL呢?這就需要安裝全局勾子:
InstallHook proc
invoke SetWindowsHookEx,WH_GETMESSAGE,addr GetMsgProc,hInstance,NULL
invoke WriteApi,WProcess,Papi1, addr hacker ,sizeof HOOKAPI
mov hHook,eax
ret
InstallHook endp
通過SetWindowsHookEx安裝勾子,最後一個參數可以決定該鉤子是局部的還是系統範圍的。如果該值
爲NULL,那麼該鉤子將被解釋成系統範圍內的,那它就可以監控所有的進程及它們的線程。
如果該函數調用成功的話,將在eax中返回鉤子的句柄,否則返回NULL。我們必須保存該句柄,因爲後
面我們還要它來卸載鉤子,可以看出,我們創建的Hook類型是WH_CALLWNDPROC類型,該類型的Hook在進程
與系統一通信時就會被加載到進程空間,從而調用dll的初始化函數完成真正的Hook,值得一提的是:因
爲要調用SetWindowsHookEx來安裝鉤子,我們GUI程序的這個DLL不會被
UnhookWidowHookEx卸載,也就只有一次DLL_PROCESS_ATTACH事件,因此這裏再要
HOOK API一次!
我們回頭來看看鉤子回調函數:
GetMsgProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
invoke CallNextHookEx,hHook,nCode,wParam,lParam
mov eax,TRUE
ret
GetMsgProc endp
可以看到這裏只是調用CallNextHookEx將消息交給Hook鏈中下一個環節處理,因爲這裏API函數
SetWindowsHookEx的唯一作用就是讓進程加載我們的dll。
UninstallHook proc
invoke UnhookWindowsHookEx,hHook
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8
ret
UninstallHook endp
要卸載一個鉤子時調用UnhookWidowHookEx函數,該函數僅有一個參數,就是欲卸載的鉤子的句柄。鉤
子卸載後我們也要還原我們GUI程序的API.
LIBRARY hookdll
EXPORTS InstallHook
EXPORTS UninstallHook
我們公開DLL裏的InstallHook和UninstallHook函數,方便程序調用,這樣我們只要在另外的程序中調
用InstallHook便可安裝全局勾子,勾住所有程序中的API:ExitWindowsEx,執行我們自定的子程!
如果不需要了,可以調用UninstallHook卸載全局勾子.
請注意:對於遠程鉤子,鉤子函數必須放到DLL中,它們將從DLL中映射到其它的進程空間中去。當
WINDOWS映射DLL到其它的進程空間中去時,不會把數據段也進行映射。簡言之,所有的進程僅共享DLL
的代碼,至於數據段,每一個進程都將有其單獨的拷貝。這是一個很容易被忽視的問題。您可能想當然
的以爲,在DLL中保存的值可以在所有映射該DLL的進程之間共享。在通常情況下,由於每一個映射該
DLL的進程都有自己的數據段,所以在大多數的情況下您的程序運行得都不錯。但是鉤子函數卻不是如
此。對於鉤子函數來說,要求DLL的數據段對所有的進程也必須相同。這樣您就必須把數據段設成共享
的:
一般來說, 目標文件有三個段, 分別是 text/data/bss 段.
.text 段放置代碼, 是隻讀且可運行段
.data 段放置靜態數據, 這些數據會被放置入 exe 文件. 這個段是可讀寫, 但是不能運行的.
.bss 段放置動態數據, 這些數據不被放入 exe 文件, 在exe文件被加載入內存後才分配的空間.
你可以通過在鏈接開關中指定段的屬性來實現:
/SECTION:name,[E][R][W][S][D][K][L][P][X]
其中S表示共享,已初期化的段名是.data,未初始化的段名是.bss。假如您想要寫一個包含鉤子函數的
DLL,而且想使它的未初始化的數據段在所有進程間共享,您必須這麼做:
link /section:.bss[S] /DLL /SUBSYSTEM:WINDOWS ..........
否則,您的全局勾子將不能正常工作!
=====[ 5. 結束語 ]================================================
我歡迎任何人提出更多的這裏沒有提到的掛鉤方法,我肯定那會有很多。同樣歡迎補充我介紹得不
是很詳細的方法。也可以把我懶得寫的其它方法完成,把源代碼發給我。這篇文檔的目的是演示掛鉤技
術的細節,我希望我做到了。
============================[ End ]========================
http://phlashback.blog.hexun.com/12573252_d.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.