Detours Hook 工具源码阅读二

Detours Create Process & Load Dll API源码分析

Hook工作一般独立于宿主工程,甚至很多时候我们Hook的都不是自己的工程。那么如何注入就变成了一个严峻的问题。这部Detours API就是为了解决注入问题。

从withdll sample开始先看看疗效

D:\code\detour\Detours-main\bin.X64>withdll -d:simple64.dll sleep5.exe 2
withdll.exe: Starting: `sleep5.exe 2
withdll.exe:   with `D:\code\detour\Detours-main\bin.X64\simple64.dll'
simple64.dll: Starting.
simple64.dll: Detoured SleepEx().
simple64.dll: Removed SleepEx() (result=0), slept 2016 ticks.

通过withdll加载simple64.dll 并启动 sleep5.exe,这样simple64 中Hook了sleep API在sleep中也生效。我们来看看如何实现的。

镇楼原理图

在这里插入图片描述

withdll sample 源码

为了更好的理解主线流程,删减掉来部分不核心的代码

int CDECL main(int argc, char **argv)
{
    /// 
    // 参数解析 ,略
    /// 
    // Dlls合法性检验,略
    /// 
    // 创建进程
    // 参数拼接,略
    STARTUPINFOA si;
    PROCESS_INFORMATION pi;
    DetourCreateProcessWithDllsA(szFullExe[0] ? szFullExe : NULL, szCommand,
                                     NULL, NULL, TRUE, dwFlags, NULL, NULL,
                                     &si, &pi, nDlls, rpszDllsOut, NULL);
    // 唤醒主线程,直到运行结束,略
    return 0;
}

核心是调用Detours API DetourCreateProcessWithDllsA 创建进程 。

DetourCreateProcessWithDllsA函数分析

BOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName,
                                         _Inout_opt_ LPSTR lpCommandLine,
                                         _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
                                         _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
                                         _In_ BOOL bInheritHandles,
                                         _In_ DWORD dwCreationFlags,
                                         _In_opt_ LPVOID lpEnvironment,
                                         _In_opt_ LPCSTR lpCurrentDirectory,
                                         _In_ LPSTARTUPINFOA lpStartupInfo,
                                         _Out_ LPPROCESS_INFORMATION lpProcessInformation,
                                         _In_ DWORD nDlls,
                                         _In_reads_(nDlls) LPCSTR *rlpDlls,
                                         _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
{
    if (pfCreateProcessA == NULL) {
        pfCreateProcessA = CreateProcessA;
    }

    // 调用WindowsAPI创建进程并挂起
    if (!pfCreateProcessA(lpApplicationName,
                          lpCommandLine,
                          lpProcessAttributes,
                          lpThreadAttributes,
                          bInheritHandles,
                          dwCreationFlags | CREATE_SUSPENDED,
                          lpEnvironment,
                          lpCurrentDirectory,
                          lpStartupInfo,
                          lpProcessInformation)) {
        return FALSE;
    }

    // 两种方式加载DLL
    if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) &&
        !DetourProcessViaHelperDllsA(lpProcessInformation->dwProcessId,
                                     nDlls,
                                     rlpDlls,
                                     pfCreateProcessA)) {

        TerminateProcess(lpProcessInformation->hProcess, ~0u);
        CloseHandle(lpProcessInformation->hProcess);
        CloseHandle(lpProcessInformation->hThread);
        return FALSE;
    }

    // 唤醒主线程
    ResumeThread(lpProcessInformation->hThread);
    CloseHandle(lpProcessInformation->hProcess);
    CloseHandle(lpProcessInformation->hThread);
    return TRUE;
}

先启动一个进程运行exe,在使用两种方式加载Dll。这里设置了启动后挂起,因此启动后的进程不会继续往下跑。

DetourUpdateProcessWithDll函数分析——加载方式1

BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess,
                                       _In_reads_(nDlls) LPCSTR *rlpDlls,
                                       _In_ DWORD nDlls)
{
    // 查找已加载的PE image中最后一个hModule 
    HMODULE hModule = NULL;
    HMODULE hLast = NULL;
    for (;;) {
        IMAGE_NT_HEADERS32 inh;
        if ((hLast = EnumerateModulesInProcess(hProcess, hLast, &inh, NULL)) == NULL) {
            break;
        }
        if ((inh.FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) {
            hModule = hLast;
        }
    }

    // 检查是不是64位系统,略
    BOOL bIs64BitOS = TRUE;
    // 检查是不是32为应用
    BOOL bIs32BitProcess = FALSE;
    
    // 调用xxxEX继续加载
    return DetourUpdateProcessWithDllEx(hProcess,
                                        hModule,
                                        bIs32BitProcess,
                                        rlpDlls,
                                        nDlls);
}

检查系统是不是64位,exe是不是64,然后就继续调用DetourUpdateProcessWithDllEx了

DetourUpdateProcessWithDllEx函数分析

BOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess,
                                         _In_ HMODULE hModule,
                                         _In_ BOOL bIs32BitProcess,
                                         _In_reads_(nDlls) LPCSTR *rlpDlls,
                                         _In_ DWORD nDlls)
{

    // 确认PE块代码是不是32位,略
    BOOL bIs32BitExe = FALSE;

    // 修改之前(DetourRestoreAfterWith)先保存各种headers
    DETOUR_EXE_RESTORE der;
    if (!RecordExeRestore(hProcess, hModule, der)) {
        return FALSE;
    }

    // exe是32位且进程是64位的处理逻辑
    if (bIs32BitExe && !bIs32BitProcess) {
        // 升级成64位
        UpdateFrom32To64(hProcess, hModule,
                              IMAGE_FILE_MACHINE_AMD64,
                              der)}

    // 64位进程,64位PE结构,加载DLL
    UpdateImports64(hProcess, hModule, rlpDlls, nDlls);

    /// Update the CLR header.
    // 更新CLR头:CLR(Common Language Runtime)头是.NET程序集的一部分
    // .net可执行文件特殊处理,略
    

     Save the undo data to the target process.
    // 上保存的 DETOUR_EXE_RESTORE der 拷贝到目标进程里,做为 撤销(操作需要使用的)数据
    DetourCopyPayloadToProcess(hProcess, DETOUR_EXE_RESTORE_GUID, &der, sizeof(der))return TRUE;
}

