最近在研究Windows Ring3上的API Hook,對HOOK導(dǎo)入表這種方法進(jìn)行了研究。HOOK導(dǎo)入表所用的C++類大同小異,不同的就是如何實(shí)現(xiàn)HOOK一個延遲加載的模塊中的函數(shù),以及FreeLibaray某個函數(shù)之后再次LoadLibaray加載這個模塊所導(dǎo)致的模塊基地址不同的時候,這時該如何HOOK住這個模塊中的API。
顯然地,掛鉤LoadLibarayA/W、LoadLibarayExA/W、GetProcAddress這些函數(shù)還不夠,還需要掛鉤FreeLibrary函數(shù)。這里我參考了《Windows核心編程》(第五版)中的一個程序,封裝了一個C++類,能夠HOOK住FreeLibrary,同時也能解決延遲加載的問題。
以下是這個類的聲明:

CApiHook類聲明
#ifndef __API_HOOK_CLASS__
#define __API_HOOK_CLASS__
///////////////////////////////////////////////////////////////////////////////
class CApiHook
{
public:
CApiHook();
// 構(gòu)造函數(shù) —— 在所有模塊中HOOK函數(shù)pszFuncName,將其替換為pfnHook
CApiHook(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook);
// 析構(gòu)
~CApiHook();
// 返回原來的函數(shù)地址
operator PROC()
{
return m_pfnOrig; // 返回原函數(shù)地址
}
// 是否掛鉤本模塊中的函數(shù)
static BOOL ExcludeAPIHookMod;
public:
// GetProcAddressRaw調(diào)用實(shí)際的GetProcAddress函數(shù)
static FARPROC WINAPI GetProcAddressRaw(HMODULE hmod, PCSTR pszProcName);
BOOL WINAPI HookApi(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook); // HOOK某個函數(shù)
void WINAPI UnHook(); // 撤銷掛鉤
protected:
void WINAPI Init();
void WINAPI Uninit();
private:
static PVOID sm_pvMaxAppAddr;
static PROC sm_pFunFreeLibrary; // FreeLibrary的實(shí)際地址
static CApiHook * sm_pHead; // 對象鏈表頭節(jié)點(diǎn)
CApiHook * m_pNext; // 下一個對象節(jié)點(diǎn)
PCSTR m_pszCalleeModName; // 要HOOK函數(shù)所在的模塊名稱(ANSI)
PCSTR m_pszFuncName; // 要HOOK的函數(shù)的函數(shù)名(ANSI)
PROC m_pfnOrig; // 函數(shù)原地址
PROC m_pfnHook; // HOOK替換函數(shù)的地址
HMODULE m_hMod; // 模塊句柄
private:
// 在一個模塊的導(dǎo)入節(jié)區(qū)掛鉤一個函數(shù)
static BOOL WINAPI ReplaceIATEntryInAllMods(PCSTR pszCalleeModName,
PROC pfnOrig, PROC pfnHook);
// 在所有模塊的導(dǎo)入節(jié)區(qū)掛鉤一個函數(shù)
static void WINAPI ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
PROC pfnOrig, PROC pfnHook, HMODULE hmodCaller);
// 在某個模塊的導(dǎo)出節(jié)區(qū)掛鉤一個函數(shù)
static void WINAPI ReplaceEATEntryInOneMod(HMODULE hmod, PCSTR pszFunctionName, PROC pfnNew);
private:
// 當(dāng)一個DLL被新加載的時候使用該函數(shù)HOOK一個函數(shù)
static void WINAPI FixupNewlyLoadedModule(PCSTR pszModName, HMODULE hmod, DWORD dwFlags);
// 當(dāng)一個DLL調(diào)用FreeLibaray等函數(shù)卸載某個模塊時,調(diào)用此函數(shù)
static BOOL WINAPI FixupNewlyUnLoadModule(HMODULE hmod);
// 一些替換函數(shù)
static HMODULE WINAPI LoadLibraryA_Hook(PCSTR pszModulePath);
static HMODULE WINAPI LoadLibraryW_Hook(PCWSTR pszModulePath);
static HMODULE WINAPI LoadLibraryExA_Hook(PCSTR pszModulePath,
HANDLE hFile, DWORD dwFlags);
static HMODULE WINAPI LoadLibraryExW_Hook(PCWSTR pszModulePath,
HANDLE hFile, DWORD dwFlags);
static FARPROC WINAPI GetProcAddress_Hook(HMODULE hmod, PCSTR pszProcName);
static BOOL WINAPI FreeLibrary_Hook(HMODULE hLibModule);
private:
// 掛鉤某些特殊函數(shù)的CApiHook對象
static CApiHook sm_LoadLibraryA; // 掛鉤LoadLibryraA函數(shù)的CApiHook對象
static CApiHook sm_LoadLibraryW; // 掛鉤LoadLibryraW函數(shù)的CApiHook對象
static CApiHook sm_LoadLibraryExA; // 掛鉤LoadLibryraExA函數(shù)的CApiHook對象
static CApiHook sm_LoadLibraryExW; // 掛鉤LoadLibryraExW函數(shù)的CApiHook對象
static CApiHook sm_GetProcAddress; // 掛鉤GetProcAddress函數(shù)的CApiHook對象
static CApiHook sm_FreeLibrary; // 掛鉤FreeLibrary函數(shù)的CApiHook對象
};
//////////////////////////////// End of File //////////////////////////////////
#endif // __API_HOOK_CLASS__
想必其中某些函數(shù)不需要說了,需要說明的是FreeLibaray_Hook函數(shù),這個函數(shù)是FreeLibaray的替換函數(shù),通過sm_FreeLibrary對象進(jìn)行HOOK。
開始寫這個函數(shù)的時候,遇到了困難,理由是這樣的,因?yàn)槿绻@段代碼作為DLL注入到目標(biāo)進(jìn)程中去,如果通過鉤子的方法注入到目標(biāo)進(jìn)程中時,那么卸載鉤子時,會引發(fā)目標(biāo)進(jìn)程調(diào)用FreeLibaray來釋放這個DLL,此時會調(diào)用FreeLibaray_Hook函數(shù),哪怕在這個函數(shù)的最后“return ::FreeLibaray(hLibModule)”,也會出現(xiàn)問題。因?yàn)檫@個函數(shù)會產(chǎn)生了C/C++運(yùn)行時的框架代碼,就會在返回之后調(diào)用一些框架代碼,比如調(diào)用C++類的析構(gòu)函數(shù)之類,而此時該模塊已經(jīng)從目標(biāo)進(jìn)程中卸載,這必然會導(dǎo)致內(nèi)存訪問違規(guī)。因此,必須如下定義該函數(shù):
__declspec(naked) BOOL WINAPI CApiHook::FreeLibrary_Hook(HMODULE hLibModule)
這樣,VC編譯器就不會對CApiHook::FreeLibaray_Hook函數(shù)產(chǎn)生一些框架代碼,就需要自己寫內(nèi)聯(lián)匯編來維持堆棧的平衡。這里給出這個函數(shù)的定義:

