Hook导入表 —— 实现挂钩FreeLibaray和HOOK延迟加载模块的API

  最近在研究Windows Ring3上的API Hook,对HOOK导入表这种方法进行了研究。HOOK导入表所用的C++类大同小异,不同的就是如何实现HOOK一个延迟加载的模块中的函数,以及FreeLibaray某个函数之后再次LoadLibaray加载这个模块所导致的模块基地址不同的时候,这时该如何HOOK住这个模块中的API。
  显然地,挂钩LoadLibarayA/W、LoadLibarayExA/W、GetProcAddress这些函数还不够,还需要挂钩FreeLibrary函数。这里我参考了《Windows核心编程》(第五版)中的一个程序,封装了一个C++类,能够HOOK住FreeLibrary,同时也能解决延迟加载的问题。
  以下是这个类的声明:

ContractedBlock.gif ExpandedBlockStart.gif 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函数产生一些框架代码,就需要自己写内联汇编来维持堆栈的平衡。这里给出这个函数的定义:
ContractedBlock.gif ExpandedBlockStart.gif 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类的完整定义:

 

ContractedBlock.gif ExpandedBlockStart.gif CApiHook类和一些辅助函数的定义
#include <windows.h>
#include 
<ImageHlp.h>
#pragma comment(lib, 
"ImageHlp")

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

ExpandedBlockStart.gifContractedBlock.gif
/**////


// 从指定地址得到包含该地址的模块句柄
static HMODULE WINAPI ModuleFromAddress(PVOID pv)
ExpandedBlockStart.gifContractedBlock.gif
{
    MEMORY_BASIC_INFORMATION mbi;
    
return((VirtualQuery(pv, &mbi, sizeof(mbi)) != 0
        
? (HMODULE) mbi.AllocationBase : NULL);
}



ExpandedBlockStart.gifContractedBlock.gif
/**////

// 异常过滤函数
LONG WINAPI InvalidReadExceptionFilter(PEXCEPTION_POINTERS pep)
ExpandedBlockStart.gifContractedBlock.gif
{
    LONG lDisposition 
= EXCEPTION_EXECUTE_HANDLER;
    
return lDisposition;
}


ExpandedBlockStart.gifContractedBlock.gif
/**////

CApiHook
* CApiHook::sm_pHead = NULL;    // CApiHook对象链表头结点

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

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


ExpandedBlockStart.gifContractedBlock.gif
/**////

// 构造函数
CApiHook::CApiHook()
ExpandedBlockStart.gifContractedBlock.gif
{
    
if (sm_pFunFreeLibrary == NULL)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        sm_pFunFreeLibrary 
= GetProcAddressRaw(GetModuleHandleW(L"Kernel32.dll"), "FreeLibrary");
    }


    
// 前插一个结点
    m_pNext  = sm_pHead;
    sm_pHead 
= this;
    Init();        
// 初始化
}



ExpandedBlockStart.gifContractedBlock.gif
/**////

// 构造函数
CApiHook::CApiHook(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook)
ExpandedBlockStart.gifContractedBlock.gif
{
    
if (sm_pFunFreeLibrary == NULL)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        sm_pFunFreeLibrary 
= GetProcAddressRaw(GetModuleHandleW(L"Kernel32.dll"), "FreeLibrary");
    }

    
// 前插一个结点
    m_pNext = sm_pHead;
    sm_pHead 
= this;
    HookApi(pszCalleeModName, pszFuncName, pfnHook);    
// 挂钩某个API函数
}



ExpandedBlockStart.gifContractedBlock.gif
/**////


CApiHook::
~CApiHook()
ExpandedBlockStart.gifContractedBlock.gif
{
    
// 从链表中删除本节点
    CApiHook * p = sm_pHead;
    
if (p == this)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        sm_pHead 
= p->m_pNext; 
    }

    
