牛人Jeffrey Richter写的Hook API工具类,我修正了少许BUG,在Windows XP/Visual Studio 2005环境下测试成功
/******************************************************************************
Module: APIHook.h
Notices: Copyright (c) 2000 Jeffrey Richter
Revisor: ZhouQinsheng
******************************************************************************/
#pragma once
///
class CApiHookHelp
{
public:
// Hook a function in all modules
CApiHookHelp(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook,
BOOL fExcludeAPIHookMod);
// Unhook a function from all modules
~CApiHookHelp();
// Returns the original address of the hooked function
operator PROC() { return(m_pfnOrig); }
protected:
// Calls the real GetProcAddress
static FARPROC WINAPI GetProcAddressRaw(HMODULE hmod, PCSTR pszProcName);
// Replaces a symbol's address in a module's import section
static void WINAPI ReplaceIATEntryInAllMods(PCSTR pszCalleeModName
, PROC pfnOrig, PROC pfnHook, BOOL fExcludeAPIHookMod);
// Replaces a symbol's address in all module's import sections
static void WINAPI ReplaceIATEntryInOneMod(PCSTR pszCalleeModName
, PROC pfnOrig, PROC pfnHook, HMODULE hmodCaller);
private:
// Used to trap when DLLs are newly loaded
static HMODULE WINAPI LoadLibraryA(PCSTR pszModulePath);
static HMODULE WINAPI LoadLibraryW(PCWSTR pszModulePath);
static HMODULE WINAPI LoadLibraryExA(PCSTR pszModulePath
, HANDLE hFile, DWORD dwFlags);
static HMODULE WINAPI LoadLibraryExW(PCWSTR pszModulePath
, HANDLE hFile, DWORD dwFlags);
// Returns address of replacement function if hooked function is requested
static FARPROC WINAPI GetProcAddress(HMODULE hmod, PCSTR pszProcName);
// Used when a DLL is newly loaded after hooking a function
static void WINAPI FixupNewlyLoadedModule(HMODULE hmod, DWORD dwFlags);
// Returns the HMODULE that contains the specified memory address
static HMODULE ModuleFromAddress(PVOID pv);
VOID FirstHookAtAll(PROC pNew);
VOID LastUnHookAtAll();
protected:
// Instantiates hooks on these functions
static CApiHookHelp *sm_pLoadLibraryA;
static CApiHookHelp *sm_pLoadLibraryW;
static CApiHookHelp *sm_pLoadLibraryExA;
static CApiHookHelp *sm_pLoadLibraryExW;
static CApiHookHelp *sm_pGetProcAddress;
private:
static PVOID sm_pvMaxAppAddr; // Maximum private memory address
static CApiHookHelp* sm_pHead; // Address of first object
CApiHookHelp* m_pNext; // Address of next object
PCSTR m_pszCalleeModName; // Module containing the function (ANSI)
PCSTR m_pszFuncName; // Function name in callee (ANSI)
PROC m_pfnOrig; // Original function address in callee
PROC m_pfnHook; // Hook function address
BOOL m_fExcludeAPIHookMod; // Hook module w/CApiHookHelp implementation?
};
End of File //
/******************************************************************************
Module: APIHook.cpp
Notices: Copyright (c) 2000 Jeffrey Richter
Revisor: ZhouQinsheng
******************************************************************************/
#include "stdafx.h"
#include <ImageHlp.h>
#pragma comment(lib, "ImageHlp")
#include "ApiHookHelp.h"
#include "Toolhelp.h"
#ifdef ASSERT
#define chASSERT ASSERT
#else
#define chASSERT
#endif
///
// When an application runs on Windows 98 under a debugger, the debugger
// makes the module's import section point to a stub that calls the desired
// function. To account for this, the code in this module must do some crazy
// stuff. These variables are needed to help with the crazy stuff.
// The highest private memory address (used for Windows 98 only)
PVOID CApiHookHelp::sm_pvMaxAppAddr = NULL;
const BYTE cPushOpCode = 0x68; // The PUSH opcode on x86 platforms
///
// The head of the linked-list of CApiHookHelp objects
CApiHookHelp *CApiHookHelp::sm_pHead = NULL;
CApiHookHelp *CApiHookHelp::sm_pLoadLibraryA = NULL;
CApiHookHelp *CApiHookHelp::sm_pLoadLibraryW = NULL;
CApiHookHelp *CApiHookHelp::sm_pLoadLibraryExA = NULL;
CApiHookHelp *CApiHookHelp::sm_pLoadLibraryExW = NULL;
CApiHookHelp *CApiHookHelp::sm_pGetProcAddress = NULL;
///
CApiHookHelp::CApiHookHelp(PSTR pszCalleeModName,
PSTR pszFuncName,
PROC pfnHook,
BOOL fExcludeAPIHookMod)
{
FirstHookAtAll(pfnHook);
if (sm_pvMaxAppAddr == NULL)
{
// Functions with address above lpMaximumApplicationAddress require
// special processing (Windows 98 only)
SYSTEM_INFO si;
GetSystemInfo(&si);
sm_pvMaxAppAddr = si.lpMaximumApplicationAddress;
}
m_pNext = sm_pHead; // The next node was at the head
sm_pHead = this; // This node is now at the head
// Save information about this hooked function
m_pszCalleeModName = pszCalleeModName;
m_pszFuncName = pszFuncName;
m_pfnHook = pfnHook;
m_fExcludeAPIHookMod = fExcludeAPIHookMod;
m_pfnOrig = GetProcAddressRaw(GetModuleHandleA(pszCalleeModName)
, m_pszFuncName);
chASSERT(m_pfnOrig != NULL); // Function doesn't exist
if (m_pfnOrig > sm_pvMaxAppAddr)
{
// The address is in a shared DLL; the address needs fixing up
PBYTE pb = (PBYTE) m_pfnOrig;
if (pb[0] == cPushOpCode)
{
// Skip over the PUSH op code and grab the real address
PVOID pv = * (PVOID*) &pb[1];
m_pfnOrig = (PROC) pv;
}
}
// Hook this function in all currently loaded modules
ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnOrig, m_pfnHook
, m_fExcludeAPIHookMod);
}
///
CApiHookHelp::~CApiHookHelp()
{
// Unhook this function from all modules
ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnHook, m_pfnOrig,
m_fExcludeAPIHookMod);
// Remove this object from the linked list
CApiHookHelp* p = sm_pHead;
if (p == this) // Removing the head node
{
sm_pHead = p->m_pNext;
}
else
{
BOOL fFound = FALSE;
// Walk list from head and fix pointers
for (; !fFound && (p->m_pNext != NULL); p = p->m_pNext)
{
if (p->m_pNext == this)
{
// Make the node that points to us point to the our next node
p->m_pNext = p->m_pNext->m_pNext;
break;
}
}
chASSERT(fFound);
}
LastUnHookAtAll();
}
///
// NOTE: This function must NOT be inlined
FARPROC WINAPI CApiHookHelp::GetProcAddressRaw(HMODULE hmod,
PCSTR pszProcName)
{
if(sm_pGetProcAddress && sm_pGetProcAddress->m_pfnOrig)
{
return ((FARPROC (WINAPI *)(HMODULE, PCSTR))sm_pGetProcAddress->m_pfnOrig)(hmod, pszProcName);
}
return ::GetProcAddress(hmod, pszProcName);
}
///
void CApiHookHelp::ReplaceIATEntryInAllMods(PCSTR pszCalleeModName,
PROC pfnCurrent,
PROC pfnNew,
BOOL fExcludeAPIHookMod)
{
HMODULE hmodThisMod = fExcludeAPIHookMod
? ModuleFromAddress(ReplaceIATEntryInAllMods) : NULL;
// Get the list of modules in this process
CToolhelp th(TH32CS_SNAPMODULE, GetCurrentProcessId());
MODULEENTRY32 me = { sizeof(me) };
for (BOOL fOk = th.ModuleFirst(&me); fOk; fOk = th.ModuleNext(&me))
{
// NOTE: We don't hook functions in our own module
if (me.hModule != hmodThisMod)
{
// Hook this function in this module
ReplaceIATEntryInOneMod(pszCalleeModName, pfnCurrent, pfnNew
, me.hModule);
}
}
}
///
void CApiHookHelp::ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
PROC pfnCurrent,
PROC pfnNew,
HMODULE hmodCaller)
{
// Get the address of the module's import section
ULONG ulSize;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)
ImageDirectoryEntryToData(hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT
, &ulSize);
if (pImportDesc == NULL)
{
return; // This module has no import section
}
// Find the import descriptor containing references to callee's functions
for (; pImportDesc->Name; pImportDesc++)
{
PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);
if (lstrcmpiA(pszModName, pszCalleeModName) == 0)
{
break; // Found
}
}
if (pImportDesc->Name == 0)
{
return; // This module doesn't import any functions from this callee
}
// Get caller's import address table (IAT) for the callee's functions
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
((PBYTE) hmodCaller + pImportDesc->FirstThunk);
// Replace current function address with new function address
for (; pThunk->u1.Function; pThunk++)
{
// Get the address of the function address
PROC* ppfn = (PROC*) &pThunk->u1.Function;
// Is this the function we're looking for?
BOOL fFound = (*ppfn == pfnCurrent);
if (!fFound && (*ppfn > sm_pvMaxAppAddr))
{
// If this is not the function and the address is in a shared DLL,
// then maybe we're running under a debugger on Windows 98. In this
// case, this address points to an instruction that may have the
// correct address.
PBYTE pbInFunc = (PBYTE) *ppfn;
if (pbInFunc[0] == cPushOpCode)
{
// We see the PUSH instruction, the real function address follows
ppfn = (PROC*) &pbInFunc[1];
// Is this the function we're looking for?
fFound = (*ppfn == pfnCurrent);
}
}
if (fFound)
{
// The addresses match, change the import section address
if(!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew
, sizeof(pfnNew), NULL))
{
DWORD dwOldFlag = 0;
VirtualProtectEx(GetCurrentProcess(), ppfn, sizeof(pfnNew)
, PAGE_WRITECOPY, &dwOldFlag);
WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew
, sizeof(pfnNew), NULL);
VirtualProtectEx(GetCurrentProcess(), ppfn, sizeof(pfnNew)
, dwOldFlag, &dwOldFlag);
}
return; // We did it, get out
}
} // If we get to here, the function is not in the caller's import section
}
///
HMODULE WINAPI CApiHookHelp::LoadLibraryA(PCSTR pszModulePath)
{
HMODULE hmod = ((HMODULE (WINAPI *)(PCSTR))sm_pLoadLibraryA->m_pfnOrig)(pszModulePath);
FixupNewlyLoadedModule(hmod, 0);
return(hmod);
}
///
HMODULE WINAPI CApiHookHelp::LoadLibraryW(PCWSTR pszModulePath)
{
HMODULE hmod = ((HMODULE (WINAPI *)(PCWSTR))sm_pLoadLibraryW->m_pfnOrig)(pszModulePath);
FixupNewlyLoadedModule(hmod, 0);
return(hmod);
}
///
HMODULE WINAPI CApiHookHelp::LoadLibraryExA(PCSTR pszModulePath,
HANDLE hFile,
DWORD dwFlags)
{
HMODULE hmod = ((HMODULE (WINAPI *)(PCSTR, HANDLE, DWORD))sm_pLoadLibraryExA->m_pfnOrig)(pszModulePath, hFile, dwFlags);
FixupNewlyLoadedModule(hmod, dwFlags);
return(hmod);
}
///
HMODULE WINAPI CApiHookHelp::LoadLibraryExW(PCWSTR pszModulePath,
HANDLE hFile,
DWORD dwFlags)
{
HMODULE hmod = ((HMODULE (WINAPI *)(PCWSTR, HANDLE, DWORD))sm_pLoadLibraryExA->m_pfnOrig)(pszModulePath, hFile, dwFlags);
FixupNewlyLoadedModule(hmod, dwFlags);
return(hmod);
}
///
FARPROC WINAPI CApiHookHelp::GetProcAddress(HMODULE hmod,
PCSTR pszProcName)
{
// Get the true address of the function
FARPROC pfn = GetProcAddressRaw(hmod, pszProcName);
// Is it one of the functions that we want hooked?
CApiHookHelp* p = sm_pHead;
for (; (pfn != NULL) && (p != NULL); p = p->m_pNext)
{
if (pfn == p->m_pfnOrig)
{
// The address to return matches an address we want to hook
// Return the hook function address instead
pfn = p->m_pfnHook;
break;
}
}
return(pfn);
}
///
void CApiHookHelp::FixupNewlyLoadedModule(HMODULE hmod,
DWORD dwFlags)
{
// If a new module is loaded, hook the hooked functions
if ((hmod != NULL) && ((dwFlags & LOAD_LIBRARY_AS_DATAFILE) == 0))
{
for (CApiHookHelp* p = sm_pHead; p != NULL; p = p->m_pNext)
{
ReplaceIATEntryInOneMod(p->m_pszCalleeModName
, p->m_pfnOrig, p->m_pfnHook, hmod);
}
}
}
///
// Returns the HMODULE that contains the specified memory address
HMODULE CApiHookHelp::ModuleFromAddress(PVOID pv)
{
MEMORY_BASIC_INFORMATION mbi;
return((VirtualQuery(pv, &mbi, sizeof(mbi)) != 0)
? (HMODULE) mbi.AllocationBase : NULL);
}
///
// Hook LoadLibrary functions and GetProcAddress so that hooked functions
// are handled correctly if these functions are called.
VOID CApiHookHelp::FirstHookAtAll(PROC pNew)
{
if(sm_pLoadLibraryA)
{
return;
}
if(pNew == reinterpret_cast<PROC>(LoadLibraryA)
|| pNew == reinterpret_cast<PROC>(LoadLibraryW)
|| pNew == reinterpret_cast<PROC>(LoadLibraryExA)
|| pNew == reinterpret_cast<PROC>(LoadLibraryExW)
|| pNew == reinterpret_cast<PROC>(GetProcAddress))
{
return;
}
sm_pLoadLibraryA = new CApiHookHelp("Kernel32.dll", "LoadLibraryA",
reinterpret_cast<PROC>(LoadLibraryA), TRUE);
sm_pLoadLibraryW = new CApiHookHelp("Kernel32.dll", "LoadLibraryW",
reinterpret_cast<PROC>(LoadLibraryW), TRUE);
sm_pLoadLibraryExA = new CApiHookHelp("Kernel32.dll", "LoadLibraryExA",
reinterpret_cast<PROC>(LoadLibraryExA), TRUE);
sm_pLoadLibraryExW = new CApiHookHelp("Kernel32.dll", "LoadLibraryExW",
reinterpret_cast<PROC>(LoadLibraryExW), TRUE);
sm_pGetProcAddress = new CApiHookHelp("Kernel32.dll", "GetProcAddress",
reinterpret_cast<PROC>(GetProcAddress), TRUE);
}
VOID CApiHookHelp::LastUnHookAtAll()
{
if(!sm_pGetProcAddress)
{
return;
}
if(sm_pHead != sm_pGetProcAddress)
{
return;
}
delete sm_pGetProcAddress;
sm_pGetProcAddress = NULL;
delete sm_pLoadLibraryExW;
sm_pLoadLibraryExW = NULL;
delete sm_pLoadLibraryExA;
sm_pLoadLibraryExA = NULL;
delete sm_pLoadLibraryW;
sm_pLoadLibraryW = NULL;
delete sm_pLoadLibraryA;
sm_pLoadLibraryA = NULL;
}
End of File //