大家好,又見面了,我是你們的朋友全棧君。
首先聲明一下,标題所指的鈎子是消息鈎子,而不是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