Windows 64位DLL入口点重写

Windows 64位DLL入口点重写,windows,64-bit,kernel,hook,driver,Windows,64 Bit,Kernel,Hook,Driver,简而言之,问题是这样的。我正在编写一个内核模式的Windows驱动程序,当加载内核模式DLL(或其他可执行模块)时会收到通知。在某些情况下,我必须截取DLL入口点例程。也就是说,重写它,以便首先调用我的例程,然后我可以将控制权传递给原始入口点 在32位(确切地说是x86)上,这样做没有问题。我得到了模块基映射地址,它实际上是以标准PE头(由Windows可执行文件使用)开始的。DLL入口点有一个RVA(相对于映像基的地址)。我只是用例程的地址减去模块的基址来覆盖它。瞧 现在,事情在64位中更加复

简而言之,问题是这样的。我正在编写一个内核模式的Windows驱动程序,当加载内核模式DLL(或其他可执行模块)时会收到通知。在某些情况下,我必须截取DLL入口点例程。也就是说,重写它,以便首先调用我的例程,然后我可以将控制权传递给原始入口点

在32位(确切地说是x86)上,这样做没有问题。我得到了模块基映射地址,它实际上是以标准PE头(由Windows可执行文件使用)开始的。DLL入口点有一个RVA(相对于映像基的地址)。我只是用例程的地址减去模块的基址来覆盖它。瞧

现在,事情在64位中更加复杂。问题是RVA仍然是32位整数。这种RVA覆盖从映像基址开始到以4GB偏移量结束的地址范围。在同一个可执行模块中引用任何符号都没有问题(假设它不超过4GB大小),但是这会给跨模块拦截带来问题。当然,我的可执行模块和我正在尝试钩住的模块不必落入相同的4GB范围,因此存在一个问题

我暂时解决了这个问题,通过无条件的
jmp
将原始例程prolog代码覆盖到我的代码中。在64位平台上,这需要12个字节。然后,为了从我的例程中调用原始代码,我恢复了被覆盖的12个字节(意思是-我在覆盖之前保存它们)

到目前为止,没有问题。但是现在情况正在改变,我必须支持对入口点例程的多线程访问(请不要问为什么,它与加载到所谓“用户空间”中的多会话DLL有关,每个终端会话都是独立的)

解决方案之一是使用全局锁,但我希望避免这种情况

我知道所谓的“蹦床功能”,但我也想避免这种情况。为此,需要对函数prolog代码进行运行时解码,以正确识别指令边界和可能的分支

最近我想到了另一个主意。如果我能找到原始DLL中一些“不需要的”部分,它至少有12个字节长(大小为
mov-RAX-addr
+
jmp-RAX
)。然后这一部分可以被
jmp
覆盖到我的手中。然后可以将入口点RVA设置为该部分

要使其工作,所需的只是可以覆盖的适当部分。我认为有这种可能性,因为PE头包含许多几十年来不再使用的历史字段

这个想法值得一试,还是这是一种众所周知的技术?安迪:还有其他建议吗


提前谢谢。

您有几种选择。不幸的是,你只能从这3个选项中选择2个:100%固体;易于实施;便宜

在.TEXT部分的末尾,很可能会找到未使用的空间。这是因为Windows将图像部分以4k为单位映射到内存中,通常.text部分不是精确的倍数

另一个易于实现的方法是使用PE头。一个非常安全的覆盖区域是DOS存根。存在的问题是,不能保证PE头与输入例程位于同一节中(尽管Microsoft linker将其放在同一节中,但不知道GNU或其他内容)

另一个简单但仅适用于系统DLL的方法是执行“热补丁”正在执行的操作,并重用每个函数前面设置为“nop”的15个字节以及“mov edi,edi”指令。所有随Windows发布的DLL都是这种情况,以支持热修补

可靠但困难的选择是按照@David Heffeman的建议去做。这种技术被称为“landing function”,将前12个字节复制到landing函数中,然后将jmp复制到原始函数中


简单可靠的选择是使用MS Detour。MicrosoftDetour是MicrosoftResearch的一个产品,它正是这样做的,工作非常好,而且它是受支持的,它处理了大量可能出现的死角情况和竞争条件(以及其他东西),它的x86版本是开源的。缺点是商业用途非常昂贵——上次我检查它是10k。

为什么不使用蹦床呢?这听起来像是一个rootkit——但我不是说它很贵。@Linuxios:希望不是:)它确实如此。64位驱动程序必须经过签名才能安装到用户的计算机上。获取证书的唯一方法是将驱动程序提交给Microsoft进行验证。在不太可能的情况下,他们不会拒绝该驱动程序,那么PatchGuard很有可能会阻止这种情况的发生。@HansPassant:您只需要一份有效的Verisign证书就可以为该驱动程序签名。无需向Microsoft提交驱动程序。