天天看點

消息鈎子的反攔截

大家好,又見面了,我是你們的朋友全棧君。

首先聲明一下,标題所指的鈎子是消息鈎子,而不是API鈎子(一種對API位址的替換技術)。若标題使您誤解,請不要繼續閱讀。

消息鈎子在Windows程式設計中有着非常廣泛的應用,它可以任意攔截Windows系統,這個以消息為驅動的系統中的絕大多數消息類型。一方面這給程式設計者帶來了巨大的靈活性,另一方面也埋下了巨大隐患,大多數竊密軟體都使用這種方法。此篇文章給您提供一種鈎子的反攔截方法,希望對您有所幫助。文章中使用了API鈎子,您之前必須對此技術有一定了解。

為求完整,文章分為兩部分,第一部分為消息鈎子的使用,熟悉此技術的讀者可以直接跳過此節。第二部分為消息鈎子的反攔截。

一、消息鈎子的使用。

消息鈎子分為本地(local)和遠端(remote)兩種(兩個local system-wide hook例外,無關主題,不多說了)。local類型的鈎子函數隻能攔截本程序的消息。能夠攔截本程序以外的消息的鈎子,都是remote類型。remote類型的鈎子必須放在DLL裡面。下面以remote類型為例,通過安裝鍵盤鈎子介紹其使用。

1、首先建立DLL,在頭檔案中添加如下代碼。

#ifdef KM_EXPORTS

#define KM_API __declspec(dllexport)

#else

#define KM_API __declspec(dllimport)

#endif

KM_API BOOL HookStart();//安裝鈎子

KM_API BOOL HookStop();//解除安裝鈎子

2、在.cpp檔案中添加代碼

#pragma data_seg(“Shared”)

HHOOK g_hhookKey=NULL;

#pragma data_seg()

#pragma comment(linker,”/SECTION:Shared,RWS”)

g_hhookKey為鍵盤鈎子的句柄,為確定此數值在所有執行個體中均保持不變,将其存放于此子產品所有執行個體的共享資料區,若在exe程式中按此格式添加一int 變量 appNum,在程式啟動時appNum++,則可以通過通路此變量的數值,确定有多少個exe的執行個體,當然這種方法也可以替代同步對象用于隻啟動一個執行個體。

HINSTANCE g_hinstDll=NULL; //添加全局變量用于記錄此DLL子產品的句柄

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;//在DLL加載時對全局變量指派

………………

}

}

LRESULT KeyHookProc(int nCode,WPARAM wParam,LPARAM lParam)//鍵盤鈎子的過濾函數

{

…………………

return::CallNextHookEx(g_hhookKey,nCode,wParam,lParam);//*****請留意此行代碼*****

}

BOOL HookStart()//安裝鈎子

{

g_hhookKey=::SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyHookProc,g_hinstDll,

::GetWindowThreadProcessId(::FindWindow(NULL,”被監視的視窗的标題“),NULL) );

return (g_hhookKey!=NULL);

}

BOOL HookStop()//解除安裝鈎子

{

BOOL ret;

if(g_hhookKey!=NULL)

ret=::UnhookWindowsHookEx(g_hhookKey);

g_hhookKey=NULL;

return ret;

}

隻要在exe程式中調用HookStart函數,就可以監視某一視窗的鍵盤消息,若此視窗為QQ的密碼框,你的密碼就洩漏了。

二、消息鈎子的反攔截。

請留意前面帶*号注釋的代碼,其中傳入了鈎子的句柄g_hhookKey,隻要使用API鈎子将CallNextHookEx函數替換,并在替換函數中将其解除安裝,消息鈎子就完蛋了。同時,還要保證本程序安裝的鈎子不被解除安裝,其中既可能有local類型的還可能有remote類型的。不要以為自己沒有在程式中安裝鈎子,程式中就一定沒有安裝鈎子,在MFC4版本中,MFC會自己裝一個local類型的鈎子,MFC7版本中好像沒了。好了,下面介紹其實作。

1、建立DLL,在頭檔案中添加如下代碼。

#ifdef HOOKFORBID_EXPORTS

#define HOOKFORBID_API __declspec(dllexport)

#else

#define HOOKFORBID_API __declspec(dllimport)

#endif

HOOKFORBID_API int fnHookForbid(void);//在exe程式中調用此函數,使DLL加載

HOOKFORBID_API bool AddHhook(HHOOK Hhook);//若exe中安裝remote類型消息鈎子,将其句柄添加

HOOKFORBID_API bool DelHhook(HHOOK Hhook);//在exe中解除安裝remote類型消息鈎子時,删除其句柄

2、在.cpp檔案中添加代碼。

CArray<HHOOK,HHOOK> array;//用于記錄本程序安裝的鈎子的句柄

