簡體   English   中英

通過 CreateRemoteThread 卸載注入的 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM