![](/img/trans.png)
[英]FreeLibraryAndExitThread crashes program when unloading injected DLL
[英]Access violation when unloading injected DLL via CreateRemoteThread
我正在嘗試將 DLL 干凈地注入前台窗口的進程,從此 DLL 調用簡單函數,然后干凈地卸載 DLL。 我的注入代碼:
HWND fgwnd = GetForegroundWindow();
DWORD cur_thread = GetCurrentThreadId();
DWORD fg_pid = 0;
DWORD fg_thread = GetWindowThreadProcessId(fgwnd, &fg_pid);
BOOL res = 0;
const char* inj_path = "C:\\Users\\pc\\source\\repos\\hothook\\x64\\Debug\\fground_injector.dll";
// Get process handle to victim
HANDLE victim = OpenProcess(PROCESS_ALL_ACCESS, FALSE, fg_pid);
// Find exact adress of LoadLibraryA function from text space of kernel32.dll loaded by the OS
// and used by victim
PVOID llib = (LPVOID)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
PVOID flib = (LPVOID)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "FreeLibrary");
// Allocate memory inside victim's address space
LPVOID inj_path_victim = (LPVOID)VirtualAllocEx(victim, NULL, strlen(inj_path)+1,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
// Write inject dll's adress into victim
SIZE_T written;
res = WriteProcessMemory(victim, inj_path_victim, inj_path, strlen(inj_path)+1, &written);
// Finally, inject DLL into victim!
// Spawn thread in remote process ================================================================
HANDLE inj_llib_thread = CreateRemoteThread(
/*I*/ victim, // Handle to process where thread will be created
/*I*/ NULL, // SECURITY_ATTRIBUTES for new thread
/*I*/ 0, // Initial stacks size, bytes. 0 -> default size
/*I*/ (LPTHREAD_START_ROUTINE)llib, // User defined callback LPTHREAD_START_ROUTINE
/*I*/ inj_path_victim, // Ptr to variable to be sent as func parameter
/*I*/ 0, // Creation control flags. 0 -> immediate start
/*O|O*/ NULL); // Ptr to variable that recieves thread ID
// ===============================================================================================
// CANNOT WAIT FOR THREAD IN OTHER PROCESS.... OR CAN I?! I CAN!
// Wait for DLL to get properly injected into victim
res = WaitForSingleObject(inj_llib_thread, INFINITE);
// Get executable base address of the loaded DLL
DWORD llib_exit;
res = GetExitCodeThread(inj_llib_thread, &llib_exit);
// Free previously allocated remote memory
res = VirtualFreeEx(victim, inj_path_victim, 0, MEM_RELEASE);
// Call injected DLL's function
HMODULE fg_inj = LoadLibraryA(inj_path);
PVOID inj_t_proc = (LPVOID)GetProcAddress(fg_inj, "injectThread");
PVOID ulib = NULL;
// Spawn thread in remote process ================================================================
HANDLE inj_thread = CreateRemoteThread(
/*I*/ victim, // Handle to process where thread will be created
/*I*/ NULL, // SECURITY_ATTRIBUTES for new thread
/*I*/ 0, // Initial stacks size, bytes. 0 -> default size
/*I*/ (LPTHREAD_START_ROUTINE)inj_t_proc, // User defined callback LPTHREAD_START_ROUTINE
/*I*/ NULL, // Ptr to variable to be sent as func parameter
/*I*/ 0, // Creation control flags. 0 -> immediate start
/*O|O*/ NULL); // Ptr to variable that recieves thread ID
// ===============================================================================================
// Wait before injected DLL's thread finishes before extraction
res = WaitForSingleObject(inj_thread, INFINITE);
// Extract injected DLL from victim
// Spawn thread in remote process ================================================================
HANDLE inj_flib_thread = CreateRemoteThread(
/*I*/ victim, // Handle to process where thread will be created
/*I*/ NULL, // SECURITY_ATTRIBUTES for new thread
/*I*/ 0, // Initial stacks size, bytes. 0 -> default size
/*I*/ (LPTHREAD_START_ROUTINE)flib, // User defined callback LPTHREAD_START_ROUTINE
/*I*/ (LPVOID)llib_exit, // Ptr to variable to be sent as func parameter
/*I*/ 0, // Creation control flags. 0 -> immediate start
/*O|O*/ NULL); // Ptr to variable that recieves thread ID
// ===============================================================================================
// Wait untill injected DLL is fully extracted from the victim
res = WaitForSingleObject(inj_flib_thread, INFINITE);
DWORD flib_exit;
res = GetExitCodeThread(inj_flib_thread, &flib_exit);
// Extract injection DLL from host
FreeLibrary(fg_inj);
// ^^^ This actually does not unload DLL from host app, even if I call it 100 times in a loop...
// Clean up by closing all utilised handles
CloseHandle(victim);
CloseHandle(inj_llib_thread);
CloseHandle(inj_thread);
CloseHandle(inj_flib_thread);
我的注入 DLL 真的很簡單:
BOOL APIENTRY DllMain(HMODULE hm,
DWORD call_reas,
LPVOID reserved)
{
switch(call_reas)
{
case DLL_PROCESS_ATTACH:
{
DisableThreadLibraryCalls(hm);
// I thought that threads calling DllMain with case 2-3 cause crash
// But no, same crash with or without this call
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
DWORD injectThread(LPVOID p)
{
//AllowSetForegroundWindow(ASFW_ANY);
// This is what it originally suppose to do
// Let the host "steal" foreground window status from victim
// But even commenting this out results in the same crash
return 0;
}
崩潰發生在我創建FreeLibrary
遠程線程以卸載注入的 DLL 之后。 這是我能得到的盡可能多的崩潰細節,因為它似乎發生在系統內核中:
Exception thrown at 0x00007FFE598C7170 in maudswch.exe: 0xC0000005: Access violation executing location 0x00007FFE598C7170.
00007ffe598c7170()
kernel32.dll!00007ffe9ac47034()
ntdll.dll!00007ffe9c682651()
Not Flagged 50864 0 Main Thread Main Thread win32u.dll!00007ffe99e51104
Not Flagged 47548 0 Worker Thread ntdll.dll thread ntdll.dll!00007ffe9c6d0794
Not Flagged 41096 0 Worker Thread ntdll.dll thread ntdll.dll!00007ffe9c6d0794
Not Flagged 43456 0 Worker Thread ntdll.dll thread ntdll.dll!00007ffe9c6d0794
Not Flagged > 29820 0 Worker Thread Win64 Thread 00007ffe598c7170
我對許多“受害者”進行了測試。 CMD、記事本、我的其他自定義 GUI 程序。 結果是一樣的。 DLL 被成功注入,DLL 函數被執行,但在卸載它時會崩潰“受害者”。 更詳細的一點是, FreeLibrary
遠程線程需要很長時間才能完成,大約 1.5 秒有時甚至 7 秒!
我試圖解決這個難題好幾天了,我搜索了所有的互聯網,但所有其他類似的案例似乎都無關緊要......此時我不知道發生了什么以及如何解決這個問題。 當然,我可以咬緊牙關,永遠不要嘗試卸載我注入的 DLL,畢竟它可以工作。 但我不想在完成工作后將無用的 DLL 留在其他進程中。
編輯:一些調試打印。 如您所見, hm:
和func addr:
在DllMain
PROCESS_ATTACH
中輸出。 如您所見,我很幸運在主機和受害者進程中擁有完全相同的基本 DLL 地址和函數地址。 但是,將完整的基地址傳遞給FreeLibrary
外部線程會導致完全相同的崩潰,因此截斷的基地址並不是崩潰背后的真正原因......
僅當受害者是 32 位進程時,使用GetExitCodeThread()
檢索目標進程中LoadLibraryA()
返回的HMODULE
才有效。 否則, HMODULE
將太大而無法放入遠程線程的退出代碼中,因此您將不得不使用不同的機制來獲取目標進程中 DLL 的基地址,例如使用EnumProcessModules()
+ GetModuleFileNameEx()
或CreateToolhelp32Snapshot()
+ Module32(First|Next)()
,甚至注入調用LoadLibraryA()
並將HMODULE
保存到注入器分配並可以通過ReadProcessMemory()
讀取的變量中的存根代碼。
更重要的是,您在目標進程中調用 DLL 的injectThread()
函數的方式是錯誤的。 您正在使用LoadLibraryA()
+ GetProcAddress()
來獲取指向注入器進程中的injectThread()
函數的指針,然后假設該函數位於目標進程中的同一地址。 但是目標進程中的 DLL 可能已加載到與注入器進程中的 DLL不同的基地址(尤其是考慮到ALSR 、DLL 變基等技術)。
正確的解決方案是獲取 DLL 中的injectThread()
函數的偏移量(注入器可以通過從函數地址中減去其 DLL 的基地址來計算),然后將該偏移量添加到目標中 DLL 的基地址過程。
此外,請確保您的injectThread()
函數具有CreateRemoteThread()
期望的正確簽名。 您顯示的內容缺少指定的調用約定,因此它將使用編譯器的默認約定,通常是__cdecl
。 但是CreateRemoteThread()
需要__stdcall
代替。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.