阿倍仲麻吕 发表于 2024-4-1 23:56:05

VEH HOOK

很古老的技术了:之前分享了ssdt hook,这次分享veh hook

一般来说程序发生异常的时候会先交给编译器处理,如果编译器不处理会交给VEH,之后是SEH。
所以处理异常的顺序:编译器>VEH>SEH
VEH可以无痕hook,而一般int3 cc断点会修改代码,给要hook的地址插入一个0xcc,这样破坏了代码的完整性,过不了crc校验,但是VEH只能设置4个硬断点,这是不足之处。

ms提供了VEH异常的回调函数和set函数,一般来说dr0储存了引发中断函数的地址(我这么表述可能不准确,但大致是这个意思)
,cr7储存了断点的属性。

我们以messageboxA为例,当调用messageboxA时候会触发异常,进入我自己写的异常处理函数,这就实现了hook的效果。

第一步:拿到的messageboxA地址:
HMODULE hmodule = LoadLibraryA("user32.dll");
        breakpoint=(DWORD)GetProcAddress(hmodule, "MessageBoxA");
        if (breakpoint == 0)
        {
                MessageBoxA(NULL, "error", "warning", MB_OK);
        }

第二步,设置寄存器:注意这里ContextFlags 相当于掩码的作用,这里有个坑。
        CONTEXT context = { 0 };
        context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
        context.Dr0 = breakpoint;
        context.Dr7 = 1;
        SetThreadContext(GetCurrentThread(), &context);
        AddVectoredExceptionHandler(1, MyExpcetionHandler);
        MessageBoxA(NULL, "aaa", "sss", MB_OK);

设置回调函数:回调函数有固定的格式
LONG WINAPI MyExpcetionHandler(_EXCEPTION_POINTERS* ExceptionInfo)
{
        if ((DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress==breakpoint)
        {
                printf("EXCETION\n");
                ExceptionInfo->ContextRecord->Eip += 2;
        }
        return EXCEPTION_CONTINUE_EXECUTION;
}
注意ContextRecord->Eip保存的是发生中断的寄存器环境

ExceptionInfo->ContextRecord->Eip += 2;这里+2是为了跳过中断,弹出窗口,
为什么要+2呢?
这是因为windows的api 前两个字节都是mov edi,edi这条花指令,对应FF 8B
这里设置cr7的最低位为1,就是设置一个字节的断点长度,所谓1个字节就是可以在任何地址上设置断点,2个字节就是需要在偶数地址上设置断点。
这段代码会在messageboxA的首地址上断下来,修改eip+2会在下一个执行的时候跳过mov edi,edi,也就是断点的位置,同时保证了代码不出错。

FishC 发表于 2024-4-1 23:56:18

VEH(Vectored Exception Handling)hook是一种古老但常用的技术,可以在程序发生异常时进行无痕hook。与传统的断点方式不同,VEH hook不会修改原始代码,因此不会破坏代码完整性。

首先,我们来以调用MessageBoxA函数为例,演示如何使用VEH hook。

第一步:获取MessageBoxA函数的地址。我们可以使用LoadLibraryA函数加载user32.dll库,然后使用GetProcAddress函数获得MessageBoxA函数的地址。

HMODULE hmodule = LoadLibraryA("user32.dll");
DWORD breakpoint = (DWORD)GetProcAddress(hmodule, "MessageBoxA");
if (breakpoint == 0) {
    MessageBoxA(NULL, "error", "warning", MB_OK);
}

第二步:设置寄存器。我们需要设置上下文(Context)结构体,并将ContextFlags成员指定为CONTEXT_DEBUG_REGISTERS,表示要使用调试寄存器。然后,将Dr0寄存器设置为MessageBoxA函数的地址,Dr7寄存器设置为1,表示在Dr0寄存器指定的地址上设置断点。

CONTEXT context = { 0 };
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
context.Dr0 = breakpoint;
context.Dr7 = 1;
SetThreadContext(GetCurrentThread(), &context);

第三步:设置回调函数。回调函数需要有固定的格式,并且注册到VEH异常处理链中。在回调函数中,我们可以根据ExceptionInfo结构体中的信息判断是否是我们关注的异常,并执行相应的操作。在这个例子中,如果发生中断的地址与MessageBoxA函数的地址相同,则说明MessageBoxA函数被调用了,我们可以在这里加入自己的代码来实现hook的效果。最后,通过修改ContextRecord结构体中的Eip成员,可以跳过中断指令,从而执行MessageBoxA函数后续的代码。

LONG WINAPI MyExceptionHandler(_EXCEPTION_POINTERS* ExceptionInfo) {
    if ((DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress == breakpoint) {
      printf("EXCEPTION\n");
      ExceptionInfo->ContextRecord->Eip += 2;
    }
    return EXCEPTION_CONTINUE_EXECUTION;
}

最后,通过调用AddVectoredExceptionHandler函数将我们的回调函数添加到VEH异常处理链中。随后,调用MessageBoxA函数即可触发异常。

AddVectoredExceptionHandler(1, MyExceptionHandler);
MessageBoxA(NULL, "aaa", "sss", MB_OK);

需要注意的是,MessageBoxA函数的前两个字节是mov edi, edi指令,对应的机器码是FF 8B。在设置Dr7寄存器时,将最低位设置为1,表示设置一个字节的断点长度。这样我们就可以在任何地址上设置断点。在我们的代码中,断点被设置在了MessageBoxA函数的起始地址上,通过修改Eip(即程序计数器)跳过断点位置,保证了代码的正确执行。

希望以上解答能够帮助到您!如果还有其他问题,请随时提问。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

wp231957 发表于 2024-4-2 09:25:03

FishC 发表于 2024-4-1 23:56
VEH(Vectored Exception Handling)hook是一种古老但常用的技术,可以在程序发生异常时进行无痕hook。与传 ...

这个东西,脚本一类的语言(比如python) 就无法实现了吧

FishC 发表于 2024-4-2 09:25:15

wp231957 发表于 2024-04-02 09:25
这个东西,脚本一类的语言(比如python) 就无法实现 ...

是的,VEH hook是一项底层的技术,涉及到操作系统和CPU的特定功能。在脚本语言(如Python)中,由于执行环境相对较高级,无法直接访问和控制底层的操作系统和硬件。因此,无法使用纯粹的脚本语言来实现VEH hook。

不过,我们可以考虑借助其他方法来实现类似的效果。例如,在Python中可以使用ctypes模块调用C/C++编写的DLL库,来实现对底层功能的访问和控制。通过调用C/C++代码,我们可以使用操作系统提供的相关功能接口,实现类似VEH hook的功能。

另外,也可以使用Python的一些第三方库,如Pydbg、Pynput等,它们是基于调试器或输入事件监听的方式来实现hook的功能。虽然与VEH hook不同,但也可以达到类似的效果。这些库提供了API和回调函数,可以针对特定的事件进行处理。

总之,虽然无法直接在纯脚本语言中实现VEH hook,但可以考虑结合其他方法或借助第三方库来达到类似的效果。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: VEH HOOK