老手請飄過,寫給新手看的低級東西
一、步驟
1、CreateThread
要搞懂遠程線程注入技術,首先應該用過CreateThread創建多線程。
如果對這個函數怎麼使用沒搞懂下面的內容就不用看了,如果對這個函數理解了那麼繼續走
2、如何加載一個DLL
要在目標進程注入一個DLL,也就是要將DLL加載到目標進程,我們平時在使用DLL時,是怎麼加載的?
通過LoadLibrary加載。那麼我們要將一個DLL注入目標進程,可以理解爲我們要在目標進程執行LoadLibrary("xxxdll")
3、如何在目標進程執行LoadLibrary
如果是在自己的進程,我們直接調用LoadLibrary就可以加載一個DLL,現在的問題是我們要在其它進程
執行LoadLibrary,這就要藉助創建遠程線程
4、如何創建遠程線程
調用下面這個函數,就可以創建遠程線程,我們看下msdn對這個函數的描述:
The CreateRemoteThread function creates a thread that runs in the virtual address space of another process.
HANDLE CreateRemoteThread(
HANDLE hProcess, // handle to process
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
SIZE_T dwStackSize, // initial stack size
LPTHREAD_START_ROUTINE lpStartAddress, // thread function
LPVOID lpParameter, // thread argument
DWORD dwCreationFlags, // creation option
LPDWORD lpThreadId // thread identifier
);
其它參數不多說了,如果不懂可以百度這個函數用法,說說第四個參數
LPTHREAD_START_ROUTINE lpStartAddress,其實這個參數和CreateThread的第三個參數一樣,LPTHREAD_START_ROUTINE原型如下:
DWORD WINAPI ThreadProc(
LPVOID lpParameter // thread data
);
說白了第四個參數就是要指定,線程跑起來時,要執行的代碼在哪裏,這就給我們注入dll帶來了機會啊,我們可以把目標進程中LoadLibrary
函數在內存中的地址作爲CreateRemoteThread的第四個參數,那麼線程運行時,就去執行LoadLibrary這個函數了。
補充下爲什麼可以把LoadLibrary這個函數的地址,作爲第四個參數,我們來看下LoadLibrary的原型:
HMODULE LoadLibrary(
LPCTSTR lpFileName // file name of module
);
發現沒,LoadLibrary在MSDN中給出的原型只少了個WINAPI,但是Windows所有的API默認都是WINAPI啊,(WINAPI表示調用約定)
返回值:DWORD 和 HMODULE 本質就是個整數
參數:LPVOID 和 LPCTSTR 都是個指針
這就是爲什麼可以把LoadLibrary作爲CreateRemoteThread函數的第三個參數,這樣我們就完美利用CreateRemoteThread創建一個
遠程線程,並執行LoadLibrary,去加載我們要注入的DLL了。
那要如何獲取目標進程中的LoadLibrary函數地址了,很簡單我們獲取自己進程中的LoadLibrary的地址即可,LoadLibrary屬於kernel32模塊
這個模塊在每個進程中加載的基地址都一樣,所以LoadLibrary在自己進程中的地址和在目標進程中的地址是一致的。
5 解決LoadLibrary的參數問題
在第四步中我們解決了如何跨進程調用LoadLibrary這個問題,現在的問題是,執行LoadLibrary,我們要給它一個參數,
這個參數用來指明DLL的路徑。我們可以利用CreateRemoteThread的第五個參數LPVOID lpParameter,來解決。
lpParameter這個參數在遠程線程執行時,會傳遞給LoadLibrary (LoadLibrary就是遠程線程),作爲LoadLibrary的第一參數。
但是這裏有一個問題,我們調用CreateRemoteThread時,是在自己的進程調用,那麼傳遞lpParameter這個參數時,不能使用自己進程
虛擬地址空間的地址,那要怎麼解決這個問題
6 VirtualAllocEx
在Step5中,碰到了新的問題,如何傳遞LoadLibrary的參數,因爲這個參數必須是目標進程中的一個虛擬地址。
我們需要用到VirtualAllocEx這個函數,這個函數可以在目標進程申請內存,我們通過調用這個函數,在目標
進程中申請一段內存,然後把DLL的路徑,寫到這段內存中,比如d:\xxx\xxoo.dll
這個函數的返回值,就是申請到的內存的起始地址。所以我們可以把返回值作爲CreateRemoteThread的第5個參數,這樣解決了給
LoadLibrary傳遞參數的問題。
7 其它一些細節問題看代碼註釋
二、注意事項
如果你要注入的目標進程是64位程序,那麼你的注入進程必須是64位的,而且要注入的DLL也必須是64位的,否則 CreateRemoteThread會 失敗,GetLastError返回錯誤代碼:5。寫一個完善的注入代碼要考慮的問題很多
三、部分關鍵代碼如下,代碼有參考這個博客:
https://www.cnblogs.com/BoyXiao/archive/2011/08/11/2134367.html
代碼網上一大把,關鍵是搞清楚思路,如果需要完整代碼可以聯繫QQ 844255657, 這裏沒有把所有代碼貼上來了,
因爲網上太多了。
int _tmain(int argc, _TCHAR* argv[])
{
if (!AdjustProcessTokenPrivilege()){
MyOutputDebugString("提權失敗\n");
}
DWORD dwPid = GetPidByName("explorer.exe");
if (dwPid == 0){
MyOutputDebugString("GetPidByName failed\n");
return -1;
}
MyOutputDebugString("explorer.exe pid=%d\n", dwPid);
HANDLE hProcess;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (hProcess == NULL)
{
MyOutputDebugString("打開進程失敗!!!!");
return -1;
}
//1.在遠程進程中分配內存
LPVOID pszRemoteBuffer = (char *)VirtualAllocEx(hProcess, NULL, MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
if (pszRemoteBuffer == NULL)
{
MyOutputDebugString("申請遠程空間失敗");
return -1;
}
//2.在遠程申請的內存空間中寫入DLL的路徑,後面這塊數據當做遠程線程的參數
SIZE_T dwWriten = 0;
const char * dllPath = "D:\\VS\\DllToInject\\x64\\Debug\\UtilDll.dll";
if (!WriteProcessMemory(hProcess, pszRemoteBuffer, (LPVOID)dllPath, strlen(dllPath) + 1, &dwWriten))
{
MyOutputDebugString("寫入內存失敗");
return -1;
}
//3.獲取遠程進程中LoadLibry的地址, 這裏使用當前進程Kernel32模塊的基地址當做遠程進程中的kernel32的基地址,這不是巧合
//kernel32在每個進程中的基地址都一樣,可能是操作系統爲進程加載kernel32時,直接將已經加載了的kernel32映射到需要這個模塊的進程中
//pfnLoadLibrary 就是遠程線程, 所以遠程線程執行就是在對方進程調用LoadLibrary,
//如果是Unicode程序,GetProcAddress需要使用LoadLibraryW
HMODULE hMouDle = GetModuleHandle("Kernel32");
PTHREAD_START_ROUTINE pfnLoadLibrary = (PTHREAD_START_ROUTINE)GetProcAddress(hMouDle, "LoadLibraryA");
if (pfnLoadLibrary == NULL)
{
MyOutputDebugString("獲取LoadLibrary地址失敗!!!");
return -1;
}
//4.創建遠程線程
DWORD dwThreadId = 0;
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, pfnLoadLibrary, pszRemoteBuffer, 0, &dwThreadId);
if (hThread == NULL)
{
MyOutputDebugString("創建遠程線程失敗 %d\n", GetLastError());
return -1;
}
//5 等待遠程線程退出
DWORD exitCode = 0;
DWORD dwRet = 0;
WaitForSingleObject(hThread, INFINITE);
dwRet = GetExitCodeThread(hThread, &exitCode);
//6 檢查退出碼,如果退出碼等於0,說明LoadLibrary失敗了
if (dwRet == 0){
MyOutputDebugString("GetExitCodeThread failed\n");
return -1;
}
if (exitCode == 0){
MyOutputDebugString("LoadLibrary %s failed\n", dllPath);
}
VirtualFreeEx(hProcess, pszRemoteBuffer, MAX_PATH, MEM_DECOMMIT);
::CloseHandle(hThread);
::CloseHandle(hProcess);
getchar();
//6 釋放注入的DLL, 需要再次創建遠程線程執行FreeLibrary
return 0;
}