我对这个问题的解决方案是在初始挂起状态下创建进程,然后将
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);
#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
评论
我将其更改为所选答案,因为这可以回答我在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