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

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

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]中是參數模塊句柄的值
jne NotMe;
mov eax, sm_pHead;
Next:
cmp eax, NULL;
je CallTrue;
mov edx, [eax].m_pNext;
push edx;
push eax; ; 這里相當于壓入this指針
call UnHook; ; 相當于調用[eax].UnHook
pop eax;
jmp Next;
CallTrue:
popad;
pop ebp;
jmp dword ptr[sm_pFunFreeLibrary]; ; 如果是卸載本模塊,則可能是其他程序調用,需要跳轉到真正的FreeLibaray函數的地址
; 不是卸載的本模塊
NotMe:
push [ebp + 8];
call FixupNewlyUnLoadModule; ; 不是本模塊卸載,則調用修正的FreeLibrary函數
popad;
pop ebp;
mov eax, TRUE;
ret 4;
}
}
這里給出CApiHook類的完整定義:

CApiHook類和一些輔助函數的定義
#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);
}



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

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


{
LONG lDisposition = EXCEPTION_EXECUTE_HANDLER;
return lDisposition;
}


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

CApiHook* CApiHook::sm_pHead = NULL; // CApiHook對象鏈表頭結點

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

PROC CApiHook::sm_pFunFreeLibrary = NULL; // FreeLibrary的內存中的地址



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

// 構造函數
CApiHook::CApiHook()


{
if (sm_pFunFreeLibrary == NULL)

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

// 前插一個結點
m_pNext = sm_pHead;
sm_pHead = this;
Init(); // 初始化
}



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

// 構造函數
CApiHook::CApiHook(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook)


{
if (sm_pFunFreeLibrary == NULL)

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



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


CApiHook::~CApiHook()


{
// 從鏈表中刪除本節點
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()


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


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

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


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



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

// 在某個模塊中HOOK某個API函數 —— 重要
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; // 替換函數地址
if (m_hMod)
m_pfnOrig = GetProcAddressRaw(m_hMod, m_pszFuncName); // 原函數地址

// 如果原函數地址為NULL,可能是因為模塊并沒有加載(比如延遲加載DLL就會出現該問題)
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某個函數
ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnOrig, m_pfnHook);
return TRUE;
}


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

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


{
if (m_pfnHook == NULL)
return;

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

Uninit();
}



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

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


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

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

// 得到本進程中的模塊列表
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))

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

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

CloseHandle(hSnapshot);

return TRUE;
}



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


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


{
ULONG ulSize;

// 一個異常可能被觸發,因為諸如explorer.exe這樣的進程能夠快速地加載和卸載模塊
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;

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

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

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

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

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

{

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

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

{
// 修改地址
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);
}
}
return;
}
}
}
}
}



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

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


{
// 得到模塊導出節地址
ULONG ulSize;

// 得到導出節目錄地址
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);

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

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

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

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

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

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

// 用新的函數地址替換當前函數地址
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*函數和GetProcAddress函數

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);



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

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


{
// 如果一個新的模塊被加載,重新掛鉤函數
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函數
for (CApiHook* p = sm_pHead; p != NULL; p = p->m_pNext)

{
// 如果某個對象的原函數地址為NULL,可能是延遲加載等原因導致原來DLL不在內存中而引起的
// 因此在這里根據模塊名獲得函數現在的地址
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)

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

{
#ifdef _DEBUG
// 可能是延遲加載導致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
}
}
}
}


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

// 防止運行時卸載模塊(如線程調用FreeLibrary等函數)
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))

{
// 如果還能夠得到地址,則說明該模塊在進程地址空間中并沒有真正卸載
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的替換函數
HMODULE WINAPI CApiHook::LoadLibraryA_Hook(PCSTR pszModulePath)


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



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

// 掛鉤LoadLibraryW的替換函數
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的替換函數
HMODULE WINAPI CApiHook::LoadLibraryExA_Hook(PCSTR pszModulePath,
HANDLE hFile, DWORD dwFlags)


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



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

// 掛鉤LoadLibraryExW的替換函數
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的替換函數
FARPROC WINAPI CApiHook::GetProcAddress_Hook(HMODULE hmod, PCSTR pszProcName)


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

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

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

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


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

// 攔截FreeLibrary的替換函數
// 注意函數采用內聯匯編進行編寫,需要使用__declspec(naked)標記函數,讓編譯器不產生框架代碼,自己維護堆棧平衡
__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; ; 這里相當于壓入this指針
call UnHook; ; 相當于調用[eax].UnHook
pop eax;
jmp Next;
CallTrue:
popad;
pop ebp;
jmp dword ptr[sm_pFunFreeLibrary]; ; 如果是卸載本模塊,則可能是其他程序調用,需要跳轉到真正的FreeLibaray函數的地址

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

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



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

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