DLL注入——使用远程线程
1.簡介
從根本上來說,DLL注入技術要求目標進程中的一個線程調用LoadLibrary來載入我們想要的DLL。由于我們不能輕易地控制別人進程中的線程,因此這種方法要求我們在目標進程中創建一個新的線程,Windows提供了創建遠程線程的函數。
HANDLE CreateRemoteThread([in] HANDLE hProcess,[in] LPSECURITY_ATTRIBUTES lpThreadAttributes,[in] SIZE_T dwStackSize,[in] LPTHREAD_START_ROUTINE lpStartAddress,[in] LPVOID lpParameter,[in] DWORD dwCreationFlags,[out] LPDWORD lpThreadId );- hProcess:用于創建線程的進程的句柄。
- lpThreadAttributes:指向SECURITY_ATTRIBUTES結構的指針,該結構確定返回的句柄是否可以被子進程繼承。
- dwStackSize:堆棧的初始大小,以字節為單位。如果此參數為零,則新線程使用默認大小。
- lpStartAddress:指向線程要執行的應用程序定義函數的指針。
- lpParameter:指向要傳遞給線程的變量的指針。
- dwCreationFlags:控制線程創建的標志。
- lpThreadId:指向接收線程標識符的變量的指針。如果該參數為NULL,則不返回線程標識符。
2.步驟
- 用VirtualAllocEx函數在遠程進程的地址空間中分配一塊內存。
- 用WriteProcessMemory函數把DLL的路徑名復制到第一步分配的內存中。
- 用GetProcAddress函數來得到LoadLibraryW或LoadLibraryA函數的實際地址(在Kernel32.dll中)
- 用CreateRemoteThread函數在遠程進程中創建一個線程,讓新線程調用正確的LoadLibrary函數并在參數中傳入第一步分配的內存地址。這時,DLL已經被注入到遠程進程的地址空間中,DLL的DllMain函數會收到DLL_PROCESS_ATTACH通知并且執行我們想要執行的代碼。
- VirtualFreeEx來釋放第1步分配的內存。
- 用GetProcAddress函數來得到FreeLibrary函數的實際地址(在Kernel32.dll中)
- 用CreateRemoteThread函數在遠程進程中創建一個線程,讓新線程調用FreeLibrary函數并在參數中傳入遠程DLL的HMODULE。
步驟1、2、3原理簡介:
字符串“D://remoteDll.dll”,位于調用進程的空間地址中,我們把這個地址傳給新創建的遠程線程,遠程線程再把它傳給LoadLibrary,當LoadLibrary去訪問這個內存地址的時候,DLL的路徑字符串并不在那里,遠程進程的線程就很可能引發訪問違規。為了解決這個問題,我們需要把DLL的字符串存放到遠程進程的地址空間中去,所以會使用VirtualAllocEx和WriteProcessMemory方法。
3.示例
3.1先做一個動態庫remoteDll.dll
此庫的功能是,一旦被注入到進程的地址空間中,就報告該進程正在使用的所有DLL,并將打印信息寫入到本地文件D://out.txt中。
const char* filepath = "D://out.txt";BOOL APIENTRY DllMain( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved) {switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:{FILE* fp = freopen(filepath, "w", stdout);PBYTE pb = NULL;MEMORY_BASIC_INFORMATION mbi;while (VirtualQuery(pb, &mbi, sizeof(mbi)) == sizeof(mbi)){int nlen;char szModName[MAX_PATH];if (mbi.State == MEM_FREE)mbi.AllocationBase = mbi.BaseAddress;if ((mbi.AllocationBase == hModule) || (mbi.AllocationBase != mbi.BaseAddress) || (mbi.AllocationBase == NULL))nlen = 0;else{nlen = GetModuleFileNameA((HINSTANCE)mbi.AllocationBase, szModName, _countof(szModName));}if (nlen > 0){char szBuf[MAX_PATH] = { 0 };wsprintfA(szBuf, "\n%p-%s", mbi.AllocationBase, szModName);printf("%s\n", szBuf);}pb += mbi.RegionSize;} fclose(fp);}break;case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE; }3.2寫一個測試程序
新建一個帶界面的程序
改了一下窗口界面,這里加了一個菜單項注入。
點擊注入出現彈窗,如下圖所示。
輸入進程ID,點擊確定即可,下面是注入的回調函數。
// “注入”框的消息處理程序。 INT_PTR CALLBACK Inject(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {UNREFERENCED_PARAMETER(lParam);switch (message){case WM_INITDIALOG:{return (INT_PTR)TRUE;}case WM_COMMAND:if (LOWORD(wParam) == IDOK){//獲取ID值DWORD processID = GetDlgItemInt(hDlg, IDC_EDIT1, NULL, FALSE);if (processID == 0){processID = GetCurrentProcessId();}if (InjectLib(processID, TEXT("D://remoteDll.dll"))){printf("inject ok");EjectLib(processID, TEXT("D://remoteDll.dll"));}else{printf("inject failed");}EndDialog(hDlg, LOWORD(wParam));return (INT_PTR)TRUE;}else if (LOWORD(wParam) == IDCANCEL){EndDialog(hDlg, LOWORD(wParam));return (INT_PTR)TRUE;}break;}return (INT_PTR)FALSE; }InjectLib函數如下:
BOOL WINAPI InjectLib(DWORD processID, PCWSTR pszLibFile) {BOOL bOk = FALSE;HANDLE hProcess = NULL, hThread = NULL;PWSTR pszLibFileRemote = NULL;__try{hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD |PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, processID);if (hProcess == NULL)__leave;int cch = 1 + lstrlenW(pszLibFile);int cb = cch * sizeof(wchar_t);pszLibFileRemote = (PWSTR)VirtualAllocEx(hProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);if (pszLibFileRemote == NULL)__leave;if (!WriteProcessMemory(hProcess, pszLibFileRemote, (PVOID)pszLibFile, cb, NULL))__leave;FARPROC pfnThreadRtn = GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");if (pfnThreadRtn == NULL)__leave;hThread = CreateRemoteThread(hProcess, NULL, 0, (PTHREAD_START_ROUTINE)pfnThreadRtn, pszLibFileRemote, 0, NULL);if (hThread == NULL)__leave;WaitForSingleObject(hThread, INFINITE);bOk = TRUE;}__finally{if (pszLibFileRemote != NULL)VirtualFreeEx(hProcess, pszLibFileRemote, 0, MEM_RELEASE);if (hThread != NULL)CloseHandle(hThread);if (hProcess != NULL)CloseHandle(hProcess);}return bOk; }EjectLib函數如下:
BOOL WINAPI EjectLib(DWORD processID, PCWSTR pszLibFile) {BOOL bOk = FALSE;HANDLE hthSnapshot = NULL;HANDLE hProcess = NULL, hThread = NULL;PWSTR pszLibFileRemote = NULL;__try{//獲取指定進程的快照,以及這些進程使用的堆、模塊和線程。hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processID);if (hthSnapshot == INVALID_HANDLE_VALUE)__leave;MODULEENTRY32W me = { sizeof(me) };BOOL bFound = FALSE;BOOL bMoreMods = Module32FirstW(hthSnapshot, &me); //檢索與進程關聯的第一個模塊的信息。for (; bMoreMods; bMoreMods = Module32NextW(hthSnapshot, &me)) //檢索與進程或線程關聯的下一個模塊的信息{bFound = (_wcsicmp(me.szModule, pszLibFile) == 0) || (_wcsicmp(me.szExePath, pszLibFile));if (bFound)break;}if (!bFound)__leave;hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD |PROCESS_VM_OPERATION, FALSE, processID);if (hProcess == NULL)__leave;FARPROC pfnThreadRtn = GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "FreeLibrary");if (pfnThreadRtn == NULL)__leave;//me.modBaseAddr 在所屬進程的上下文中模塊的基地址。hThread = CreateRemoteThread(hProcess, NULL, 0, (PTHREAD_START_ROUTINE)pfnThreadRtn, me.modBaseAddr, 0, NULL);if (hThread == NULL)__leave;WaitForSingleObject(hThread, INFINITE);bOk = TRUE;}__finally{if (hthSnapshot != NULL)CloseHandle(hthSnapshot);if (hThread != NULL)CloseHandle(hThread);if (hProcess != NULL)CloseHandle(hProcess);}return bOk; }3.3執行程序
打開一個記事本程序,找到PID,如下圖所示,PID = 20752。
運行3.2中的程序,輸入這個PID,點擊確定注入成功。
3.4運行結果
注入之后,在本地文件out.txt中,可以看見正在該記事本程序正在使用的DLL。
總結
以上是生活随笔為你收集整理的DLL注入——使用远程线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: lingo入门教程之三 --- 文件数据
- 下一篇: 京东万能转链API接口 含商品信息优惠券