我們知道,系統函數都是以DLL封裝起來的,應用程序應用到系統函數時,應首先把該DLL加載到當前的進程空間中,調用的系統函數的入口地址,可以通過GetProcAddress函數進行獲取。當系統函數進行調用的時候,首先把所必要的信息保存下來(包括參數和返回地址,等一些別的信息),然后就跳轉到函數的入口地址,繼續執行。其實函數地址,就是系統函數“可執行代碼”的開始地址。那么怎么才能讓函數首先執行我們的函數呢?呵呵,應該明白了吧,把開始的那段可執行代碼替換為我們自己定制的一小段可執行代碼,這樣系統函數調用時,不就按我們的意圖乖乖行事了嗎?其實,就這么簡單。Very very簡單。 :P.實際的說,就可以修改系統函數入口的地方,讓他調轉到我們的函數的入口點就行了。采用匯編代碼就能簡單的實現Jmp XXXX, 其中XXXX就是要跳轉的相對地址。
我們的做法是:把系統函數的入口地方的內容替換為一條Jmp指令,目的就是跳到我們的函數進行執行。而Jmp后面要求的是相對偏移,也就是我們的函數入口地址到系統函數入口地址之間的差異,再減去我們這條指令的大小。用公式表達如下:
int nDelta = UserFunAddr – SysFunAddr - (我們定制的這條指令的大小);
Jmp nDleta;
為了保持原程序的健壯性,我們的函數里做完必要的處理后,要回調原來的系統函數,然后返回。所以調用原來系統函數之前必須先把原來修改的系統函數入口地方給恢復,否則,系統函數地方被我們改成了Jmp XXXX就會又跳到我們的函數里,死循環了。
那么說一下程序執行的過程。
我們的dll“注射”入被hook的進程 -> 保存系統函數入口處的代碼 -> 替換掉進程中的系統函數入口指向我們的函數 -> 當系統函數被調用,立即跳轉到我們的函數 -> 我們函數進行處理 -> 恢復系統函數入口的代碼 -> 調用原來的系統函數 -> 再修改系統函數入口指向我們的函數(為了下次hook)-> 返回。
於是,一次完整的Hook就完成了。
好,這個問題明白以后,講一下下個問題,就是如何進行dll“注射”?即將我們的dll注射到要Hook的進程中去呢?
很簡單哦,這里我們采用調用Windows提供給我們的一些現成的Hook來進行注射。舉個例子,鼠標鈎子,鍵盤鈎子,大家都知道吧?我們可以給系統裝一個鼠標鈎子,然后所有響應到鼠標事件的進程,就會“自動”(其實是系統處理了)載入我們的dll然后設置相應的鈎子函數。其實我們的目的只是需要讓被注射進程載入我們的dll就可以了,我們可以再dll實例化的時候進行函數注射的,我們的這個鼠標鈎子什么都不干的。
4簡單的例子OneAddOne
講了上面的原理,現在我們應該實戰一下了。先不要考慮windows系統那些繁雜的函數,我們自己編寫一個API函數來進行Hook與被Hook的練習吧,哈哈。
////////////////////
第一步,首先編寫一個add.dll,很簡單,這個dll只輸出一個API函數,就是add啦。
新建一個win32 dll工程,
add.cpp的內容:
#i nclude "stdafx.h"
int WINAPI add(int a,int b){ file://千萬別忘記聲明WINAPI 否則調用的時候回產生聲明錯誤哦!
return a+b;
}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
然后別忘了在add.def里面輸出函數:
LIBRARY Add
DESCRIPTION "ADD LA"
EXPORTS
add @1;
編譯,ok,我們獲得了add.dll
////////////////////
第二步,編寫1+1主程序
新建一個基於對話框的工程One,
在 OnOK()里面調用add函數:
ConeDlg.h里面加入一些變量的聲明:
….
Public:
HINSTANCE hAddDll;
typedef int (WINAPI*AddProc)(int a,int b);
AddProc add;
…
ConeDlg.cpp里進行調用:
void COneDlg::OnOK()
{
// TODO: Add extra validation here
if (hAddDll==NULL)
hAddDll=::LoadLibrary("add.dll");
add=(AddProc)::GetProcAddress(hAddDll,"add");
int a=1;
int b=2;
int c=add(a,b);
CString temp;
temp.Format("%d+%d=%d",a,b,c);
AfxMessageBox(temp);
}
OK,編譯運行,正確的話就會顯示1+2=3咯
////////////////////
第3步,要動手Hook咯,爽阿
新建一個MFC的dll工程,Hook
在Hook.dll工程里:
添加一個鼠標Hook MouseProc,鼠標hook什么也不做
LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
LRESULT RetVal= CallNextHookEx(hhk,nCode,wParam,lParam);
return RetVal;
}
添加鼠標鈎子的安裝和卸載函數:
BOOL InstallHook()
{
hhk=::SetWindowsHookEx(WH_MOUSE,MouseProc,hinst,0);
….
return true;
}
void UninstallHook()
{
::UnhookWindowsHookEx(hhk);
}
再實例化中獲得一些參數
BOOL CHookApp::InitInstance()
{
獲得dll 實例,進程句柄
hinst=::AfxGetInstanceHandle();
DWORD dwPid=::GetCurrentProcessId();
hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
調用注射函數
Inject();
return CWinApp::InitInstance();
}
好,最重要的注射函數:
void Inject()
{
if (m_bInjected==false)
{ 保證只調用1次
m_bInjected=true;
獲取add.dll中的add()函數
HMODULE hmod=::LoadLibrary("add.dll");
add=(AddProc)::GetProcAddress(hmod,"add");
pfadd=(FARPROC)add;
if (pfadd==NULL)
{
AfxMessageBox("cannot locate add()");
}
// 將add()中的入口代碼保存入OldCode[]
_asm
{
lea edi,OldCode
mov esi,pfadd
cld
movsd
movsb
}
NewCode[0]=0xe9;//實際上0xe9就相當於jmp指令
//獲取Myadd()的相對地址
_asm
{
lea eax,Myadd
mov ebx,pfadd
sub eax,ebx
sub eax,5
mov dword ptr [NewCode+1],eax
}
//填充完畢,現在NewCode[]里的指令相當於Jmp Myadd
HookOn(); //可以開啟鈎子了
}
}
開啟鈎子的函數
void HookOn()
{
ASSERT(hProcess!=NULL);
DWORD dwTemp=0;
DWORD dwOldProtect;
//將內存保護模式改為可寫,老模式保存入dwOldProtect
VirtualProtectEx(hProcess,pfadd,5,PAGE_READWRITE,&dwOldProtect);
//將所屬進程中add()的前5個字節改為Jmp Myadd
WriteProcessMemory(hProcess,pfadd,NewCode,5,0);
//將內存保護模式改回為dwOldProtect
VirtualProtectEx(hProcess,pfadd,5,dwOldProtect,&dwTemp);
bHook=true;
}
關閉鈎子的函數
void HookOff()//將所屬進程中add()的入口代碼恢復
{
ASSERT(hProcess!=NULL);
DWORD dwTemp=0;
DWORD dwOldProtect;
VirtualProtectEx(hProcess,pfadd,5,PAGE_READWRITE,&dwOldProtect);
WriteProcessMemory(hProcess,pfadd,OldCode,5,0);
VirtualProtectEx(hProcess,pfadd,5,dwOldProtect,&dwTemp);
bHook=false;
}
然后,寫我們自己的Myadd()函數
int WINAPI Myadd(int a,int b)
{
//截獲了對add()的調用,我們給a,b都加1
a=a+1;
b=b+1;
HookOff();//關掉Myadd()鈎子防止死循環
int ret;
ret=add(a,b);
HookOn();//開啟Myadd()鈎子
return ret;
}
然后別忘記在hook.def里面輸出
InstallHook
MouseProc
Myadd
UninstallHook
四個函數。
好到這里基本上大功告成咯
////////////////////
第4步,我們就可以修改前面的One的測試程序了
增加一個安裝鈎子的函數/按鈕
void COneDlg::doHook()
{
hinst=LoadLibrary("hook.dll");
if(hinst==NULL)
{
AfxMessageBox("no hook.dll!");
return;
}
typedef BOOL (CALLBACK *inshook)();
inshook insthook;
insthook=::GetProcAddress(hinst,"InstallHook");
if(insthook==NULL)
{
AfxMessageBox("func not found!");
return;
}
DWORD pid=::GetCurrentProcessId();
BOOL ret=insthook();
}
別忘了退出時卸掉鈎子
void COneDlg::OnCancel()
{
// TODO: Add extra cleanup here
typedef BOOL (CALLBACK *UnhookProc)();
UnhookProc UninstallHook;
UninstallHook=::GetProcAddress(hinst,"UninstallHook");
if(UninstallHook==NULL) UninstallHook();
if (hinst!=NULL)
{
::FreeLibrary(hinst);
}
if (hAddDll!=NULL)
{
::FreeLibrary(hAddDll);
}
CDialog::OnCancel();
}
http://blog.csdn.net/jiangxinyu/article/details/5385821