CApiHook::FreeLibrary_Hook
__declspec(naked) BOOL WINAPI CApiHook::FreeLibrary_Hook(HMODULE hLibModule)
{
__asm
{
push ebp;
mov ebp, esp;
pushad;
push dword ptr[FreeLibrary_Hook];
call dword ptr[ModuleFromAddress];
cmp [ebp + 8], eax; ; [ebp + 8]中是參數(shù)模塊句柄的值
jne NotMe;
mov eax, sm_pHead;
Next:
cmp eax, NULL;
je CallTrue;
mov edx, [eax].m_pNext;
push edx;
push eax; ; 這里相當(dāng)于壓入this指針
call UnHook; ; 相當(dāng)于調(diào)用[eax].UnHook
pop eax;
jmp Next;
CallTrue:
popad;
pop ebp;
jmp dword ptr[sm_pFunFreeLibrary]; ; 如果是卸載本模塊,則可能是其他程序調(diào)用,需要跳轉(zhuǎn)到真正的FreeLibaray函數(shù)的地址
; 不是卸載的本模塊
NotMe:
push [ebp + 8];
call FixupNewlyUnLoadModule; ; 不是本模塊卸載,則調(diào)用修正的FreeLibrary函數(shù)
popad;
pop ebp;
mov eax, TRUE;
ret 4;
}
}
這里給出CApiHook類的完整定義:

CApiHook類和一些輔助函數(shù)的定義
#include <windows.h>
#include <ImageHlp.h>
#pragma comment(lib, "ImageHlp")