保存环境,把运行环境和exe都改成64位,然后调用UpdateImports64 Load Dll

UpdateImports64函数分析

这个函数实在是太晦涩了,允许我先吐一会……
uimports.cpp就这一个300行风格古怪的函数,大概率是祖传代码。
实在受不了了!理解这个函数需要了解Dll的加载过程,我们单开一篇文章来讲解吧。
略略略略略略略略略略略略
略略略略略略略略略略略略
略略略略略略略略略略略略

DetourProcessViaHelperDllsA函数分析——加载方式2

BOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid,
                                        _In_ DWORD nDlls,
                                        _In_reads_(nDlls) LPCSTR *rlpDlls,
                                        _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
{  
    //把要加载的Dll都用AllocExeHelper处理一下,生成一个PDETOUR_EXE_HELPER结构
    PDETOUR_EXE_HELPER helper = NULL;
    AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)// 拼接WINDIR路径
    CHAR szExe[MAX_PATH];
    GetEnvironmentVariableA("WINDIR", szExe, ARRAYSIZE(szExe));
    // 拼出rundll32.exe的路径
    StringCchCatA(szExe, ARRAYSIZE(szExe), "\\syswow64\\rundll32.exe");
    CHAR szCommand[MAX_PATH];
    StringCchPrintfA(szCommand, ARRAYSIZE(szCommand),
                          "rundll32.exe \"%s\",#1", &helper->rDlls[0]);

    // 创建一个rundll32.exe的进程
    PROCESS_INFORMATION pi;
    STARTUPINFOA si;
    ZeroMemory(&pi, sizeof(pi));
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    pfCreateProcessA(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED,
                         NULL, NULL, &si, &pi)// PDETOUR_EXE_HELPER结构的helper写到刚刚创建的,rundll32进程中去。
    DetourCopyPayloadToProcess(pi.hProcess,
                                        DETOUR_EXE_HELPER_GUID,
                                        helper, helper->cb)// 执行到结束                                                      
    ResumeThread(pi.hThread);
    WaitForSingleObject(pi.hProcess, INFINITE);

    // 回收资源
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    FreeExeHelper(&helper);
 
    return TRUE;
}

有点迷糊了。这里又创建了一个rundll32.exe进程,且加载了dll。我们的预期是给DetourCreateProcessWithDllsA创建出的进程加载dll。是哪里理解错了吗?

我们实验一下吧。先把方式一的路堵上
[图片]

观察启动rundll32.exe进程时的参数
[图片]

szExe :C:\WINDOWS\syswow64\rundll32.exe
szCommandrundll32.exe “D:\code\detour\Detours-main\bin.X64\simple32.dll”,#1
语义是,使用rundll32.exe 打开 simple32.dll 的#1号导出函数。
Dependency瞅一眼,这是个啥?
[图片]

最终在Makefile这里找到了结果
[图片]

原来是这个函数!那我们调起来吧。
让主进程停在这里
[图片]

这个时候rundll32.exe进程应该还在挂起状态,我们Attach上去看看。
[图片]

这时停在ntdll里,因为创建进程以后就挂起了嘛,符合预期。
现在simple 的dllmain 和 DetourFinishHelperProcess 里都打上断线。看看到底怎么会回事。跑起来。
[图片]

先进DllMain,符合预期。
[图片]

原来是这里Load出了之前塞进rundll32.exe 里的 Helper。
[图片]

[图片]

和主进程里看到的完全一致。
再跑并没能顺利进入DetourFinishHelperProcess。但是看到的返回码确实符合预期。因为我们改了DetourUpdateProcessWithDll,这里必然失败。
[图片]

至此,真相大白。我们也退出调试模式吧。

BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
{
    // 判断是不是加载了DetourHelper信息的进程,并且Load DetourHelper信息
    if (DetourIsHelperProcess()) {
        return TRUE;
    }
}
rundllndll32进程,加载sample.dll时运行dllmain
BOOL WINAPI DetourIsHelperProcess(VOID)
{
    // Load DetourHelper信息放在全家变量中。
    pvData = DetourFindPayloadEx(DETOUR_EXE_HELPER_GUID, &cbData);
    
    s_pHelper = (PDETOUR_EXE_HELPER)pvData;
    return TRUE;
}
DetourHelper被加载在 s_pHelper中
 VOID CALLBACK DetourFinishHelperProcess(_In_ HWND,
                                        _In_ HINSTANCE,
                                        _In_ LPSTR,
                                        _In_ INT)
{

    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, s_pHelper->pid);
   
    rlpDlls = new NOTHROW LPCSTR [s_pHelper->nDlls];

    DetourUpdateProcessWithDll(hProcess, rlpDlls, s_pHelper->nDlls);

    ExitProcess(Result);
}

然后rundll32调用DetourFinishHelperProcess,于是又回来到方式一。
SO,假设方式一加载失败,Detours会调用rundll32起个进程,再跑一边加载dll的过程,相当于重试机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值