//

int fnHookForbid(void)

{

return 1;

}

bool AddHhook(HHOOK Hhook)

{

array.Add(Hhook);

return true;

}

bool DelHhook(HHOOK Hhook)

{

bool ret=false;

for(int i=0;i<array.GetSize();i++)

{

if(array.GetAt(i)==Hhook)

{

array.RemoveAt(i);

ret=true;

break;

}

}

return ret;

}

//

下面的代碼用于API替換,其中用到了CAPIHook 類,《Windows 核心程式設計》(Jeffrey Richter著)一書中有源代碼。使用其它開發包也可以實作此功能。

//

typedef HHOOK (WINAPI *PFNSETWINDOWSHOOKEX)(

int idHook,

HOOKPROC lpfn,

HINSTANCE hMod,

DWORD dwThreadId

);

typedef LRESULT (WINAPI *PFNCALLNEXTHOOKEX)(

HHOOK hhk,

int nCode,

WPARAM wParam,

LPARAM lParam

);

//

extern CAPIHook g_SetWindowsHookExA;

extern CAPIHook g_SetWindowsHookExW;

extern CAPIHook g_CallNextHookEx;

//

//此函數用于替換SetWindowsHookEx函數的ASCII版本SetWindowsHookExA

HHOOK WINAPI Hook_SetWindowsHookExA( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId )

{

HHOOK nResult =0;

nResult = ((PFNSETWINDOWSHOOKEX)(PROC) g_SetWindowsHookExA)( idHook, lpfn, hMod, dwThreadId );

//若在本程序中安裝了local類型鈎子,記錄其句柄

if(hMod==NULL)

array.Add(nResult);

return(nResult);

}

//此函數用于替換SetWindowsHookEx函數的UNICODE版本SetWindowsHookExW

HHOOK WINAPI Hook_SetWindowsHookExW( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId )

{

HHOOK nResult =0;

nResult = ((PFNSETWINDOWSHOOKEX)(PROC) g_SetWindowsHookExW)( idHook, lpfn, hMod, dwThreadId );

//若在本程序中安裝了local類型鈎子,記錄其句柄

if(hMod==NULL)

array.Add(nResult);

return(nResult);

}

//此函數用于替換CallNextHookEx函數,此函數隻有一個版本

LRESULT WINAPI Hook_CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam)

{

LRESULT nResult =0;

nResult = ((PFNCALLNEXTHOOKEX)(PROC) g_CallNextHookEx)( hhk, nCode, wParam, lParam );

//在數組中查找句柄,若找不到,将其解除安裝

bool bfind=false;

for(int i=0;i<array.GetSize();i++)

{

if(array.GetAt(i)==hhk)

{

bfind=true;

break;

}

}

if(!bfind)

{

UnhookWindowsHookEx( hhk );

}

return (nResult);

}

//

//使用CAPIHook 類對函數進行替換

CAPIHook g_SetWindowsHookExA(“User32.dll”, “SetWindowsHookExA”,

(PROC) Hook_SetWindowsHookExA, true);

CAPIHook g_SetWindowsHookExW(“User32.dll”, “SetWindowsHookExW”,

(PROC) Hook_SetWindowsHookExW, true);

CAPIHook g_CallNextHookEx(“User32.dll”, “CallNextHookEx”,

(PROC) Hook_CallNextHookEx, true);

到了這裡,所有工作都完成了,隻要在exe程式中調用fnHookForbid函數,并在安裝remote類型鈎子時調用AddHhook函數記錄其句柄,解除安裝時調用DelHhook函數删除句柄就萬事ok了。

一點不足:這種方法可以有效屏蔽消息鈎子對資訊安全的威脅。可以使Spy++失效。然而,由于是在CallNextHookEx函數中解除安裝鈎子,是以,鈎子函數總是會被調用一次。還有一件非常費解的事,金山詞霸總能夠正常取詞,不知道詞霸是怎麼做到的。

本人并非專業程式員, 若此方法存在任何錯誤或隐患,敬請批評指出,請不要在文章上損我。

呵呵!假如我的鈎子是這麼用的:

FUN_SETWINDOWSHOOKA *pFn = (FUN_SETWINDOWSHOOKA *)

::GetProcAddress(::GetModuleHandle(“kernel32.dll”), “SetWindowsHookA”);

pFn(…);

你的方法還是屏蔽不了哦!不信試驗一下!記得給分哦!(該法為:“反反API鈎子大法”)

當然!同理也可以繞過API鈎子!有同樣興趣的人記得發消息給我哦!

首先聲明一下:我攔截的是消息鈎子,如果安裝鈎子時考慮到了反解除安裝則不在讨論之内。