#include "APIHook.h"
#include <tlhelp32.h>


/**////////////////////////////////////////////////////////////////////////////////


// 從指定地址得到包含該地址的模塊句柄
static HMODULE WINAPI ModuleFromAddress(PVOID pv)


{
MEMORY_BASIC_INFORMATION mbi;
return((VirtualQuery(pv, &mbi, sizeof(mbi)) != 0)
? (HMODULE) mbi.AllocationBase : NULL);
}



/**////////////////////////////////////////////////////////////////////////////////

// 異常過濾函數(shù)
LONG WINAPI InvalidReadExceptionFilter(PEXCEPTION_POINTERS pep)


{
LONG lDisposition = EXCEPTION_EXECUTE_HANDLER;
return lDisposition;
}


/**////////////////////////////////////////////////////////////////////////////////

CApiHook* CApiHook::sm_pHead = NULL; // CApiHook對象鏈表頭結(jié)點(diǎn)

BOOL CApiHook::ExcludeAPIHookMod = TRUE; // 是否排除HOOK本模塊的函數(shù)

PROC CApiHook::sm_pFunFreeLibrary = NULL; // FreeLibrary的內(nèi)存中的地址



/**////////////////////////////////////////////////////////////////////////////////

// 構(gòu)造函數(shù)
CApiHook::CApiHook()


{
if (sm_pFunFreeLibrary == NULL)

{
sm_pFunFreeLibrary = GetProcAddressRaw(GetModuleHandleW(L"Kernel32.dll"), "FreeLibrary");
}

// 前插一個結(jié)點(diǎn)
m_pNext = sm_pHead;
sm_pHead = this;
Init(); // 初始化
}



/**////////////////////////////////////////////////////////////////////////////////

// 構(gòu)造函數(shù)
CApiHook::CApiHook(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook)


{
if (sm_pFunFreeLibrary == NULL)

{
sm_pFunFreeLibrary = GetProcAddressRaw(GetModuleHandleW(L"Kernel32.dll"), "FreeLibrary");
}
// 前插一個結(jié)點(diǎn)
m_pNext = sm_pHead;
sm_pHead = this;
HookApi(pszCalleeModName, pszFuncName, pfnHook); // 掛鉤某個API函數(shù)
}



/**////////////////////////////////////////////////////////////////////////////////


CApiHook::~CApiHook()


{
// 從鏈表中刪除本節(jié)點(diǎn)
CApiHook * p = sm_pHead;
if (p == this)

{
sm_pHead = p->m_pNext;
}
else

{
BOOL bFound = FALSE;

for (; p->m_pNext != NULL; p = p->m_pNext)

{
if (p->m_pNext == this)

{
p->m_pNext = p->m_pNext->m_pNext;
break;
}
}
}
UnHook(); // 撤銷掛鉤
}



/**////////////////////////////////////////////////////////////////////////////////

void CApiHook::Init()


{
m_hMod = NULL;
m_pszCalleeModName = NULL;
m_pszFuncName = NULL;
m_pfnOrig = NULL;
m_pfnHook = NULL;
}


/**////////////////////////////////////////////////////////////////////////////////

void CApiHook::Uninit()


{
// 這里不把模塊名、函數(shù)名和替換函數(shù)地址設(shè)置為NULL,防止重復(fù)加載
m_hMod = NULL;
m_pfnOrig = NULL;
}


/**////////////////////////////////////////////////////////////////////////////////

// 本函數(shù)不能為inline類型 —— 得到函數(shù)的實(shí)際地址
FARPROC WINAPI CApiHook::GetProcAddressRaw(HMODULE hmod, PCSTR pszProcName)


{
return(::GetProcAddress(hmod, pszProcName)); // 得到函數(shù)的真實(shí)地址
}



/**////////////////////////////////////////////////////////////////////////////////

// 在某個模塊中HOOK某個API函數(shù) —— 重要
BOOL WINAPI CApiHook::HookApi(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook)


