遠程線程和遠程線程注入
CreateRemoteThread函數
作用:創建在另一個進程的虛擬地址空間中運行的線程
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 // 返回線程id
);
和我們 CreateThread 函數相比也就多了一個hProcess參數
遠程線程
#include<iostream>
#include<windows.h>// 參數:進程ID,進程內函數地址
BOOL myCreateRemoteThread(DWORD dwProcessId,DWORD dwProcAddr) {// 獲取進程句柄DWORD dwThreadId = 0;HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessId);if (hProcess == NULL) {std::cout << "打開進程失敗!,錯誤碼:" << GetLastError() << std::endl;return FALSE;}// 創建遠程線程HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)dwProcAddr, NULL, 0, NULL);if (hThread == NULL) {std::cout << "創建遠程線程失敗!,錯誤碼:" << GetLastError() << std::endl;return FALSE;}getchar();// 釋放資源CloseHandle(hProcess);CloseHandle(hThread);return TRUE;
}int main()
{ 獲取窗口句柄//HWND hWind = FindWindow(NULL, L"進程A.exe");//DWORD pid; 通過窗口句柄獲取進程ID和線程id//GetWindowThreadProcessId(hWind,&pid);// 我們先使用硬編碼的方式來測試 myCreateRemoteThread(10872, 0x00007FF76ECD2220);return 0;
}
現在我們可以在其它進程中創建一個新的線程,然后執行它里面的方法
遠程線程注入
那如果我們要在其他進程中執行自己的代碼要怎么辦呢?
這就是用到注入的技術了,所謂注入就是在第三方進程不知道或者不允許的情況下將模塊或者代碼寫入對方進程空間,并設法執行的技術
常見的注入方式:
- 遠程線程注入
- APC注入
- 消息鉤子注入
- 注冊表注入(已淘汰 需要早期系統版本支撐)
- 導入表注入
- 輸入法注入
- …
在CreateRemoteThread方法中,有一個參數lpStartAddress,這個參數是一個返回類型為4/8個字節,接收的參數類型也是4/8個字節的方法(字節數與當前是多少位的系統相關)
DWORD WINAPI ThreadProc(LPVOID lpParameter
);
還有一個函數和ThreadProc很像,也是只有一個參數(4個字節),返回類型也是4個字節,那就是LoadLibrary這個函數
HMODULE LoadLibrary(LPCSTR lpLibFileName
);
這樣以來我們就可以把LoadLibrary函數替換成ThreadProc函數去執行,我們回想一下,執LoadLibrary需要有一個參數,這個參數就是我們dll的路徑,但是這個dll路徑是不能放在我們當前進程的,進程之前的數據都隔離的,所以具體的實現步驟為以下幾點
- 在要注入的進程中分配空間,
存儲dll的路徑
- 獲取
LoadLibrary
的函數地址 - 創建遠程線程,執行
LoadLibrary
函數
接下來我們來看一下具體的實現流程
DLL注入模塊的編寫
// dllmain.cpp : 定義 DLL 應用程序的入口點。
#include "pch.h"
extern "C" __declspec(dllimport) void __stdcall MyPrintWindow(); // pch.hDWORD WINAPI ThreadProc(LPVOID lpParameter) { for (;;){Sleep(1000);std::cout << "注入的代碼在執行。。。\n";}return 0;
}BOOL APIENTRY DllMain( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);break;case DLL_THREAD_ATTACH:break;case DLL_THREAD_DETACH:break;case DLL_PROCESS_DETACH:break;}return TRUE;
}
實現注入功能的代碼
#include<iostream>
#include<windows.h>
#include <tlhelp32.h>// 兩個參數,processId要注入的進程ID,dllAddress dll的地址
BOOL LoadDLL(DWORD dwProcessId,const WCHAR* pDllAddressStr) {BOOL bRet = FALSE;HANDLE hProcess = NULL;HANDLE hThread;DWORD dwLength;DWORD64 dwLoadAddr = NULL;LPVOID lpAllocAddr;DWORD dwThreadId;HMODULE hModule;// 1.獲取進程句柄hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);if (hProcess == NULL) {std::cout << "打開進程失敗!,錯誤碼:" << GetLastError() << std::endl;return FALSE;}// 2.計算Dll路徑名稱長度dwLength = (lstrlen(pDllAddressStr) + 1) * sizeof(WCHAR);// 3.在目標進程中申請內存 參數1,目標進程句柄,參數2,內存起始地址,參數3,內存大小,參數4,內存屬性已提交,參數5,具有讀寫權限lpAllocAddr = VirtualAllocEx(hProcess, NULL, dwLength, MEM_COMMIT, PAGE_READWRITE);if (lpAllocAddr == NULL) {std::cout << "分配置內存失敗!,錯誤碼:" << GetLastError() << std::endl;CloseHandle(hProcess);return FALSE;}// 4.把Dll路徑名稱復制到剛剛分配的目標進程內存當中bRet = WriteProcessMemory(hProcess, lpAllocAddr, pDllAddressStr, dwLength, NULL);if (!lpAllocAddr) {std::cout << "寫目標進程內存失敗!,錯誤碼:" << GetLastError() << std::endl;CloseHandle(hProcess);return FALSE;}// 5.獲取模塊地址hModule = GetModuleHandle(L"Kernel32.dll");if (!hModule) {std::cout << "獲取模塊失敗!,錯誤碼:" << GetLastError() << std::endl;CloseHandle(hProcess);return FALSE;}// 6.獲取LoadLibraryW函數地址dwLoadAddr = (DWORD64)GetProcAddress(hModule, "LoadLibraryW");if (!dwLoadAddr) {std::cout << "函數地址失敗!,錯誤碼:" << GetLastError() << std::endl;CloseHandle(hProcess);CloseHandle(hModule);return FALSE;}// 7.創建遠程線程,加載dllhThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)dwLoadAddr, lpAllocAddr, 0, NULL);if (hThread == NULL) {std::cout << "加載dll失敗!,錯誤碼:" << GetLastError() << std::endl;CloseHandle(hProcess);CloseHandle(hModule);return FALSE;}CloseHandle(hThread);return TRUE;}// 遍歷進程id
DWORD FindConsoleExePid(const WCHAR* exeName) {HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (hSnap != INVALID_HANDLE_VALUE){setlocale(LC_ALL, "chs");PROCESSENTRY32 pe32 = { 0 };pe32.dwSize = sizeof(pe32);BOOL bRet = Process32First(hSnap, &pe32);while (bRet){//wprintf(L"%s %d\r\n", pe32.szExeFile, pe32.th32ProcessID);if (lstrcmp(pe32.szExeFile, exeName) == 0){// 返回pidreturn pe32.th32ParentProcessID;}bRet = Process32Next(hSnap, &pe32);}CloseHandle(hSnap);}return 0;
}int main()
{DWORD dwProcessId = FindConsoleExePid(L"遠程線程");//注入dllif (LoadDLL(dwProcessId, L"C:\\Users\\BananaLi\\Desktop\\注入DLL.dll")) {std::cout << "注入成功\n";}else{std::cout << "注入失敗\n";}return 0;}
我們來一步步的分析下代碼
// 1.獲取進程句柄
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
首先要知道要往哪個進程中注入,就要獲取它的句柄
// 2.計算Dll路徑名稱長度
dwLength = (lstrlen(pDllAddressStr) + 1) * sizeof(WCHAR);
我們要把LoadLibraryW的參數也就是dll的路徑地址傳遞進去,要計算字符串的長度,后面好分配空間
// 3.在目標進程中申請內存 參數1,目標進程句柄,參數2,內存起始地址,參數3,內存大小,參數4,內存屬性已提交,參數5,具有讀寫權限
lpAllocAddr = VirtualAllocEx(hProcess, NULL, dwLength, MEM_COMMIT, PAGE_READWRITE);
我們要在目標進程中開辟一塊空間用來存入我們的dll地址:0x0x0000014922cd0000
// 4.把Dll路徑名稱復制到剛剛分配的目標進程內存當中
bRet = WriteProcessMemory(hProcess, lpAllocAddr, pDllAddressStr, dwLength, NULL);
運行后我們發現我們dll的路徑已經寫入到了目標進程的空間
// 5.獲取模塊地址
hModule = GetModuleHandle(L"Kernel32.dll");
// 6.獲取LoadLibraryW函數地址
dwLoadAddr = (DWORD64)GetProcAddress(hModule, “LoadLibraryW”);
我們會發現不管在任何程序當中都會有ntdll.dll,kernel32.dll這些模塊,并且他們的模塊地址和函數地址在任何進程中都是固定的,然而我們的LoadLibraryW就在kernel32.dll這個模塊當中
// 7.創建遠程線程,加載dll
最后就是CreateRemoteThread函數加載我們的LoadLibraryW函數,然后在加載LoadLibraryW函數時,創建一個新的線程執行我們需要執行的方法就可以了
查看模塊,我們dll也被識別出來了
最后還有一個函數是用來退出的
- FreeLibraryAndExitThread // 卸載自己并退出