其次:上述方法不可靠,對CAPIHook類進行更改,可以實時對位址進行替換,就象消息鈎子被調用次序的不确定性一樣,到時候沒法确定那個替換函數被調用了。

防止全局鈎子的侵入

Author: pjf([email protected])

Windows消息鈎子一般都很熟悉了。它的用處很多,耳熟能詳的就有——利用鍵盤鈎子擷取目标程序的鍵盤輸入,進而獲得各類密碼以達到不可告人的目的。朋友想讓他的軟體不被别人的全局鈎子監視,有沒有辦法實作呢?答案是肯定的,不過缺陷也是有的。

首先簡單看看全局鈎子如何注入别的程序。

消息鈎子是由Win32子系統提供,其核心部分通過NtUserSetWindowsHookEx為使用者提供了設定消息鈎子的系統服務,使用者通過它注冊全局鈎子。當系統擷取某些事件,比如使用者按鍵,鍵盤driver将掃描碼等傳入win32k的KeyEvent處理函數,處理函數判斷有無相應hook,有則callhook。此時,系統取得Hook對象資訊,若目标程序沒有裝載對應的Dll,則裝載之(利用KeUserModeCallback“調用”使用者例程,它與Apc調用不同,它是仿制中斷傳回環境,其調用是“立即”性質的)。

進入使用者态的KiUserCallbackDispatcher後,KiUserCallbackDispatcher根據傳遞的資料擷取所需調用的函數、參數等,随後調用。針對上面的例子,為裝載hook dll,得到調用的是LoadLibraryExW,随後進入LdrLoadDll,裝載完畢後傳回,後面的步驟就不叙述了。

從上面的讨論我們可以得出一個最簡單的防侵入方案:在加載hook dll之前hook相應api使得加載失敗,不過有一個缺陷:系統并不會因為一次的失敗而放棄,每次有消息産生欲call hook時系統都會試圖在你的程序加載dll,這對于性能有些微影響,不過應該感覺不到。剩下一個問題就是不是所有的LoadLibraryExW都應攔截,這個容易解決,比如判斷傳回位址。下面給出一個例子片斷,可以添加一些判斷使得某些允許加載的hook dll被加載。

這裡hook api使用了微軟的detours庫,可自行修改。

以下内容為程式代碼:

typedef HMODULE (__stdcall *LOADLIB)(

LPCWSTR lpwLibFileName,

HANDLE hFile,

DWORD dwFlags);

extern “C” {

DETOUR_TRAMPOLINE(HMODULE __stdcall Real_LoadLibraryExW(

LPCWSTR lpwLibFileName,

HANDLE hFile,

DWORD dwFlags),

LoadLibraryExW);

}

ULONG user32 = 0;

HMODULE __stdcall Mine_LoadLibraryExW(

LPCWSTR lpwLibFileName,

HANDLE hFile,

DWORD dwFlags)

{

ULONG addr;

_asm mov eax, [ebp+4]

_asm mov addr, eax

if ((user32 & 0xFFFF0000) == (addr & 0xFFFF0000))

{

return 0;

}

HMODULE res = (LOADLIB(Real_LoadLibraryExW)) (

lpwLibFileName,

hFile,

dwFlags);

return res;

}

BOOL ProcessAttach()

{

DetourFunctionWithTrampoline((PBYTE)Real_LoadLibraryExW,

(PBYTE)Mine_LoadLibraryExW);

return TRUE;

}

BOOL ProcessDetach()

{

DetourRemove((PBYTE)Real_LoadLibraryExW,

(PBYTE)Mine_LoadLibraryExW);

return TRUE;

}

CAnti_HookApp::CAnti_HookApp() //在使用使用者界面服務前調用ProcessAttach

{

user32 = (ULONG)GetModuleHandle(“User32.dll”);

ProcessAttach();

}

WINDOWS核心程式設計裡的用IAT的APIHOOK是不全面的。

::GetProcAddress(::GetModuleHandle(“kernel32.dll”), “SetWindowsHookA”);

就可以繞過。

APIHOOK最好的方法還是直接修改API入口點的代碼。

同時HOOK GetProcAddress 不就行了,但是要是對方使用搜尋PE函數導出表的話就沒用

防止IAT型的鈎子我要是對PE檔案的IAT加密,調用時解密調用,就可以了吧(極其複雜)

防止jmp型的鈎子我沒想到好辦法

防止調試我可以判斷api入口處是否有int3中斷代碼就可以了吧(簡單)

消息鈎子的反攔截其實核心是利用API攔截,來取消鈎子攔截.

如果API攔截被激活成功教程也就是說消息鈎子反攔截沒有成功.

釋出者:全棧程式員棧長,轉載請注明出處:https://javaforall.cn/159493.html原文連結:https://javaforall.cn