我编写了一个C API,通过覆盖导出函数的相应IAT条目来支持静态导入挂钩。它适用于较旧的简单应用程序,但对于较现代的应用程序,效果较差。这主要是由于当今有大量动态导入功能的应用程序。

我对这个问题的解决方案是在初始挂起状态下创建进程,然后将LoadLibrary(Ex)(A/W)函数家族与GetProcAddress()挂钩,以用我自己的函数替换检索到的目标函数的地址。该解决方案之所以受到局限,部分原因在于它仅基于应用程序导入可执行模块中的动态功能和库,而无法处理以后创建的进程(尽管可以通过挂接CreateProcess()来解决),或者更重要的是,无法处理应用程序中的DLL,这些DLL也调用我要挂接的目标函数。显然,这是因为DLL在其自己的PE中与可执行模块具有单独的导入部分。

这带给我一个问题,我该如何挂钩DLL的入口点?我想实现该方法,因为我需要将DLL绑定到它静态导入的库,因此在DLL有机会在DLLMain中加载/导入它们之前,我可以将LoadLibrary / GetProcAddress函数挂钩。我假设有一种方法可以通过更改DLL的入口点,或通过挂钩处理kernel32.dll中调用DLL入口点的较低级函数来实现。

如果我要的方法有更好的选择,那么我能很高兴接受该解决方案,只要它能达到我想要的效果。

我的处理器是AMD Athlon II X2 250与x86-x64兼容,并且我的操作系统是Windows7。

#1 楼

要回答原始问题,您可以做的是将LdrpCallInitRoutine钩在ntdll.dll中。 DLL加载/卸载代码使用此函数来实际调用DLL入口点(DllMain)以及TLS回调。第一个参数是要调用的地址:

BOOLEAN NTAPI LdrpCallInitRoutine(PDLL_INIT_ROUTINE EntryPoint, PVOID BaseAddress, ULONG Reason, PVOID Context);


评论


我将其更改为所选答案,因为这可以回答我在OP中提出的问题。

–船长很明显
14年8月6日在19:33

问题-如何找到没有调试符号的LdrpCallInitRoutine?

–user541686
16-10-25在18:26

@Mehrdad:您可以在一个新问题中提出它;评论用于评论/澄清

–伊戈尔·斯科钦斯基♦
16-10-25在18:29

@IgorSkochinsky:是为了澄清。答案中建议使用此方法,但目前尚不清楚是否有可能。

–user541686
16-10-25在20:43



#2 楼

出于与上述相同的原因,您不应使用IAT重写挂钩方法。
将内联挂钩方法与对目标挂钩的JMP指令一起使用。您需要将覆盖的字节保存在某个地方,因为您需要将其用作蹦床。
将此作为内联钩子的示例。

使用内联钩子可确保在相同地址空间中加载的所有(现有或将来的)模块都将使用钩子函数,无论它们如何解析API地址。

jbremer.org具有有关API挂钩的丰富信息资源。

评论


我不会说内联挂钩确保将来所有的模块都将使用该功能,除非这些模块使用包含该挂钩的相同模块。

–船长很明显
2014年8月4日在1:45



@CaptainObvious当然。假设您内联钩子Kernel32.CreateFileA,然后调用其他函数(例如Gdi32.BitBlt)将永远不会触发该钩子。我的意思是,任何调用CreateFileA的模块都将使用我们的挂钩函数。

– 0xec
2014年8月4日在1:56



@CaptainObvious如果您不知道,则每个进程在其地址空间中都只有一个dll副本。这意味着无论使用LoadLibrary加载kernel32多少次,都只有CreateFileA函数的一个副本。在我们内联钩子的情况下,它是一劳永逸的。

– 0xec
2014年8月4日在2:28