{
if (m_pszCalleeModName != NULL)
UnHook();

// 得到HOOK信息
m_hMod = GetModuleHandleA(pszCalleeModName); // 得到模塊句柄
m_pszCalleeModName = pszCalleeModName;
m_pszFuncName = pszFuncName;
m_pfnHook = pfnHook; // 替換函數(shù)地址
if (m_hMod)
m_pfnOrig = GetProcAddressRaw(m_hMod, m_pszFuncName); // 原函數(shù)地址

// 如果原函數(shù)地址為NULL,可能是因?yàn)槟K并沒有加載(比如延遲加載DLL就會出現(xiàn)該問題)
if (m_pfnOrig == NULL)

{
#ifdef _DEBUG
wchar_t szPathname[MAX_PATH];
GetModuleFileNameW(NULL, szPathname, _countof(szPathname));
wchar_t sz[1024];
wsprintfW(sz, L"[%4u - %s] impossible to find %S\r\n",
GetCurrentProcessId(), szPathname, pszFuncName);
OutputDebugString(sz);
#endif
return FALSE;
}

#ifdef _DEBUG
// This section was used for debugging sessions when Explorer died as
// a folder content was requested
//
//static BOOL s_bFirstTime = TRUE;
//if (s_bFirstTime)
//{
// s_bFirstTime = FALSE;

// wchar_t szPathname[MAX_PATH];
// GetModuleFileNameW(NULL, szPathname, _countof(szPathname));
// wchar_t* pszExeFile = wcsrchr(szPathname, L'\\') + 1;
// OutputDebugStringW(L"Injected in ");
// OutputDebugStringW(pszExeFile);
// if (_wcsicmp(pszExeFile, L"Explorer.EXE") == 0)
// {
// DebugBreak();
// }
// OutputDebugStringW(L"\n --> ");
// wsprintfW(szPathname, L"%S", pszFuncName);
// OutputDebugStringW(szPathname);
// OutputDebugStringW(L"\n");
//}
#endif

// HOOK某個函數(shù)
ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnOrig, m_pfnHook);
return TRUE;
}


/**////////////////////////////////////////////////////////////////////////////////

// 撤銷掛鉤
void WINAPI CApiHook::UnHook()


{
if (m_pfnHook == NULL)
return;

// 撤銷掛鉤 —— 將函數(shù)地址復(fù)原
ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnHook, m_pfnOrig);

Uninit();
}



/**////////////////////////////////////////////////////////////////////////////////

// 將本進(jìn)程中所有模塊的IAT中的指定函數(shù)地址pfnCurrent替換為pfnNew
BOOL CApiHook::ReplaceIATEntryInAllMods(PCSTR pszCalleeModName,
PROC pfnCurrent, PROC pfnNew)


{
if (pszCalleeModName == NULL || pfnCurrent == NULL || pfnNew == NULL)
return FALSE;

// 得到當(dāng)前模塊(即包含這段代碼的模塊)
HMODULE hmodThisMod = ExcludeAPIHookMod
? ModuleFromAddress(ReplaceIATEntryInAllMods) : NULL;

// 得到本進(jìn)程中的模塊列表
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
if (hSnapshot == INVALID_HANDLE_VALUE)
return FALSE;



MODULEENTRY32 me =
{ sizeof(me) };

for (BOOL bOk = Module32First(hSnapshot, &me); bOk; bOk = Module32Next(hSnapshot, &me))

{
// 不替換當(dāng)前模塊的函數(shù)
if (me.hModule != hmodThisMod)

{
ReplaceIATEntryInOneMod(pszCalleeModName, pfnCurrent, pfnNew, me.hModule);
}
}

CloseHandle(hSnapshot);

return TRUE;
}



/**////////////////////////////////////////////////////////////////////////////////


// 替換模塊導(dǎo)入表中的函數(shù)地址
void WINAPI CApiHook::ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller)