else
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        BOOL bFound 
= FALSE;

        
for (; p->m_pNext != NULL; p = p->m_pNext)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
if (p->m_pNext == this)
ExpandedSubBlockStart.gifContractedSubBlock.gif            

                p
->m_pNext = p->m_pNext->m_pNext; 
                
break;
            }

        }

    }

    UnHook();        
// 撤销挂钩
}



ExpandedBlockStart.gifContractedBlock.gif
/**////

void CApiHook::Init()
ExpandedBlockStart.gifContractedBlock.gif
{
    m_hMod 
= NULL;
    m_pszCalleeModName 
= NULL;
    m_pszFuncName 
= NULL;
    m_pfnOrig 
= NULL;
    m_pfnHook 
= NULL;
}


ExpandedBlockStart.gifContractedBlock.gif
/**////

void CApiHook::Uninit()
ExpandedBlockStart.gifContractedBlock.gif
{
    
// 这里不把模块名、函数名和替换函数地址设置为NULL,防止重复加载
    m_hMod = NULL;
    m_pfnOrig 
= NULL;
}


ExpandedBlockStart.gifContractedBlock.gif
/**////

// 本函数不能为inline类型 —— 得到函数的实际地址
FARPROC WINAPI CApiHook::GetProcAddressRaw(HMODULE hmod, PCSTR pszProcName)
ExpandedBlockStart.gifContractedBlock.gif
{
    
return(::GetProcAddress(hmod, pszProcName));    // 得到函数的真实地址
}



ExpandedBlockStart.gifContractedBlock.gif
/**////

// 在某个模块中HOOK某个API函数 —— 重要
BOOL WINAPI CApiHook::HookApi(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook)
ExpandedBlockStart.gifContractedBlock.gif
{
    
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)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
#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;
}


ExpandedBlockStart.gifContractedBlock.gif
/**////

// 撤销挂钩
void WINAPI CApiHook::UnHook()
ExpandedBlockStart.gifContractedBlock.gif
{
    
if (m_pfnHook == NULL)
        
return;

    
// 撤销挂钩 —— 将函数地址复原
    ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnHook, m_pfnOrig);

    Uninit();
}



ExpandedBlockStart.gifContractedBlock.gif
/**////

// 将本进程中所有模块的IAT中的指定函数地址pfnCurrent替换为pfnNew
BOOL CApiHook::ReplaceIATEntryInAllMods(PCSTR pszCalleeModName, 
                                        PROC pfnCurrent, PROC pfnNew)
ExpandedBlockStart.gifContractedBlock.gif
{
    
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;


ExpandedSubBlockStart.gifContractedSubBlock.gif    MODULEENTRY32 me 
= sizeof(me) };

    
for (BOOL bOk = Module32First(hSnapshot, &me); bOk; bOk = Module32Next(hSnapshot, &me))
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
// 不替换当前模块的函数
        if (me.hModule != hmodThisMod)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            ReplaceIATEntryInOneMod(pszCalleeModName, pfnCurrent, pfnNew, me.hModule);
        }

    }


    CloseHandle(hSnapshot);

    
return TRUE;
}



ExpandedBlockStart.gifContractedBlock.gif
/**////


// 替换模块导入表中的函数地址
void WINAPI CApiHook::ReplaceIATEntryInOneMod(PCSTR pszCalleeModName, 
                                       PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller)
ExpandedBlockStart.gifContractedBlock.gif
{
    ULONG ulSize;

    
// 一个异常可能被触发,因为诸如explorer.exe这样的进程能够快速地加载和卸载模块
    PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL;
    __try
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        pImportDesc 
= (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToDataEx(
            hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, 
&ulSize, NULL);
    }
 
    __except (InvalidReadExceptionFilter(GetExceptionInformation()))
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
    }


    
if (pImportDesc == NULL)
        
return;

    
// 在导入描述符中查找导入信息
    for (; pImportDesc->Name; ++pImportDesc)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
// 得到模块名pszModName(模块基地址 + 名称偏移)
        PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);

        
