2010-07-12 111 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時的重定位。這些將加快您的編程並節省內存。