當前位置:編程學習大全網 - 源碼下載 - dll被加載後線程的問題

dll被加載後線程的問題

現在不管下載什麽 ,總是DLL加載失敗嗎我該怎麽辦

把妳的代碼放到壹個DLL中;然後用 windows 鉤子把它映射到遠程進程。

2. 把妳的代碼放到壹個DLL中;然後用 CreateRemoteThread 和 LoadLibrary 把它映射到遠程進程。

3. 不用DLL,直接復制妳的代碼到遠程進程(使用WriteProcessMemory)並且用CreateRemoteThread執行之。在這裏有詳細的說明:

Ⅰ. Windows 鉤子

示例程序:HookSpy 和 HokInjEx

Windows鉤子的主要作用就是監視某個線程的消息流動。壹般可分為:

1. 局部鉤子,只監視妳自己進程中某個線程的消息流動。

2. 遠程鉤子,又可以分為:

a. 特定線程的,監視別的進程中某個線程的消息;

b. 系統級的,監視整個系統中正在運行的所有線程的消息。

如果被掛鉤(監視)的線程屬於別的進程(情況2a和2b),妳的鉤子過程(hook procedure)必須放在壹個動態連接庫(DLL)中。系統把這包含了鉤子過程的DLL映射到被掛鉤的線程的地址空間。Windows會映射整個DLL而不僅僅是妳的鉤子過程。這就是為什麽windows鉤子可以用來向其他線程的地址空間註入代碼的原因了。

在這裏我不想深入討論鉤子的問題(請看MSDN中對SetWindowsHookEx的說明),讓我再告訴妳兩個文檔中找不到的訣竅,可能會有用:

1. 當SetWindowHookEx調用成功後,系統會自動映射這個DLL到被掛鉤的線程,但並不是立即映射。因為所有的Windows鉤子都是基於消息的,直到壹個適當的事件發生後這個DLL才被映射。比如:

如果妳安裝了壹個監視所有未排隊的(nonqueued)的消息的鉤子(WH_CALLWNDPROC),只有壹個消息發送到被掛鉤線程(的某個窗口)後這個DLL才被映射。也就是說

::GetModuleFileName( hModule, lib_name, MAX_PATH );

::LoadLibrary( lib_name );

// 安全卸載鉤子

::UnhookWindowsHookEx( g_hHook );

}

return TRUE;

}

我們來看壹下。首先,我們用鉤子映射這個DLL到遠程線程,然後,在DLL被真正映射進去後,我們立即卸載掛鉤(unhook)。壹般來說當第壹個消息到達被掛鉤線程後,這DLL會被卸載,然而我們通過LoadLibrary來增加這個DLL的引用次數,避免了DLL被卸載。

剩下的問題是:使用完畢後如何卸載這個DLL?UnhookWindowsHookEx不行了,因為我們已經對那個線程取消掛鉤(unhook)了。妳可以這麽做:

○在妳想要卸載這個DLL之前再安裝壹個鉤;

○發送壹個“特殊”的消息到遠程線程;

○在妳的新鉤子的鉤子過程(hook procedure)中截獲該消息,調用FreeLibrary 和 (譯者註:對新鉤子調用)UnhookwindowsHookEx。

現在,鉤子只在映射DLL到遠程進程和從遠程進程卸載DLL時使用,對被掛鉤線程的性能沒有影響。也就是說,我們找到了壹種(相比第二部分討論的LoadLibrary技術)WinNT和Win9x下都可以使用的,不影響目的進程性能的DLL映射機制。

但是,我們應該在何種情況下使用該技巧呢?通常是在DLL需要在遠程進程中駐留較長時間(比如妳要子類[subclass]另壹個進程中的控件)並且妳不想過於幹涉目的進程時比較適合使用這種技巧。我在HookSpy中並沒有使用它,因為那個DLL只是短暫地註入壹段時間――只要能取得密碼就足夠了。我在另壹個例子HookInjEx中演示了這種方法。HookInjEx把壹個DLL映射進“explorer.exe”(當然,最後又從其中卸載),子類了其中的開始按鈕,更確切地說我是把開始按鈕的鼠標左右鍵點擊事件顛倒了壹下。

妳可以在本文章的開頭部分找到HookSpy和HookInjEx及其源代碼的下載包鏈接。

Ⅱ. CreateRemoteThread 和 LoadLibrary 技術

示例程序:LibSpy

中的線程過程的起始地址。

2. 如果把ThreadProc的lpParameter參數當做壹個普通的32位整數(FreeLibrary把它當做HMODULE)那麽沒有如何問題,但是如果把它當做壹個指針(LoadLibrary把它當做壹個char*),它就必須指向遠程進程中的內存數據。

第壹個問題其實已經迎刃而解了,因為LoadLibrary和FreeLibrary都是存在於kernel32.dll中的函數,而kernel32可以保證任何“正常”進程中都存在,且其加載地址都是壹樣的。(參看附錄A)於是LoadLibrary/FreeLibrary在任何進程中的地址都是壹樣的,這就保證了傳遞給遠程進程的指針是個有效的指針。

第二個問題也很簡單:把DLL的文件名(LodLibrary的參數)用WriteProcessMemory復制到遠程進程。

所以,使用CreateRemoteThread和LoadLibrary技術的步驟如下:

1. 得到遠程進程的HANDLE(使用OpenProcess)。

2. 在遠程進程中為DLL文件名分配內存(VirtualAllocEx)。

3. 把DLL的文件名(全路徑)寫到分配的內存中(WriteProcessMemory)

4. 使用CreateRemoteThread和LoadLibrary把妳的DLL映射近遠程進程。