{
ULONG ulSize;

// 一個異常可能被觸發(fā),因?yàn)橹T如explorer.exe這樣的進(jìn)程能夠快速地加載和卸載模塊
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL;
__try

{
pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToDataEx(
hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize, NULL);
}
__except (InvalidReadExceptionFilter(GetExceptionInformation()))

{
}

if (pImportDesc == NULL)
return;

// 在導(dǎo)入描述符中查找導(dǎo)入信息
for (; pImportDesc->Name; ++pImportDesc)

{
// 得到模塊名pszModName(模塊基地址 + 名稱偏移)
PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);

// 判斷模塊名是否為指定模塊
if (lstrcmpiA(pszModName, pszCalleeModName) == 0)

{
// 取得調(diào)用者導(dǎo)入函數(shù)地址表的在模塊中的函數(shù)地址(模塊基址 + 偏移)
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
((PBYTE)hmodCaller + pImportDesc->FirstThunk);

// 用新的函數(shù)地址替換當(dāng)前函數(shù)地址
for (; pThunk->u1.Function; pThunk++)

{

// 取得函數(shù)地址
PROC* ppfn = (PROC*) &pThunk->u1.Function;

// 是否是需要替換的函數(shù)(比較函數(shù)地址)
BOOL bFound = (*ppfn == pfnCurrent);
if (bFound)

{
// 修改地址
if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
sizeof (pfnNew), NULL) && (ERROR_NOACCESS == GetLastError()))

{
// 如果失敗,則更改頁保護(hù)屬性
DWORD dwOldProtect;
if (VirtualProtect(ppfn, sizeof (pfnNew), PAGE_WRITECOPY,
&dwOldProtect))

{

WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
sizeof (pfnNew), NULL);
VirtualProtect(ppfn, sizeof (pfnNew), dwOldProtect,
&dwOldProtect);
}
}
return;
}
}
}
}
}



/**////////////////////////////////////////////////////////////////////////////////

// 替換模塊導(dǎo)出表中函數(shù)的地址
void WINAPI CApiHook::ReplaceEATEntryInOneMod(HMODULE hmod, PCSTR pszFunctionName, PROC pfnNew)


{
// 得到模塊導(dǎo)出節(jié)地址
ULONG ulSize;

// 得到導(dǎo)出節(jié)目錄地址
PIMAGE_EXPORT_DIRECTORY pExportDir = NULL;
__try

{
pExportDir = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryEntryToData(
hmod, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ulSize);
}
__except (InvalidReadExceptionFilter(GetExceptionInformation()))

{
}

if (pExportDir == NULL)
return;

PDWORD pdwNamesRvas = (PDWORD) ((PBYTE) hmod + pExportDir->AddressOfNames);
PWORD pdwNameOrdinals = (PWORD)
((PBYTE) hmod + pExportDir->AddressOfNameOrdinals);
PDWORD pdwFunctionAddresses = (PDWORD)
((PBYTE) hmod + pExportDir->AddressOfFunctions);

// 遍歷模塊函數(shù)名表
for (DWORD n = 0; n < pExportDir->NumberOfNames; n++)

{
// 取得函數(shù)名
PSTR pszFuncName = (PSTR) ((PBYTE) hmod + pdwNamesRvas[n]);

// 如果不是指定的函數(shù),則繼續(xù)遍歷
if (lstrcmpiA(pszFuncName, pszFunctionName) != 0)
continue;

// 得到這個指定函數(shù)的序號
WORD ordinal = pdwNameOrdinals[n];

// 得到函數(shù)地址
PROC * ppfn = (PROC*) &pdwFunctionAddresses[ordinal];

// 用RVA(相對虛擬地址)更換成新的地址
pfnNew = (PROC) ((PBYTE) pfnNew - (PBYTE) hmod);

// 用新的函數(shù)地址替換當(dāng)前函數(shù)地址
if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew, sizeof (pfnNew), NULL)
&& (ERROR_NOACCESS == GetLastError()))

{
DWORD dwOldProtect;
if (VirtualProtect(ppfn, sizeof (pfnNew), PAGE_WRITECOPY,
&dwOldProtect))

{

WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
sizeof (pfnNew), NULL);
VirtualProtect(ppfn, sizeof (pfnNew), dwOldProtect, &dwOldProtect);
}
}
break;
}
}



/**////////////////////////////////////////////////////////////////////////////////

// 替換LoadLibaray*函數(shù)和GetProcAddress函數(shù)

CApiHook CApiHook::sm_LoadLibraryA ("Kernel32.dll", "LoadLibraryA",
(PROC) CApiHook::LoadLibraryA_Hook);

CApiHook CApiHook::sm_LoadLibraryW ("Kernel32.dll", "LoadLibraryW",
(PROC) CApiHook::LoadLibraryW_Hook);

CApiHook CApiHook::sm_LoadLibraryExA("Kernel32.dll", "LoadLibraryExA",
(PROC) CApiHook::LoadLibraryExA_Hook);

CApiHook CApiHook::sm_LoadLibraryExW("Kernel32.dll", "LoadLibraryExW",
(PROC) CApiHook::LoadLibraryExW_Hook);

