在Windows7上是否可用设置单步陷阱?

14

我正在进行所谓的“seh hooking”技术。实际上,它会更改内存区域的权限,并在其被访问时捕获异常,从而可以钩取函数。

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

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

恢复对PAGE_NOACCESS的保护。

该应用程序在Win XP上运行良好,但在Win 7上表现不如预期。 它在Win 7上冻结了。 我非常怀疑这是因为“设置单步陷阱”之类的事情,但我不确定。

点击此处获取源包的直接下载链接。


3
请按正常方式挂钩函数。使用此类疯狂技术很可能会导致您的代码被标记为恶意软件。 - Raymond Chen
你以管理员身份启动了程序吗?Windows 7为程序权限添加了一定的安全性和烦恼程度。你可以通过设置链接器->清单文件->UAC执行级别->requireAdmin来请求权限。 - typ1232
1个回答

12

简短回答:

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

我已经成功下载了您的项目,并且在关闭UAC的Windows 8上没有进行任何修改,一切都很正常。因此,在Windows 7上应该也可以工作。启动VEH Hooking Test.exe时,它会显示两个消息框,在每个消息框后,我都会得到MessageBoxA控制台输出,因此钩子起作用了。也许尝试以管理员身份在Windows 7上启动程序?


详细回答:

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

VEH hooking是一种非常慢的hooking方法,因此它不能在性能关键的hooks中使用,例如图形hook,其中您的hooks每秒击中多次。它通常用于一次性的hooks。 VEH hooking的目的是非常隐蔽的,在别人的代码中没有写入任何内存,也不必使用调试寄存器。


以下是我将如何使用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;
}
如果代码遇到受保护的内存页面,它将引发警戒页面违规。我们检查是否在钩子位置。如果是,则一切正常,我们可以调用钩子回调。如果不在正确的位置,我们需要以某种方式重新保护代码,但如果现在这样做,我们就无法前进,并且总会在相同位置上引发异常并死锁应用程序。因此,我们设置了处理器单步标志。
现在当接收到单步异常时,我们可以再次设置警卫保护,因为我们已经执行了一条指令。这就是如何始终保护目标页面而不错过任何钩子命中的方法。
成本是每个在目标页面中执行的指令都需要两个异常和一个页面保护。不要尝试在附加了调试器的情况下执行此操作。它会变得疯狂。
对于实际实现,您可能需要同步对象来摆脱钩子而不崩溃程序,并更好地管理钩子。
我真的很喜欢这个巧妙的机制,并希望有些人学到了一些东西。

我不确定这是否回答了OP的问题,但感谢您的解释,我很喜欢阅读它! - Lorenzo Dematté
@dema80 感谢您的反馈。我实际上已经猜到了从哪里下载该文件。在 Windows 6.x 上一切都很正常。 - typ1232
@typ1232 我修改了我的问题,添加了源代码的直接下载链接,对造成的不便表示抱歉。 - Mickey Shine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接