5. 等待遠程線程結束(WaitForSingleObject),即等待LoadLibrary返回。也就是說當我們的DllMain(是以DLL_PROCESS_ATTACH為參數調用的)返回時遠程線程也就立即結束了。

6. 取回遠程線程的結束碼(GetExitCodeThtread),即LoadLibrary的返回值――我們DLL加載後的基地址(HMODULE)。

7. 釋放第2步分配的內存(VirtualFreeEx)。

8. 用CreateRemoteThread和FreeLibrary把DLL從遠程進程中卸載。調用時傳遞第6步取得的HMODULE給FreeLibrary(通過CreateRemoteThread的lpParameter參數)。

9. 等待線程的結束(WaitSingleObject)。

同時,別忘了在最後關閉所有的句柄:第4、8步得到的線程句柄,第1步得到的遠程進程句柄。

sp; MEM_COMMIT, PAGE_READWRITE );

::WriteProcessMemory( hProcess, pLibRemote, (void*)szLibPath,

sizeof(szLibPath), NULL );

// 加載 "LibSpy.dll" 到遠程進程

// (通過 CreateRemoteThread & LoadLibrary)

hThread = ::CreateRemoteThread( hProcess, NULL, 0,

(LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32,

nbsp; "LoadLibraryA" ),

pLibRemote, 0, NULL );

::WaitForSingleObject( hThread, INFINITE );

//取得DLL的基地址

::GetExitCodeThread( hThread, &hLibModule );

//掃尾工作

::CloseHandle( hThread );

::VirtualFreeEx( hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE );

我們放在DllMain中的真正要註入的代碼(比如為SendMessage)現在已經被執行了(由於DLL_PROCESS_ATTACH),所以現在可以把DLL從目的進程中卸載了。

// 從目標進程卸載LibSpu.dll

// (通過 CreateRemoteThread & FreeLibrary)

hThread = ::CreateRemoteThread( hProcess, NULL, 0,

rocess Communications部分)或其他資料中都有很好的說明。我在LibSpy中使用的是#pragma data_seg。

妳可以在本文章的開頭找到LibSpy及源代碼的下載鏈接。

Ⅲ.CreateRemoteThread和WriteProcessMemory技術

示例程序:WinSpy

另壹種註入代碼到其他進程地址空間的方法是使用WriteProcessMemory API。這次妳不用編寫壹個獨立的DLL而是直接復制妳的代碼到遠程進程(WriteProcessMemory)並用CreateRemoteThread執行之。

讓我們看壹下CreateRemoteThread的聲明:

HANDLE CreateRemoteThread(

HANDLE hProcess, // handle to process to create thread in

LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to security

&nb

p; // attributes

DWORD dwStackSize, // initial thread stack size, in bytes

LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread

// function

LPVOID lpPa

teThread啟動遠程的ThreadFunc。

7. 等待遠程線程的結束(WaitForSingleObject)。

8. 從遠程進程取回指執行結果(ReadProcessMemory 或 GetExitCodeThread)。

9. 釋放第2、4步分配的內存(VirtualFreeEx)。

10. 關閉第6、1步打開打開的句柄。

另外,編寫ThreadFunc時必須遵守以下規則:

1. ThreadFunc不能調用除kernel32.dll和user32.dll之外動態庫中的API函數。只有kernel32.dll和user32.dll(如果被加載)可以保證在本地和目的進程中的加載地址是壹樣的。(註意:user32並不壹定被所有的Win32進程加載!)參考附錄A。如果妳需要調用其他庫中的函數,在註入的代碼中使用LoadLibrary和GetProcessAddress強制加載。如果由於某種原因,妳需要的動態庫已經被映射進了目的進程,妳也可以使用GetMoudleHandle代替LoadLibrary。同樣,如果妳想在ThreadFunc中調用妳自己的函數,那麽就分別復制這些函數到遠程進程並通過INJDATA把地址提供給ThreadFunc。

2.

不要使用static字符串。把所有的字符串提供INJDATA傳遞。為什麽?編譯器會把所有的靜態字符串放在可執行文件的“.data”段,而僅僅在代碼中保留它們的引用(即指針)。這樣,遠程進程中的ThreadFunc就會執行不存在的內存數據(至少沒有在它自己的內存空間中)。

3. 去掉編譯器的/GZ編譯選項。這個選項是默認的(看附錄B)。

4. 要麽把ThreadFunc和AfterThreadFunc聲明為static,要麽關閉編譯器的“增量連接(incremental linking)”(看附錄C)。

5. ThreadFunc中的局部變量總大小必須小於4k字節(看附錄D)。註意,當degug編譯時,這4k中大約有10個字節會被事先占用。

6. 如果有多於3個switch分支的case語句,必須像下面這樣分割開,或用if-else if代替:

switch( expression ) {

case constant1: statement1; goto END;

case constant2: statement2; goto END;

case constant3: statement2; goto END;

}

switch( expression

的緩沖

返回值:

成功復制的字符數。

讓我們看以下它的部分代碼,特別是註入的數據和代碼。為了簡單起見,沒有包含支持Unicode的代碼。

INJDATA

typedef LRESULT (WINAPI *SENDMESSAGE)(HWND,UINT,WPARAM,LPARAM);

typedef struct {

HWND hwnd; // handle to edit control

SENDMESSAGE fnSendMessage; // pointer to user32!SendMessageA

char psText[

28]; // buffer that is to receive the password

} INJDATA;

  • 上一篇:成都UI設計培訓哪家好?
  • 下一篇:馬甲為什麽壹直去不了TI,實力其實還是最大的原因
  • copyright 2024編程學習大全網