CApiHook CApiHook::sm_GetProcAddress("Kernel32.dll", "GetProcAddress",
(PROC) CApiHook::GetProcAddress_Hook);

CApiHook CApiHook::sm_FreeLibrary("Kernel32.dll", "FreeLibrary",
(PROC) CApiHook::FreeLibrary_Hook);



/**////////////////////////////////////////////////////////////////////////////////

// 防止運(yùn)行時加載模塊(如線程調(diào)用LoadLibarayW等函數(shù))
void CApiHook::FixupNewlyLoadedModule(PCSTR pszModPath, HMODULE hmod, DWORD dwFlags)


{
// 如果一個新的模塊被加載,重新掛鉤函數(shù)
if ((hmod != NULL) &&
(hmod != ModuleFromAddress(FixupNewlyLoadedModule)) &&
((dwFlags & LOAD_LIBRARY_AS_DATAFILE) == 0) &&
((dwFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE) == 0) &&
((dwFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE) == 0)
)

{
// 遍歷CApiHook對象鏈,掛鉤個對象要求的API函數(shù)
for (CApiHook* p = sm_pHead; p != NULL; p = p->m_pNext)

{
// 如果某個對象的原函數(shù)地址為NULL,可能是延遲加載等原因?qū)е略瓉鞤LL不在內(nèi)存中而引起的
// 因此在這里根據(jù)模塊名獲得函數(shù)現(xiàn)在的地址
if (p->m_pfnOrig == NULL)

{
if (lstrcmpiA(p->m_pszCalleeModName, pszModPath) == 0)

{
p->m_hMod = hmod;
p->m_pfnOrig = p->GetProcAddressRaw(hmod, p->m_pszFuncName);
}
}
if (p->m_pfnOrig != NULL)

{
// 重新掛鉤每個函數(shù)
ReplaceIATEntryInAllMods(p->m_pszCalleeModName, p->m_pfnOrig, p->m_pfnHook);
}
else

{
#ifdef _DEBUG
// 可能是延遲加載導(dǎo)致p->m_pfnOrig為空
wchar_t szPathname[MAX_PATH];
GetModuleFileNameW(NULL, szPathname, _countof(szPathname));
wchar_t sz[1024];
wsprintfW(sz, L"[%4u - %s] impossible to find %S\r\n",
GetCurrentProcessId(), szPathname, p->m_pszCalleeModName);
OutputDebugString(sz);
#endif
}
}
}
}


/**////////////////////////////////////////////////////////////////////////////////

// 防止運(yùn)行時卸載模塊(如線程調(diào)用FreeLibrary等函數(shù))
BOOL WINAPI CApiHook::FixupNewlyUnLoadModule(HMODULE hmod)


{
if (hmod == NULL || hmod == ModuleFromAddress(FixupNewlyUnLoadModule))
return FALSE;

BOOL bFree = TRUE;
wchar_t szModPath[MAX_PATH];
szModPath[0] = L'\0';

if (!GetModuleFileNameW(hmod, szModPath, MAX_PATH))
return FALSE;

if (!::FreeLibrary(hmod))
return FALSE;

bFree = TRUE;
if (GetModuleFileNameW(hmod, szModPath, MAX_PATH))

{
// 如果還能夠得到地址,則說明該模塊在進(jìn)程地址空間中并沒有真正卸載
if (GetModuleHandleW(szModPath))
bFree = FALSE;
}

// 如果卸載了,則撤銷某些掛鉤
if (bFree)

{
for (CApiHook * p = sm_pHead; p; p = p->m_pNext)

{
if (p->m_pfnOrig && p->m_hMod == hmod)
p->UnHook();
}
}
return TRUE;
}



/**////////////////////////////////////////////////////////////////////////////////

// 掛鉤LoadLibarayA的替換函數(shù)
HMODULE WINAPI CApiHook::LoadLibraryA_Hook(PCSTR pszModulePath)


{
HMODULE hmod = ::LoadLibraryA(pszModulePath);
FixupNewlyLoadedModule(pszModulePath, hmod, 0);
return hmod;
}



/**////////////////////////////////////////////////////////////////////////////////

// 掛鉤LoadLibraryW的替換函數(shù)
HMODULE WINAPI CApiHook::LoadLibraryW_Hook(PCWSTR pszModulePath)


