我正在进行所谓的“seh hooking”技术。实际上,它会更改内存区域的权限,并在其被访问时捕获异常,从而可以钩取函数。
它使用了单步陷阱,看起来像这样:
info->ContextRecord->EFlags |= 0x100;
恢复对PAGE_NOACCESS
的保护。
该应用程序在Win XP上运行良好,但在Win 7上表现不如预期。 它在Win 7上冻结了。 我非常怀疑这是因为“设置单步陷阱”之类的事情,但我不确定。
点击此处获取源包的直接下载链接。
我正在进行所谓的“seh hooking”技术。实际上,它会更改内存区域的权限,并在其被访问时捕获异常,从而可以钩取函数。
它使用了单步陷阱,看起来像这样:
info->ContextRecord->EFlags |= 0x100;
恢复对PAGE_NOACCESS
的保护。
该应用程序在Win XP上运行良好,但在Win 7上表现不如预期。 它在Win 7上冻结了。 我非常怀疑这是因为“设置单步陷阱”之类的事情,但我不确定。
点击此处获取源包的直接下载链接。
简短回答:
是的,单步标志是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;
}
如果代码遇到受保护的内存页面,它将引发警戒页面违规。我们检查是否在钩子位置。如果是,则一切正常,我们可以调用钩子回调。如果不在正确的位置,我们需要以某种方式重新保护代码,但如果现在这样做,我们就无法前进,并且总会在相同位置上引发异常并死锁应用程序。因此,我们设置了处理器单步标志。