// 判断模块名是否为指定模块
        if (lstrcmpiA(pszModName, pszCalleeModName) == 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
// 取得调用者导入函数地址表的在模块中的函数地址(模块基址 + 偏移)
            PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA) 
                ((PBYTE)hmodCaller 
+ pImportDesc->FirstThunk);

            
// 用新的函数地址替换当前函数地址
            for (; pThunk->u1.Function; pThunk++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{

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

                
// 是否是需要替换的函数(比较函数地址)
                BOOL bFound = (*ppfn == pfnCurrent);
                
if (bFound)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
{
                    
// 修改地址
                    if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew, 
                        
sizeof (pfnNew), NULL) && (ERROR_NOACCESS == GetLastError()))
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
{
                        
// 如果失败,则更改页保护属性
                        DWORD dwOldProtect;
                        
if (VirtualProtect(ppfn, sizeof (pfnNew), PAGE_WRITECOPY, 
                            
&dwOldProtect))
ExpandedSubBlockStart.gifContractedSubBlock.gif                        
{

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

                    }

                    
return;
                }

            }

        }

    }

}



ExpandedBlockStart.gifContractedBlock.gif
/**////

// 替换模块导出表中函数的地址
void WINAPI CApiHook::ReplaceEATEntryInOneMod(HMODULE hmod, PCSTR pszFunctionName, PROC pfnNew)
ExpandedBlockStart.gifContractedBlock.gif
{
    
// 得到模块导出节地址
    ULONG ulSize;

    
// 得到导出节目录地址
    PIMAGE_EXPORT_DIRECTORY pExportDir = NULL;
    __try
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        pExportDir 
= (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryEntryToData(
            hmod, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, 
&ulSize);
    }
 
    __except (InvalidReadExceptionFilter(GetExceptionInformation()))
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
    }


    
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++)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
// 取得函数名
        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()))
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            DWORD dwOldProtect;
            
if (VirtualProtect(ppfn, sizeof (pfnNew), PAGE_WRITECOPY, 
                
&dwOldProtect))
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{

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

        }

        
break;
    }

}



ExpandedBlockStart.gifContractedBlock.gif
/**////

// 替换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);


ExpandedBlockStart.gifContractedBlock.gif
/**////

// 防止运行时加载模块(如线程调用LoadLibarayW等函数)
void CApiHook::FixupNewlyLoadedModule(PCSTR pszModPath, HMODULE hmod, DWORD dwFlags)
ExpandedBlockStart.gifContractedBlock.gif
{
    
// 如果一个新的模块被加载,重新挂钩函数
    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)
        )
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
// 遍历CApiHook对象链,挂钩个对象要求的API函数
        for (CApiHook* p = sm_pHead; p != NULL; p = p->m_pNext)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
// 如果某个对象的原函数地址为NULL,可能是延迟加载等原因导致原来DLL不在内存中而引起的
            
// 因此在这里根据模块名获得函数现在的地址
            if (p->m_pfnOrig == NULL)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
if (lstrcmpiA(p->m_pszCalleeModName, pszModPath) == 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
{
                    p
->m_hMod = hmod;
                    p
->m_pfnOrig = p->GetProcAddressRaw(hmod, p->m_pszFuncName);
                }

            }

            
if (p->m_pfnOrig != NULL)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
// 重新挂钩每个函数
                ReplaceIATEntryInAllMods(p->m_pszCalleeModName, p->m_pfnOrig, p->m_pfnHook);
            }

            
else
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
#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
            }

        }

    }

}


ExpandedBlockStart.gifContractedBlock.gif
/**////

// 防止运行时卸载模块(如线程调用FreeLibrary等函数)
BOOL WINAPI CApiHook::FixupNewlyUnLoadModule(HMODULE hmod)
ExpandedBlockStart.gifContractedBlock.gif
{
    
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))
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
// 如果还能够得到地址,则说明该模块在进程地址空间中并没有真正卸载
        if (GetModuleHandleW(szModPath))
            bFree 
