在win 7上设置单步陷阱?

时间:2013-04-29 05:37:09

标签: c++ windows winapi visual-c++ hook

我正在做一个所谓的“seh hooking'”。实际上它会更改内存区域的权限并在访问时捕获异常,因此可以挂钩该函数。

它使用单步陷阱,如下所示:

info->ContextRecord->EFlags |= 0x100;

将保护恢复为PAGE_NOACCESS

该应用程序在win xp上运行良好,但在win 7上没有例外。它在win 7中被冻结。我非常怀疑这是因为设置了单步陷阱'事情,但我不确定。

Click here到源包的直接下载链接

1 个答案:

答案 0 :(得分:11)

简短回答:

是的,单步标志是x86架构的一部分,并且仍然通过处理器上下文的eflags组件在Windows 7中实现。

我已经设法下载了你的项目,一切正常,没有在关闭UAC的Windows 8上进行修改。所以它也适用于Windows 7。当启动VEH Hooking Test.exe时,它会显示两个消息框,在每个消息框之后我得到MessageBoxA控制台输出,因此挂钩工作。也许尝试在Windows 7上以管理员身份启动程序?


答案很长:

SEH代表结构化异常处理,但您所描述的内容更像是VEH - 向量异常处理。

VEH挂钩是一种非常慢的挂钩方法,所以它不能真正用于性能关键钩子,例如图形钩子,例如你的钩子每秒多次击中。它通常用于一次性挂钩。 VEH挂钩的目的是要非常隐秘。没有用别人的代码写的内存,你也不必使用调试寄存器。


以下是我将如何使用c ++实现它。

首先,您必须注册一个向量的异常处理程序。这是该进程的全局异常处理程序。未处理并将落入操作系统的每个抛出异常都将被此处理程序捕获。

PVOID pExHandler = AddVectoredExceptionHandler(1, VectoredHandler);

在此之后,您应该设置HOOK_LOCATION(要挂钩的地址)所在页面的内存保护。我正在使用的新保护是PAGE_EXECUTE_READ|PAGE_GUARD。受保护的页面将导致访问异常,并在此之后自动删除保护。任何人都不会处理此异常,因此它将落入我们的向量处理程序。抛出异常后,可以再次访问该页面。 (见Creating Guard Pages

内存只能在页面中保护(通常为0x1000字节长)。这就是为什么我们不仅可以保护挂钩位置并且具有巨大的性能开销。

DWORD orgProt;
VirtualProtect(HOOK_LOCATION, 1, PAGE_EXECUTE_READ|PAGE_GUARD, &orgProt);

现在我们的异常处理程序。这就是它的样子:

LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS exc)
{
    if (exc->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION)
    {
        // guard page exeption occured. guard page protection is gone now

        if (HOOK_LOCATION == reinterpret_cast<long*>(exc->ContextRecord->Eip)) {
            // hook location was hit! call any hook callbacks here
        } else {
            // hook was not hit and has to be refreshed. set single-step flag
            exc->ContextRecord->EFlags |= 0x100;
        }

        return EXCEPTION_CONTINUE_EXECUTION;
    }

    if (exc->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP)
    {
        // single-step exeption occured. single-step flag is cleared now

        // set guard page protection
        DWORD oldProt;
        VirtualProtect(HOOK_LOCATION, 1, PAGE_EXECUTE_READ|PAGE_GUARD, &oldProt);

        return EXCEPTION_CONTINUE_EXECUTION;
    }

    return EXCEPTION_CONTINUE_SEARCH;
}

如果代码进入受保护的内存页面,它将抛出防护页面违规。我们检查我们是否在钩子位置。如果我们一切都很好,我们可以调用钩子回调。如果我们不在正确的位置,我们需要重新保护代码,但如果我们现在就这样做,我们就无法前进并始终在同一位置获得异常并使应用程序死锁。所以我们设置处理器单步标志。

现在,当收到单步异常时,我们可以再次设置保护保护,因为我们按一条指令前进。这就是我们如何始终保护目标页面并且不会错过任何钩子命中。

成本是两个例外,每个指令在目标页面中执行一个页面保护。不要尝试使用附加的调试器。它会变得疯狂。

对于实际的实现,您可能需要同步对象来摆脱钩子而不会使程序崩溃并更好地进行钩子管理。

我真的很喜欢这个聪明的机制,并希望有些人对它有所了解。