HOOK DLL

前一阶段闲着无聊,用远程线程注入的方法把DLL注入到Explorer.exe进程实现音乐循环播放。

  在DLL中的代码是这样的:


BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, PVOID fImpLoad)
{
    
static HANDLE hThread;
    
static DWORD dwThreadId;
    
switch (fdwReason)
    {
    
case DLL_PROCESS_ATTACH:
        g_hInstDll 
= hInstDll;
        hThread 
= BEGINTHREADEX(NULL, 0, ThreadProc, (PVOID)hInstDll, 0&dwThreadId);
        
if (hThread == NULL)
        {
            
return FALSE;
        }
        
break;

    
case DLL_PROCESS_DETACH:
        SendMessage(g_hWnd, WM_CLOSE, 
00);
        WaitForSingleObject(hThread, INFINITE);    
// 这里存在死循环

        CloseHandle(hThread);    
// 执行不到这里
        break;

    
case DLL_THREAD_ATTACH:
        
break;

    
case DLL_THREAD_DETACH:
        
break;

    
default:
        
break;
    }
    
return TRUE;
}

  在DLL_PROCESS_ATTACH的时候创建了一个线程,这个线程中创建了一个隐藏的窗口,并利用MCI实现播放音乐,具体就不贴出了。然后在DLL_PROCESS_DETACH中停止和关闭设备。

  但是在应用程序中,发现注入时无异常,在需要撤销DLL映射时出现了问题,程序出现了死循环,在等待远程线程执行FreeLibaray完毕时不返回了。

  注意上面代码中DLL_PROCESS_DETACH的代码,SendMessage用来关闭窗口,窗口会在WM_DESTROY通知下调用PostQuitMessage(0)使得线程的消息循环退出,从而线程退出。但是WaitForSingleObject函数无法返回。

  后来发现这里存在死循环,因为执行DllMain的线程是序列化的,必须等待一个线程执行完毕之后另一个线程才能执行。在SendMessage后,导致创建的线程即将退出,这时该线程会调用DllMain,并以DLL_THREAD_DETACH作为通知。可是调用SendMessage的线程调用DllMain时却还在等待即将退出的线程结束,DllMain还没有返回,因此存在了死循环。

  因此,在DllMain中不适合调用WaitForSingleObject等函数来等待线程完毕。


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,同时也能解决延迟加载的问题。
  以下是这个类的声明:


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

__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类的完整定义:

 


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

阅读更多
上一篇《Windows via C/C++》学习笔记 (八) Windows 线程池 纤程
下一篇[转]利用Winsock实现HTTP的GET请求
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