学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

2万

积分

41

好友

1157

主题
发表于 2021-11-14 20:24:40 | 查看: 1277| 回复: 1
平常常用的三环Hook通常是InlineHook和虚Hook,这两种Hook方式均是通过修改内存来劫持控制流,InlineHook通过修改代码段上的代码而虚Hook通过修改虚表指针。因为均对内存做了修改(且往往Hook的位置都是敏感内存),很容易被诸如CRC之类的手段检测到。故学习了一下三环下的无痕HOOK,注意这里的无痕指的仅仅是不对代码段等内存产生修改,而不是不会被发现。

在x86架构下有一组特殊的寄存器叫做硬件断点寄存器,分别是Dr0-Dr7。其中Dr0-Dr3这四个寄存器用于于设置硬件断点的,Dr4和Dr5由系统保留,Dr6用于显示哪个硬件调试寄存器引发的断点,Dr7则是用于控制断点属性。如果我们能够设置硬件断点到我们想Hook的位置,那样程序运行到该处时就会抛出一个异常,转而进入异常处理函数;如果我们设置了与之匹配的异常处理函数,则可以在异常发生时劫持得到程序的控制流程流。 硬件断点寄存器可以通过修改线程上下文结构体(ThreadContext)来设置,而异常处理函数通常则是使用VEH。

下面来看一个demo,这个Demo实现了硬断MessageBoxA这个API

HMODULE hUser32 = GetModuleHandleA("user32.dll");
size_t hookaddr = (size_t)GetProcAddress(hUser32, "MessageBoxA");

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
        AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)ExceptionHandler);
        if(SetHwBreakpoint() == FALSE)  printf("SetHwBreakpoint Error:%d\n", GetLastError());
        break;
    }
    case DLL_THREAD_ATTACH:     // 对每一个新创建的线程均添加硬件断点
    {
        SetHwBreakpoint();
    }
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

BOOL SetHwBreakpoint()
{
    CONTEXT ctx;
    ctx.ContextFlags = CONTEXT_ALL;
    GetThreadContext(GetCurrentThread(), &ctx);
    ctx.Dr0 = hookaddr;
    ctx.Dr7 = 0x1;
    return SetThreadContext(GetCurrentThread(), &ctx);
}

size_t NTAPI ExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
{
    if ((size_t)ExceptionInfo->ExceptionRecord->ExceptionAddress == hookaddr)
    {
        printf("Hook!\n");
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    else
    {
        //在异常handler里重设drx防止断点被意外清除
        ExceptionInfo->ContextRecord->Dr0 = hookaddr;
        ExceptionInfo->ContextRecord->Dr7 = 0x405;
        return EXCEPTION_CONTINUE_SEARCH;
    }
}

如果用心的去试一下,就会发现这个Demo在某些情况下是无法实现Hook的,比如在远线程注入该Dll的时候就发现Hook不住MessageboxA

为什么会这样呢?这里又是一个点,就是一个程序的上下文(寄存器等信息)是绑定于线程还是进程的。三环的远线程注入DLL本质是将Dll写入该进程的内存中,然后CreateRemoteThread在DllMain的位置创建了一个线程运行Dll的Main函数。DllMain函数中调用了SetHwBreakpoint对硬件断点寄存器做了修改,但此时这一条线程并不是程序原先运行代码的线程(主线程),而是我们远程创建的新线程,故设置出来的硬件断点仅对这条用于初始化dll的新线程有效。 正确的做法应该是枚举线程,对每一条线程均设置上硬件断点。
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
        // DebugBreak();
        printf("hUser32:%p\n", hUser32);
        printf("hook:%p\n", hookaddr);

        AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)ExceptionHandler);
        SetHBToAllThread();     // 为所有线程设置硬件断点
        break;
    }
    case DLL_THREAD_ATTACH:
    {
        // 为新创建的线程设置硬件断点
        SetHwBreakpoint(GetCurrentThread());
    }
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

void SetHwBreakpoint(HANDLE hThread)
{
    CONTEXT ctx;
    ctx.ContextFlags = CONTEXT_ALL;
    GetThreadContext(hThread, &ctx);
    ctx.Dr0 = hookaddr;
    ctx.Dr7 = 0x1;
    if (SetThreadContext(hThread, &ctx) == FALSE)
        printf("SetHwBreakpoint Error:%d\n", GetLastError());
}
// 枚举线程,并对每一条已存在的线程设置硬件断点
void SetHBToAllThread() {
    HANDLE hThreadShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId());
    THREADENTRY32* threadInfo = new THREADENTRY32;
    HANDLE hThread = NULL;

    threadInfo->dwSize = sizeof(THREADENTRY32);
    int cnt = 0;

    while (Thread32Next(hThreadShot, threadInfo) != FALSE)
    {
        if (GetCurrentProcessId() == threadInfo->th32OwnerProcessID)
        {
            cnt++;
            printf("ThreadId:%x\n", threadInfo->th32ThreadID);
            hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, threadInfo->th32ThreadID);
            SetHwBreakpoint(hThread);
            CloseHandle(hThread);
        }
    }
    CloseHandle(hThreadShot);
}
温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的认可,还可以获得学币奖励,请尊重他人的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
论坛交流群:672619046

    发表于 2021-12-20 21:25:30
    看了LZ的帖子,我只想说一句很好很强大!

    小黑屋|手机版|站务邮箱|学逆向论坛 ( 粤ICP备2021023307号 )|网站地图

    GMT+8, 2024-4-17 04:32 , Processed in 0.095454 second(s), 43 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表