= FALSE;
    }


    
// 如果卸载了,则撤销某些挂钩
    if (bFree)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
for (CApiHook * p = sm_pHead; p; p = p->m_pNext)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
if (p->m_pfnOrig && p->m_hMod == hmod)
                p
->UnHook();
        }

    }

    
return TRUE;
}



ExpandedBlockStart.gifContractedBlock.gif
/**////

// 挂钩LoadLibarayA的替换函数
HMODULE WINAPI CApiHook::LoadLibraryA_Hook(PCSTR pszModulePath)
ExpandedBlockStart.gifContractedBlock.gif
{
    HMODULE hmod 
= ::LoadLibraryA(pszModulePath);
    FixupNewlyLoadedModule(pszModulePath, hmod, 
0);
    
return hmod;
}



ExpandedBlockStart.gifContractedBlock.gif
/**////

// 挂钩LoadLibraryW的替换函数
HMODULE WINAPI CApiHook::LoadLibraryW_Hook(PCWSTR pszModulePath)
ExpandedBlockStart.gifContractedBlock.gif
{
    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;
}



ExpandedBlockStart.gifContractedBlock.gif
/**////

// 挂钩LoadLibraryExA的替换函数
HMODULE WINAPI CApiHook::LoadLibraryExA_Hook(PCSTR pszModulePath, 
                                        HANDLE hFile, DWORD dwFlags)
ExpandedBlockStart.gifContractedBlock.gif
{
    HMODULE hmod 
= ::LoadLibraryExA(pszModulePath, hFile, dwFlags);
    FixupNewlyLoadedModule(pszModulePath, hmod, dwFlags);
    
return hmod;
}



ExpandedBlockStart.gifContractedBlock.gif
/**////

// 挂钩LoadLibraryExW的替换函数
HMODULE WINAPI CApiHook::LoadLibraryExW_Hook(PCWSTR pszModulePath, 
                                        HANDLE hFile, DWORD dwFlags)
ExpandedBlockStart.gifContractedBlock.gif
{

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



ExpandedBlockStart.gifContractedBlock.gif
/**////

// 挂钩GetProcAddress的替换函数
FARPROC WINAPI CApiHook::GetProcAddress_Hook(HMODULE hmod, PCSTR pszProcName)
ExpandedBlockStart.gifContractedBlock.gif
{
    
// 取得函数的实际地址
    FARPROC pfn = GetProcAddressRaw(hmod, pszProcName);

    CApiHook 
* p = sm_pHead;
    
for (; (pfn != NULL) && (p != NULL); p = p->m_pNext)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
// 查看是否是想挂钩的函数,如果是,则返回新的替换函数
        if (pfn == p->m_pfnOrig)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
// 返回我们的替换函数地址
            pfn = p->m_pfnHook;
            
break;
        }

    }

    
return pfn;    // 返回替换函数地址
}


ExpandedBlockStart.gifContractedBlock.gif
/**////

// 拦截FreeLibrary的替换函数
// 注意函数采用内联汇编进行编写,需要使用__declspec(naked)标记函数,让编译器不产生框架代码,自己维护堆栈平衡
__declspec(naked) BOOL WINAPI CApiHook::FreeLibrary_Hook(HMODULE hLibModule)
ExpandedBlockStart.gifContractedBlock.gif
{
    __asm
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        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;
    }

}



ExpandedBlockStart.gifContractedBlock.gif
/**///// End of File //

 

  如上就可以实现挂钩FreeLibaray函数的功能了。

  同时,这里在HOOK所有LoadLibaray*函数的时候,调用了FixupNewlyLoadedModule函数,该函数会遍历所有CApiHook对象,查看是否存在某个对象的API原地址为NULL,如果是,就检测模块名是否一致,这样就可以通过模块得到这个API原始地址了,这样就可以解决延迟加载的问题。

转载于:https://www.cnblogs.com/wz19860913/archive/2010/04/29/1723578.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值