2010-07-12 113 views
0

我的目标是在概念上很简单:我想设置一个GetMessage全局钩子函数,它利用共享文件句柄。问题出现是因为根据我的理解,包含挂钩函数的DLL会为每个进程加载多次,每个进程都有自己的“地址空间”。出于这个原因,我导致我不能简单地处理DllMain的DLL_PROCESS_ATTACH来创建所需的文件,因为将使用不同的句柄创建多个文件。全局钩子DLL的应用程序

一个引起我注意的解决方案是命名管道。基本上,应用程序将充当服务器端;它会创建文件一次,然后将文件句柄提供给DLL客户端,因此每个全局钩子将使用相同的文件。

我似乎无法让它从我收集的代码工作。在应用中,我创建了文件,设置全局钩子函数,然后让它通过这个循环:

while(1) 
{ 
    HANDLE hPipe = CreateNamedPipe("\\\\.\\pipe\\pipename", PIPE_ACCESS_OUTBOUND, 
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 32, 32, 5000, NULL); 
    if(hPipe == INVALID_HANDLE_VALUE) 
    return 42; 
    if(!ConnectNamedPipe(hPipe, NULL)) 
    return 43; 
    DWORD dwWritten; 
    WriteFile(hPipe, logFile, sizeof(logFile), &dwWritten, NULL); 
    FlushFileBuffers(hPipe); 
    DisconnectNamedPipe(hPipe); 
    CloseHandle(hPipe); 
} 

然后我处理的DllMain的DLL_PROCESS_ATTACH像这样:

case DLL_PROCESS_ATTACH: 
{ 
    HANDLE hPipe; 
    while(1) 
    { 
    hPipe = CreateFile("\\\\.\\pipe\\pipename", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); 
    if(hPipe != INVALID_HANDLE_VALUE) 
     break; 
    WaitNamedPipe("\\\\.\\pipe\\pipename", NMPWAIT_USE_DEFAULT_WAIT); 
    } 
    DWORD dwRead; 
    ReadFile(hPipe, logFile, sizeof(logFile), &dwRead, NULL); 
    CloseHandle(hPipe); 
    break; 
} 

简单地说,它不起作用,我似乎无法弄清楚为什么。有什么我失踪或在我的代码中做错了吗?

另一个我无法弄清楚的问题是,应用程序被困在一个持续服务的无限循环中。我想设置一个事件,DLL将在某些情况下设置并导致主应用程序解除全局钩子,关闭文件并退出,但ConnectNamedPipe是一个阻塞函数。什么是确定什么时候所有客户端都被服务的方法,以便服务环路可以中断?

感谢您的任何帮助。

回答

1

对于您在DLL_PROCESS_ATTACHDLL_THREAD_ATTACH期间可能调用的系统API有很强的限制。从MSDN documentation.

的入口点函数应该 只进行简单的初始化或终止 任务。它不能调用 函数的LoadLibrary或LoadLibraryEx 函数(或者调用 这些函数的函数),因为这可能会在 DLL的 加载顺序中创建依赖关系循环。这可能导致在系统有 执行其初始化代码之前使用DLL 。 类似地,入口点函数 不能调用FreeLibrary函数 (或调用FreeLibrary则函数)过程终止期间 ,因为 这可能导致一个DLL使用 系统已经执行其 终止代码之后。

因为Kernel32.dll中是保证在 进程地址空间 被加载时 调用,调用函数中 Kernel32.dll中不会导致 DLL其 初始化之前所使用的入口点函数代码已执行。 因此,入口点函数 可以调用不加载其他DLL的Kernel32.dll 中的函数。例如,对于 示例,DllMain可以创建 同步对象,例如 关键部分和互斥锁,并使用 TLS。不幸的是,在Kernel32.dll中没有 安全功能 的完整列表。

的Windows 2000:不要在DllMain中 创建一个名为 同步对象,因为系统会再加载 额外的DLL。

调用函数 需要Kernel32.dll以外的其他DLL 可能会导致难以诊断的问题 。例如, 调用用户,外壳和COM函数 可能导致访问冲突错误, ,因为某些功能加载其他 系统组件。相反,在 终止期间调用 等功能终止可能导致访问冲突 错误,因为相应的 组件可能已经被 卸载或未初始化。

对于实验级工作,请考虑使用线程附加事件来查看“发生了什么”。对于生产工作,您需要一个完全修改的方法,它不会在DllMain中进行繁重的工作。您可以在上面看到,未来的历史将在此操作系统设施中包含更多的错误。

+0

你有任何建议,有什么其他方法我可以使用,不涉及的DllMain? – kaykun 2010-07-12 01:39:18

+0

我不知道你想通过命名管道发送的消息来完成什么。你有什么更大的目标呢? – 2010-07-12 02:12:35

+0

主应用程序使用SetWindowsHookEx创建一个文件并设置一个GetMessage全局钩子。每个进程(或线程,我真的不知道哪个)被注入了DLL中的钩子函数。钩子函数应该写入在主应用程序中创建的文件。因此我需要一些方法将应用程序中的文件句柄传递给hook函数的所有实例。 – kaykun 2010-07-12 02:48:11

1

在我看来,你的主要问题可能是CreateNamedPipe函数(SECURITY_ATTRIBUTES)的最后一个参数或其他安全问题(见下文)。

我真的不明白你计划在logFile到wrire可以是不为32字节(16个WCHARs),它的实物资料。 sizeof()CreateNamedPipe中的用法也会好一点(也可以考虑64位操作系统)。你是否想要将一个进程中打开的日志文件的句柄发送到其他进程?如果你这样做,你应该使用像DuplicateHandle这样的函数(见http://msdn.microsoft.com/en-us/library/ms724251.aspx)。一般而言,与您发布的命名管道通信的代码示例我觉得不太好。我建议你首先调试命名的管道通信,而不是钩住DLL(至少有两个单独的客户端进程,它们可以在不同的用户凭证和创建管道的服务器进程下运行)。

有在Windows API的DLL_THREAD_ATTACH内的使用原因的限制,但Kernel32.dll中的你的情况似乎使用我的安全。

我不知道你想实现什么样的沟通方式,但一般的像完成例程或其他异步操作的使用模式,畅通的使用(见http://msdn.microsoft.com/en-us/library/aa365788.aspxhttp://msdn.microsoft.com/en-us/library/aa365788.aspxhttp://msdn.microsoft.com/en-us/library/aa365601.aspx)的DllMain内可能会更好。

另一个小建议:您应该在DLL_PROCESS_ATTACH的情况下使用DisableThreadLibraryCalls(),并尝试选择一个合理的DLL基址(链接器开关),这将减少在不同进程中加载​​DLL时的重定位。这些将加快您的编程并节省内存。