在win7上设置单步陷阱

Is set single step trap available on win 7?

本文关键字:单步 陷阱 设置 win7      更新时间:2023-10-16

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

它使用单步陷阱,看起来像:

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

恢复对PAGE_NOACCESS的保护。

该应用程序在win xp上运行良好,但在win 7上不例外。在第七场比赛中卡住了。我非常怀疑这是因为"设置单步陷阱"的事情,但我不确定。

点击这里直接下载源码包的链接

简短回答:

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

我已经设法下载了你的项目,一切都很好,没有修改在Windows 8与UAC关闭。所以它应该也能在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。受保护的页面将导致访问异常,并在此之后自动删除保护。任何人都不会处理此异常,因此它将传递给向量处理程序。抛出异常后,该页可再次访问。(参见创建保护页)

内存只能以页为单位进行保护(通常为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;
}

如果代码运行到受保护的内存页,它将抛出保护页冲突。我们检查我们是否在钩子的位置。如果一切正常,我们就可以调用钩子回调了。如果我们不在正确的位置,我们需要重新保护代码,但是如果我们现在这样做,我们就不能前进,并且总是在相同的位置上获得异常并死锁应用程序。因此,我们设置了处理器单步标志。

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

代价是在目标页中执行的每条指令的两个异常和一个页保护。不要尝试在附加调试器的情况下执行此操作。它会发疯的。

对于一个实际的实现,你可能需要同步对象来摆脱钩子而不崩溃程序和更好的钩子管理。

我真的很喜欢这个聪明的机制,希望有些人能从中学到一点。