{
HMODULE hmod = ::LoadLibraryW(pszModulePath);
char szModPath[MAX_PATH];
WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, pszModulePath, -1,
szModPath, MAX_PATH * sizeof (char), NULL, NULL);
FixupNewlyLoadedModule(szModPath, hmod, 0);
return hmod;
}



/**////////////////////////////////////////////////////////////////////////////////

// 掛鉤LoadLibraryExA的替換函數(shù)
HMODULE WINAPI CApiHook::LoadLibraryExA_Hook(PCSTR pszModulePath,
HANDLE hFile, DWORD dwFlags)


{
HMODULE hmod = ::LoadLibraryExA(pszModulePath, hFile, dwFlags);
FixupNewlyLoadedModule(pszModulePath, hmod, dwFlags);
return hmod;
}



/**////////////////////////////////////////////////////////////////////////////////

// 掛鉤LoadLibraryExW的替換函數(shù)
HMODULE WINAPI CApiHook::LoadLibraryExW_Hook(PCWSTR pszModulePath,
HANDLE hFile, DWORD dwFlags)


{

HMODULE hmod = ::LoadLibraryExW(pszModulePath, hFile, dwFlags);
char szModPath[MAX_PATH];
WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, pszModulePath, -1,
szModPath, MAX_PATH * sizeof (char), NULL, NULL);
FixupNewlyLoadedModule(szModPath, hmod, dwFlags);
return hmod;
}



/**////////////////////////////////////////////////////////////////////////////////

// 掛鉤GetProcAddress的替換函數(shù)
FARPROC WINAPI CApiHook::GetProcAddress_Hook(HMODULE hmod, PCSTR pszProcName)


{
// 取得函數(shù)的實(shí)際地址
FARPROC pfn = GetProcAddressRaw(hmod, pszProcName);

CApiHook * p = sm_pHead;
for (; (pfn != NULL) && (p != NULL); p = p->m_pNext)

{
// 查看是否是想掛鉤的函數(shù),如果是,則返回新的替換函數(shù)
if (pfn == p->m_pfnOrig)

{
// 返回我們的替換函數(shù)地址
pfn = p->m_pfnHook;
break;
}
}
return pfn; // 返回替換函數(shù)地址
}


/**////////////////////////////////////////////////////////////////////////////////

// 攔截FreeLibrary的替換函數(shù)
// 注意函數(shù)采用內(nèi)聯(lián)匯編進(jìn)行編寫,需要使用__declspec(naked)標(biāo)記函數(shù),讓編譯器不產(chǎn)生框架代碼,自己維護(hù)堆棧平衡
__declspec(naked) BOOL WINAPI CApiHook::FreeLibrary_Hook(HMODULE hLibModule)


{
__asm

{
push ebp;
mov ebp, esp;
pushad;
push dword ptr[FreeLibrary_Hook];
call dword ptr[ModuleFromAddress];
cmp [ebp + 8], eax;
jne NotMe;
mov eax, sm_pHead;
Next:
cmp eax, NULL;
je CallTrue;
mov edx, [eax].m_pNext;
push edx;
push eax; ; 這里相當(dāng)于壓入this指針
call UnHook; ; 相當(dāng)于調(diào)用[eax].UnHook
pop eax;
jmp Next;
CallTrue:
popad;
pop ebp;
jmp dword ptr[sm_pFunFreeLibrary]; ; 如果是卸載本模塊,則可能是其他程序調(diào)用,需要跳轉(zhuǎn)到真正的FreeLibaray函數(shù)的地址

; 不是卸載的本模塊
NotMe:
push [ebp + 8];
call FixupNewlyUnLoadModule; ; 不是本模塊卸載,則調(diào)用修正的FreeLibrary函數(shù)

popad;
pop ebp;
mov eax, TRUE;
ret 4;
}
}



/**///////////////////////////////// End of File //////////////////////////////////

如上就可以實(shí)現(xiàn)掛鉤FreeLibaray函數(shù)的功能了。
同時,這里在HOOK所有LoadLibaray*函數(shù)的時候,調(diào)用了FexupNewlyLoadedModule函數(shù),該函數(shù)會遍歷所有CApiHook對象,查看是否存在某個對象的API原地址為NULL,如果是,就檢測模塊名是否一致,這樣就可以通過模塊得到這個API原始地址了,這樣就可以解決延遲加載的問題。