引子
前些日子由于項目要求,在網(wǎng)上到處找資料,于無(wú)意中發(fā)現了 CodeProject 上的一篇很老的文章,文章標題為:
Three Ways to Inject Your Code into Another Process
這篇文章呢,出來(lái)很久咯,還是 03 年的文章了,可惜我弄底層弄得時(shí)間不久哦,不然應該早就看過(guò)這篇大作了,
由于是大作,而且出來(lái)的又久了,自然在網(wǎng)上也就到處流傳咯,
所以也有人將這篇文章翻譯成了中文版的,下面給出這篇大作的兩個(gè)鏈接:
中文版:http://www.vckbase.com/document/viewdoc/?id=1886
英文版:http://www.codeproject.com/KB/threads/winspy.aspx
然后呢,這邊由于老大給弄了蠻多好書(shū)過(guò)來(lái)了,其中一本就是所謂的駭客之類(lèi)的東西,
雖然是繁體的,但是知識點(diǎn)都很不錯哦,所以也拿過(guò)來(lái)看了看,就發(fā)現其中對這個(gè)遠程線(xiàn)程的注入有很多的介紹,
而且貌似前些年的很多病毒或者木馬就是通過(guò)這屁東西來(lái)隱藏的,
看著(zhù)看著(zhù)就來(lái)勁了,而后呢,自己就根據書(shū)中的思路,
然后再結合自己的理解,將理解整理出了代碼,然后就出了這篇文章咯 !
然后注意一點(diǎn)的是,在 CodeProject 上的那篇文章中介紹了三種注入代碼技術(shù),
第一種就是眾所周知的 Hook 了;
第二種是直接將所要執行的代碼全部拷貝到宿主進(jìn)程中,即代碼遠程注入技術(shù);
第三種則是 DLL 的遠程注入技術(shù)了,其通過(guò)在宿主進(jìn)程加載自己寫(xiě)的另外的一個(gè) DLL 來(lái)實(shí)現注入;
然后在我的這篇博文中,我也只是總結前人的思想,然后再加入我自己的立即,
同時(shí)由于 Hook 太常見(jiàn)了,常見(jiàn)得不行了,所以我并不會(huì )介紹 Hook 了,而只介紹后面的兩種方式。
代碼遠程注入技術(shù)
Demo 的效果:
創(chuàng )建的項目為 RemoteThreadCode,即遠程注入代碼,其實(shí)現的功能是當運行 RemoteThreadCode.exe 時(shí),
會(huì )在 Explorer.exe 進(jìn)程中創(chuàng )建一個(gè)線(xiàn)程,而這個(gè)創(chuàng )建的線(xiàn)程功能實(shí)現很簡(jiǎn)單,
就是彈出一個(gè)消息框即 OK !
Demo 的效果展示:
當雙擊執行 RemoteThreadCode.exe 時(shí),則會(huì )注入一個(gè)線(xiàn)程到 Explorer.exe 中
當點(diǎn)擊確定后,注入到 Explorer.exe 中的線(xiàn)程執行完畢,從而 WaitForSingleObject 等待成功 !
基本思路以及所對應的代碼:
1. 提升進(jìn)程權限,如果權限不夠的話(huà),很容易造成 OpenProcess 失敗;
1: //=====================================================================================//
2: //Name: bool AdjustProcessTokenPrivilege() //
3: // //
4: //Descripion: 提升當前進(jìn)程權限 //
5: //=====================================================================================//
6: bool AdjustProcessTokenPrivilege()
7: { 8: LUID luidTmp; 9: HANDLE hToken; 10: TOKEN_PRIVILEGES tkp; 11: 12: if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
13: {14: OutputDebugString("AdjustProcessTokenPrivilege OpenProcessToken Failed ! \n");
15: 16: return false;
17: } 18: 19: if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidTmp))
20: {21: OutputDebugString("AdjustProcessTokenPrivilege LookupPrivilegeValue Failed ! \n");
22: 23: CloseHandle(hToken); 24: 25: return FALSE;
26: } 27: 28: tkp.PrivilegeCount = 1; 29: tkp.Privileges[0].Luid = luidTmp; 30: tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 31: 32: if(!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
33: {34: OutputDebugString("AdjustProcessTokenPrivilege AdjustTokenPrivileges Failed ! \n");
35: 36: CloseHandle(hToken); 37: 38: return FALSE;
39: }40: return true;
41: }最好是選擇系統要想運行,則必須開(kāi)啟的那種進(jìn)程,比如資源管理器進(jìn)程 Explorer.exe,
Windows 子系統進(jìn)程 csrss.exe 等等,但是這里注意的是,我注入 System 進(jìn)程的時(shí)候造成了失敗哦,
所以最好還是別拿 System 做實(shí)驗,而且如果你注入失敗了的話(huà),是會(huì )造成宿主進(jìn)程崩潰的,
等下一不小心把 System 進(jìn)程給弄崩潰了就不好了;
1: //=====================================================================================//
2: //Name: bool ProcessIsExplorer(DWORD dwProcessId) //
3: // //
4: //Descripion: 判定一個(gè)進(jìn)程是否為 Explorer 進(jìn)程 //
5: //=====================================================================================//
6: bool ProcessIsExplorer(DWORD dwProcessId)
7: { 8: HANDLE hProcess; 9: 10: hProcess = NULL; 11: 12: hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
13: if(NULL == hProcess)
14: {15: OutputErrorMessage("ProcessIsExplorer - OpenProcess Failed , Error Code Is %d , Error Message Is %s !");
16: 17: return FALSE;
18: } 19: 20: DWORD dwNameLen; 21: TCHAR pathArray[MAX_PATH]; 22: ZeroMemory(pathArray, MAX_PATH); 23: 24: dwNameLen = 0; 25: dwNameLen = GetModuleFileNameEx(hProcess, NULL, pathArray, MAX_PATH);26: if(dwNameLen == 0)
27: { 29: CloseHandle(hProcess); 30: 31: return FALSE;
32: } 33: 34: TCHAR exeNameArray[MAX_PATH]; 35: ZeroMemory(exeNameArray, MAX_PATH); 36: _tsplitpath(pathArray, NULL, NULL, exeNameArray, NULL); 37: 38: string str1 = exeNameArray;39: if((str1.compare("Explorer") == 0) || (str1.compare("explorer") == 0))
40: { 41: CloseHandle(hProcess); 42: 43: return TRUE;
44: } 45: 46: return FALSE;
47: }3. 打開(kāi)宿主進(jìn)程了(我這里打開(kāi)的是 Explorer.exe 進(jìn)程),思路是首先變量當前系統下運行的所有的進(jìn)程,
然后遍歷獲取到得所有的進(jìn)程的 PID,再調用 ProcessIsExplorer 函數來(lái)判斷這個(gè)進(jìn)程是否為 Explorer.exe 進(jìn)程,
如果是則記錄下這個(gè)進(jìn)程的 PID 就可以了,這樣就獲得了 Explorer.exe 進(jìn)程的 PID 了,
再通過(guò) OpenProcess 來(lái)打開(kāi)這個(gè) Explorer.exe 進(jìn)程就 OK 了;
1: //提升當前進(jìn)程的權限
2: AdjustProcessTokenPrivilege(); 3: 4: //第一個(gè)參數為用來(lái)保存所有的進(jìn)程 ID
5: //第二個(gè)參數則是第一個(gè)參數的字節數
6: //第三個(gè)參數則是寫(xiě)入 dwProcess 數組的字節數
7: EnumProcesses(dwProcess, sizeof(dwProcess), &dwNeeded);
8: 9: //找到 explorer.exe 進(jìn)程的 ID
10: dwExplorerId = 0;11: for(int i = 0; i < dwNeeded / sizeof(DWORD); i++)
12: {13: if(0 != dwProcess[i])
14: {15: if(ProcessIsExplorer(dwProcess[i]))
16: { 17: dwExplorerId = dwProcess[i];18: break;
19: } 20: } 21: } 22: 23: hProcess = NULL;24: hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwExplorerId);
25: if(NULL == hProcess)
26: {27: OutputErrorMessage("main - OpenProcess Failed , Error Code Is %d , Error Message Is %s !");
28: }4. 在宿主進(jìn)程中分配好存儲空間,這個(gè)存儲空間是用來(lái)存放我們將要創(chuàng )建的遠程線(xiàn)程的線(xiàn)程處理例程的,
這里需要注意的是:我們分配的內存必須標記必須帶有 EXECUTE,因為分配的這塊內存是用來(lái)存放線(xiàn)程處理例程的,
而線(xiàn)程處理例程必須得執行,所以必須得帶有 EXECUTE 標記,而至于 WRITE 標記的話(huà),很明顯是需要的,
因為我們在后面的代碼中還必須調用 WriteProcessMemory 來(lái)將線(xiàn)程處理例程寫(xiě)入到這塊內存中,自然其必須可寫(xiě);
1: //在 hProcess 所代表的進(jìn)程內部分配虛擬內存來(lái)容納我們將要創(chuàng )建的遠程線(xiàn)程
2: PVOID pRemoteThread = VirtualAllocEx(hProcess, NULL, THREAD_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);3: if(NULL == pRemoteThread)
4: {5: OutputErrorMessage("main - VirtualAllocEx Failed , Error Code Is %d , Error Message Is %s !");
6: 7: //關(guān)閉進(jìn)程句柄
8: CloseHandle(hProcess); 9: }5. 將遠程線(xiàn)程處理例程寫(xiě)入到 4 中在宿主進(jìn)程中所分配的內存中,這個(gè)可以直接調用 WriteProcessMemory 來(lái)實(shí)現;
1: //往我們在 hProcess 進(jìn)程中分配的虛擬內存里面寫(xiě)入數據,這里主要是將整個(gè)線(xiàn)程都寫(xiě)進(jìn)去
2: if(WriteProcessMemory(hProcess, pRemoteThread, &RemoteThreadProc, THREAD_SIZE, 0) == FALSE)
3: {4: OutputErrorMessage("main - WriteProcessMemory Failed , Error Code Is %d , Error Message Is %s !");
5: 6: //釋放 VirtualAllocEx 分配的內存
7: VirtualFreeEx(hProcess, pRemoteThread, 0, MEM_RELEASE); 8: CloseHandle(hProcess); 9: }6. 在宿主進(jìn)程中分配好存儲空間,這個(gè)存儲空間是用來(lái)存放我們將要傳遞給遠程線(xiàn)程線(xiàn)程處理例程的參數,
從下面的結構體中可以看出,其由三個(gè)參數組成,第一個(gè)參數代表要在對話(huà)框中顯示的內容,
第二個(gè)參數代表要在對話(huà)框中顯示的標題,第三個(gè)參數則是 MessageBox 這個(gè) API 的地址,
因為在 Explorer.exe 中 MessageBox 的地址會(huì )發(fā)生重定向,所以需要將其地址通過(guò)參數傳遞給線(xiàn)程處理例程;
1: typedef struct _REMOTE_PARAMETER
2: { 3: CHAR m_msgContent[MAX_PATH]; 4: CHAR m_msgTitle[MAX_PATH]; 5: DWORD m_dwMessageBoxAddr; 6: 7: }RemotePara, * PRemotePara;1: //=====================================================================================//
2: //Name: void GetMessageBoxParameter(PRemotePara pRemotePara) //
3: // //
4: //Descripion: 獲得 MessageBox 這個(gè) API 的地址以及填充的參數 //
5: //=====================================================================================//
6: void GetMessageBoxParameter(PRemotePara pRemotePara)
7: {8: HMODULE hUser32 = LoadLibrary("User32.dll");
9: 10: pRemotePara->m_dwMessageBoxAddr = (DWORD)GetProcAddress(hUser32, "MessageBoxA");
11: strcat(pRemotePara->m_msgContent, "Hello, Zachary.XiaoZhen !\0");
12: strcat(pRemotePara->m_msgTitle, "Hello\0");
13: 14: //注意要釋放掉 User32
15: FreeLibrary(hUser32); 16: }
1: RemotePara remotePara;2: ZeroMemory(&remotePara, sizeof(RemotePara));
3: GetMessageBoxParameter(&remotePara); 4: 5: //在 hProcess 所代表的進(jìn)程中分配虛擬內存來(lái)容納線(xiàn)程的參數部分
6: PRemotePara pRemotePara = (PRemotePara)VirtualAllocEx(hProcess, NULL, sizeof(RemotePara), MEM_COMMIT, PAGE_READWRITE);
7: if(NULL == pRemotePara)
8: {9: OutputErrorMessage("main - VirtualAllocEx Failed , Error Code Is %d , Error Message Is %s !");
10: 11: //釋放 VirtualAllocEx 分配的內存
12: VirtualFreeEx(hProcess, pRemoteThread, 0, MEM_RELEASE); 13: CloseHandle(hProcess); 14: }7. 將參數寫(xiě)入到 6 中在宿主進(jìn)程中所分配的內存中,同樣是調用 WriteProcessMemory 來(lái)完成;
1: //往在 hProcess 進(jìn)程中分配的虛擬內存中寫(xiě)入參數數據
2: if(WriteProcessMemory(hProcess, pRemotePara, &remotePara, sizeof(RemotePara), 0) == FALSE)
3: {4: OutputErrorMessage("main - WriteProcessMemory Failed , Error Code Is %d , Error Message Is %s !");
5: //釋放 VirtualAllocEx 分配的內存
6: VirtualFreeEx(hProcess, pRemoteThread, 0, MEM_RELEASE); 7: VirtualFreeEx(hProcess, pRemotePara, 0, MEM_RELEASE); 8: 9: CloseHandle(hProcess); 10: }8. 調用 CreateRemoteThread 在 Explorer.exe(宿主進(jìn)程)中創(chuàng )建遠程線(xiàn)程;
注意,當遠程線(xiàn)程沒(méi)有執行完時(shí),不能夠通過(guò) VirtualFreeEx 來(lái)將遠程進(jìn)程中的內存釋放掉,
你想啊,我他媽的線(xiàn)程都還在 Explorer.exe 里面執行,你他媽的在外面把我占的內存給釋放掉了,我還執行個(gè)屁啊 !
所以這里調用了 WaitForSingleObject 來(lái)等待這個(gè)遠程線(xiàn)程執行完畢,
其執行完畢后再釋放在 Explorer.exe 中所分配的存儲空間 !
1: HANDLE hThread; 2: DWORD dwThreadId; 3: 4: hThread = NULL; 5: dwThreadId = 0; 6: 7: //將已經(jīng)寫(xiě)入到 hProcess 進(jìn)程中的線(xiàn)程以及線(xiàn)程的參數作為 CreateRemoteThread 的參數,從而創(chuàng )建遠程線(xiàn)程
8: hThread = CreateRemoteThread(hProcess, NULL, 0, (DWORD (WINAPI *)(LPVOID))pRemoteThread, pRemotePara, 0, &dwThreadId);9: if(NULL == hThread)
10: {11: OutputErrorMessage("main - CreateRemoteThread Failed , Error Code Is %d , Error Message Is %s !");
12: }13: else
14: {15: OutputSuccessMessage("Code Inject Success !");
16: } 17: 18: //等待遠程線(xiàn)程結束
19: WaitForSingleObject(hThread, INFINITE); 20: CloseHandle(hThread); 21: 22: //必須等到遠程線(xiàn)程結束后才能釋放宿主進(jìn)程中所分配的內存,否則宿主進(jìn)程會(huì )直接崩潰
23: //釋放 VirtualAllocEx 分配的內存
24: VirtualFreeEx(hProcess, pRemoteThread, 0, MEM_RELEASE); 25: VirtualFreeEx(hProcess, pRemotePara, 0, MEM_RELEASE); 26: 27: CloseHandle(hProcess);9. 編寫(xiě)好遠程線(xiàn)程的線(xiàn)程處理例程即可;
1: //=====================================================================================//
2: //Name: bool RemoteThreadProc(LPVOID lpParameter) //
3: // //
4: //Descripion: 遠程線(xiàn)程處理例程 //
5: //=====================================================================================//
6: DWORD WINAPI RemoteThreadProc(PRemotePara pRemotePara) 7: {8: //這個(gè) MessageBox 的地址必須由外部參數傳入,因為在其他進(jìn)程中需要重定向
9: typedef int (WINAPI *MESSAGEBOXA)(HWND, LPCSTR, LPCSTR, UINT);
10: 11: MESSAGEBOXA MessageBoxA; 12: MessageBoxA = (MESSAGEBOXA)pRemotePara->m_dwMessageBoxAddr; 13: 14: //調用 MessageBoxA 來(lái)打印消息
15: MessageBoxA(NULL, pRemotePara->m_msgContent, pRemotePara->m_msgTitle, MB_OK); 16: 17: return 0;
18: }
DLL 遠程注入技術(shù)
Demo 的效果:
創(chuàng )建的項目為 RemoteThreadDll,即遠程注入 DLL,其實(shí)現的功能是當運行 RemoteThreadDll.exe 時(shí),
會(huì )在 Explorer.exe 進(jìn)程中創(chuàng )建一個(gè)線(xiàn)程,而這個(gè)創(chuàng )建的線(xiàn)程功能實(shí)現則相對于上面的遠程注入代碼來(lái)說(shuō)復雜一點(diǎn),
在線(xiàn)程的處理例程中,首先是由線(xiàn)程參數傳遞過(guò)來(lái)的 LoadLibrary 的地址
和 GetProcAddress 的地址來(lái)找到 LoadLibrary 和 GetProcAddress API,
然后再通過(guò) LoadLibrary(“MyDllName”) 來(lái)加載到我自己的 DLL,
然后再通過(guò) GetProcAddress 在這個(gè) DLL 中找到我的 DLL 所公開(kāi)的函數,
再就是調用這個(gè)公開(kāi)的函數了,我新建的 DLL 項目為 – ZacharyDll.dll,
該 DLL 中導出了兩個(gè)函數,一個(gè)函數用來(lái)彈出一個(gè)對話(huà)框,一個(gè)函數則是用來(lái)打印出調試信息;
Demo 的效果展示:
當雙擊執行 RemoteThreadCode.exe 時(shí),則會(huì )注入一個(gè)線(xiàn)程到 Explorer.exe 中,
而后注入的這個(gè)線(xiàn)程就會(huì )調用我自己的 ZacharyDll.dll,
再調用 ZacharyDll.dll 中導出的兩個(gè)函數了,一個(gè)輸出調試信息,一個(gè)彈出對話(huà)框:
當點(diǎn)擊確定后,注入到 Explorer.exe 中的線(xiàn)程執行完畢,從而 WaitForSingleObject 等待成功 !
基本思路以及所對應的代碼:
1. 提升進(jìn)程權限,如果權限不夠的話(huà),很容易造成 OpenProcess 失敗;
這一部分的代碼同上面的遠程注入代碼是一樣的;
2. 確定你的宿主進(jìn)程,即你所要注入代碼的進(jìn)程,這個(gè)其實(shí)很好辦,你要是不想你的木馬或者病毒被別個(gè)一下子就結束了的話(huà),
最好是選擇系統要想運行,則必須開(kāi)啟的那種進(jìn)程,比如資源管理器進(jìn)程 Explorer.exe,
Windows 子系統進(jìn)程 csrss.exe 等等,但是這里注意的是,我注入 System 進(jìn)程的時(shí)候造成了失敗哦,
所以最好還是別拿 System 做實(shí)驗,而且如果你注入失敗了的話(huà),是會(huì )造成宿主進(jìn)程崩潰的,
等下一不小心把 System 進(jìn)程給弄崩潰了就不好了;
這一部分的代碼同上面的遠程注入代碼是一樣的;
3. 打開(kāi)宿主進(jìn)程了(我這里打開(kāi)的是 Explorer.exe 進(jìn)程),思路是首先變量當前系統下運行的所有的進(jìn)程,
然后遍歷獲取到得所有的進(jìn)程的 PID,再調用 ProcessIsExplorer 函數來(lái)判斷這個(gè)進(jìn)程是否為 Explorer.exe 進(jìn)程,
如果是則記錄下這個(gè)進(jìn)程的 PID 就可以了,這樣就獲得了 Explorer.exe 進(jìn)程的 PID 了,
再通過(guò) OpenProcess 來(lái)打開(kāi)這個(gè) Explorer.exe 進(jìn)程就 OK 了;
這一部分的代碼同上面的遠程注入代碼是一樣的;
4. 在宿主進(jìn)程中分配好存儲空間,這個(gè)存儲空間是用來(lái)存放我們將要創(chuàng )建的遠程線(xiàn)程的線(xiàn)程處理例程的,
這里需要注意的是:我們分配的內存必須標記必須帶有 EXECUTE,因為分配的這塊內存是用來(lái)存放線(xiàn)程處理例程的,
而線(xiàn)程處理例程必須得執行,所以必須得帶有 EXECUTE 標記,而至于 WRITE 標記的話(huà),很明顯是需要的,
因為我們在后面的代碼中還必須調用 WriteProcessMemory 來(lái)將線(xiàn)程處理例程寫(xiě)入到這塊內存中,自然其必須可寫(xiě);
這一部分的代碼同上面的遠程注入代碼是一樣的;
5. 將遠程線(xiàn)程處理例程寫(xiě)入到 4 中在宿主進(jìn)程中所分配的內存中,這個(gè)可以直接調用 WriteProcessMemory 來(lái)實(shí)現;
這一部分的代碼同上面的遠程注入代碼是一樣的;
6. 在宿主進(jìn)程中分配好存儲空間,這個(gè)存儲空間是用來(lái)存放我們將要傳遞給遠程線(xiàn)程線(xiàn)程處理例程的參數,
從下面的結構體中可以看出,其由六個(gè)參數組成,
第一個(gè)參數代表 ZacharyDll.dll 中導出的 PrintMessageBox 的名稱(chēng),
第二個(gè)參數代表 ZacharyDll.dll 中導出的 PrintDbgStr 的名稱(chēng)題,
第三個(gè)參數則是 ZacharyDll.dll 所在的路徑,
第四個(gè)參數則是代表 LoadLibrary 的地址,
第五個(gè)參數代表 FreeLibrary 的地址,
第六個(gè)參數代表 GetProcAddress 的地址;
1: #define DLLNAME "\\ZacharyDll.dll\0"
2: 3: typedef struct _REMOTE_PARAMETER
4: { 5: CHAR m_printMsgBox[MAX_PATH]; 6: CHAR m_printDbgStr[MAX_PATH]; 7: CHAR m_strDllPath[MAX_PATH]; 8: DWORD m_dwLoadLibraryAddr; 9: DWORD m_dwFreeLibraryAddr; 10: DWORD m_dwGetProcAddrAddr; 11: 12: }RemotePara, * PRemotePara;
1: HMODULE hKernel32 = GetModuleHandle("Kernel32");
2: if(NULL == hKernel32)
3: {4: OutputErrorMessage("main - GetModuleHandle Failed , Error Code Is %d , Error Message Is %s !");
5: 6: //釋放 VirtualAllocEx 分配的內存
7: VirtualFreeEx(hProcess, pRemoteThread, 0, MEM_RELEASE); 8: CloseHandle(hProcess); 9: }10: else
11: { 12: RemotePara remotePara;13: ZeroMemory(&remotePara, sizeof(RemotePara));
14: 15: //將 LoadLibraryA、FreeLibrary 和 GetProcAddress 三個(gè) Kernel32 API 的地址保存到 remotePara 中
16: remotePara.m_dwLoadLibraryAddr = (DWORD)GetProcAddress(hKernel32, "LoadLibraryA");
17: remotePara.m_dwFreeLibraryAddr = (DWORD)GetProcAddress(hKernel32, "FreeLibrary");
18: remotePara.m_dwGetProcAddrAddr = (DWORD)GetProcAddress(hKernel32, "GetProcAddress");
19: 20: string strMsgBox = "PrintMessageBox";
21: string strBbgStr = "PrintDebugString";
22: 23: CHAR tmpArray[MAX_PATH];24: CHAR * pTmpMsgBoxArray = "PrintMessageBox";
25: CHAR * pTmpDbgStrArray = "PrintDebugString";
26: 27: //將 ZacharyDll.dll 中導出的 API 的名稱(chēng)保存到 remotePara 中
28: strcpy(remotePara.m_printMsgBox, pTmpMsgBoxArray); 29: strcpy(remotePara.m_printDbgStr, pTmpDbgStrArray); 30: 31: ZeroMemory(tmpArray, MAX_PATH); 32: 33: //獲取到當前路徑
34: GetCurrentDirectory(MAX_PATH, tmpArray); 35: 36: //路徑加上 DLL 名稱(chēng)(從而可以將 DLL 和 Loader EXE 放在同一個(gè)目錄下運行了)
37: //免去了將 DLL 復制到系統目錄下的麻煩
38: string strDllPath = tmpArray; 39: strDllPath += DLLNAME; 40: 41: //將 DLL 的路徑完整的復制到 remotePara 中
42: strcpy(remotePara.m_strDllPath, strDllPath.c_str());43: //free(tmpArray);
44: 45: //在宿主進(jìn)程中分配虛擬內存來(lái)容納遠程線(xiàn)程所需要的參數
46: PVOID pRemotePara = VirtualAllocEx(hProcess, NULL, sizeof(RemotePara), MEM_COMMIT, PAGE_READWRITE);
47: if(NULL == pRemotePara)
48: {49: OutputErrorMessage("main - VirtualAllocEx Failed , Error Code Is %d , Error Message Is %s !");
50: 51: //釋放 VirtualAllocEx 分配的內存
52: VirtualFreeEx(hProcess, pRemoteThread, 0, MEM_RELEASE); 53: CloseHandle(hProcess); 54: }7. 將參數寫(xiě)入到 6 中在宿主進(jìn)程中所分配的內存中,同樣是調用 WriteProcessMemory 來(lái)完成;
1: //將遠程線(xiàn)程所攜帶的參數寫(xiě)入到宿主進(jìn)程中所分配的虛擬內存
2: if(NULL == WriteProcessMemory(hProcess, pRemotePara, &remotePara, sizeof(RemotePara), 0))
3: {4: OutputErrorMessage("main - WriteProcessMemory Failed , Error Code Is %d , Error Message Is %s !");
5: 6: //釋放 VirtualAllocEx 分配的內存
7: VirtualFreeEx(hProcess, pRemoteThread, 0, MEM_RELEASE); 8: VirtualFreeEx(hProcess, pRemotePara, 0, MEM_RELEASE); 9: CloseHandle(hProcess); 10: }8. 調用 CreateRemoteThread 在 Explorer.exe(宿主進(jìn)程)中創(chuàng )建遠程線(xiàn)程;
注意,當遠程線(xiàn)程沒(méi)有執行完時(shí),不能夠通過(guò) VirtualFreeEx 來(lái)將遠程進(jìn)程中的內存釋放掉,
你想啊,我他媽的線(xiàn)程都還在 Explorer.exe 里面執行,你他媽的在外面把我占的內存給釋放掉了,我還執行個(gè)屁啊 !
所以這里調用了 WaitForSingleObject 來(lái)等待這個(gè)遠程線(xiàn)程執行完畢,
其執行完畢后再釋放在 Explorer.exe 中所分配的存儲空間 !
這一部分的代碼同上面的遠程注入代碼是類(lèi)似的;
9. 編寫(xiě)好遠程線(xiàn)程的線(xiàn)程處理例程即可;
1: //=====================================================================================//
2: //Name: bool RemoteThreadProc(LPVOID lpParameter) //
3: // //
4: //Descripion: 遠程線(xiàn)程處理例程 //
5: //=====================================================================================//
6: DWORD WINAPI RemoteThreadProc(PRemotePara pRemotePara) 7: {8: //對于從參數 pRemotePara 中傳過(guò)來(lái)的 API 都需要重新聲明
9: typedef HMODULE (WINAPI *LOADLIBRARY_ZACHARY)(LPCSTR);
10: typedef BOOL (WINAPI *FREELIBRARY_ZACHARY)(HMODULE);
11: typedef FARPROC (WINAPI *GETPROCADDRESS_ZACHARY)(HMODULE hModule, LPCSTR lpProcName);
12: 13: //這兩個(gè) API 是由 ZacharyDLL.dll 導出的,也需要重新聲明
14: typedef void (* PRINTMESSAGEBOX_ZACHARY)();
15: typedef void (* PRINTDEBUGSTRING)();
16: 17: LOADLIBRARY_ZACHARY LoadLibrary_Zachary; 18: FREELIBRARY_ZACHARY FreeLibrary_Zachary; 19: GETPROCADDRESS_ZACHARY GetProcAddress_Zachary; 20: PRINTMESSAGEBOX_ZACHARY PrintMessageBox_Zachary; 21: PRINTDEBUGSTRING PrintDebugString_Zachary; 22: 23: //在參數 pRemotePara 中保存了 LoadLibray,FreeLibrary 和 GetProcAddress 這三個(gè) API 的地址
24: LoadLibrary_Zachary = (LOADLIBRARY_ZACHARY)pRemotePara->m_dwLoadLibraryAddr; 25: FreeLibrary_Zachary = (FREELIBRARY_ZACHARY)pRemotePara->m_dwFreeLibraryAddr; 26: GetProcAddress_Zachary = (GETPROCADDRESS_ZACHARY)pRemotePara->m_dwGetProcAddrAddr; 27: 28: //獲得 DLL 所在的地址
29: PCHAR pDllPath = pRemotePara->m_strDllPath; 30: 31: //加載我們自己的 DLL - ZacharyDLL.dll
32: HMODULE hMyDll = LoadLibrary_Zachary(pDllPath); 33: 34: if(NULL != hMyDll)
35: {36: //從 ZacharyDll.dll 中通過(guò) GetProcAddress 獲取 DLL 導出的 API 的地址
37: PrintDebugString_Zachary = (PRINTDEBUGSTRING)GetProcAddress_Zachary(hMyDll, pRemotePara->m_printDbgStr); 38: PrintMessageBox_Zachary = (PRINTMESSAGEBOX_ZACHARY)GetProcAddress_Zachary(hMyDll, pRemotePara->m_printMsgBox); 39: 40: //執行 DLL 中所導出的 API
41: PrintDebugString_Zachary(); 42: PrintMessageBox_Zachary();43: //釋放所加載的 DLL
44: FreeLibrary_Zachary(hMyDll); 45: }46: return 0;
47: }10. 編寫(xiě)好要注入的 DLL – ZacharyDll.dll;
1: BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved) 2: {3: switch (dwReason)
4: {5: case DLL_PROCESS_ATTACH:
6: break;
7: case DLL_PROCESS_DETACH:
8: break;
9: case DLL_THREAD_ATTACH:
10: break;
11: case DLL_THREAD_DETACH:
12: break;
13: } 14: 15: return TRUE;
16: } 17: 18: //彈出一個(gè)對話(huà)框
19: void PrintMessageBox()
20: { 21: MessageBox(NULL, MESSAGE_CONTENT, MESSAGE_TITLE, MB_OK); 22: } 23: 24: //打印語(yǔ)句
25: void PrintDebugString()
26: {27: //直接打印出 5 條同樣的消息
28: for(int i=0; i<5; i++)
29: {30: OutputDebugString("In ZacharyDll - PrintDebugString !");
31: } 32: }
兩種注入技術(shù)的優(yōu)點(diǎn)和缺點(diǎn)總結
使用代碼遠程注入技術(shù)的話(huà),其相對于使用 DLL 遠程注入技術(shù)來(lái)說(shuō),
有一個(gè)優(yōu)點(diǎn),就是其不會(huì )無(wú)緣無(wú)故的讓宿主進(jìn)程加載其他的 DLL,
為什么說(shuō)這是一個(gè)優(yōu)點(diǎn) ? 那是因為很多的監控軟件或者殺軟都在實(shí)時(shí)監控著(zhù)每個(gè)進(jìn)程,
當一個(gè)進(jìn)程中加載了其他的 DLL 時(shí)(加載 DLL 的動(dòng)作相對來(lái)說(shuō)是比較大的)很容易被發(fā)覺(jué),
而且當一個(gè) DLL 被加載到進(jìn)程中以后,可以利用很多的工具,
比如 Process Explorer 之類(lèi)的將該進(jìn)程所加載的 DLL 枚舉出來(lái),這樣你所注入的 DLL 也就暴露無(wú)遺了 !
而如果你使用的是代碼注入,那相對來(lái)說(shuō)會(huì )安靜很多(畢竟是小動(dòng)作),而且也不會(huì )讓宿主進(jìn)程加載其他的 DLL,
所以相對來(lái)說(shuō),其成功的可能性會(huì )更高,但是使用代碼遠程注入有一個(gè)致命的弱點(diǎn),
那就是你所注入的代碼中所使用的 API 都必須要重定向,
而且如果是自定義的函數的話(huà),則必須將這個(gè)函數也全部拷貝到宿主進(jìn)程中,
注入的代碼中所使用的全局變量以及所使用的字符串都必須重新拷貝到宿主進(jìn)程中,
聽(tīng)起來(lái)貌似沒(méi)什么,但是事實(shí)上,這屁東西會(huì )搞得很復雜,就比如你在注入的代碼中所使用的一個(gè)字符串,
你也必須先在宿主進(jìn)程中分配虛擬內存,然后將這個(gè)字符串寫(xiě)入到這個(gè)新分配的虛擬內存中,
如果你字符串多的話(huà),這樣的操作會(huì )煩死人去,而且這還僅僅是字符串,對于你所需要使用的一些 Win32 API,
你也都得先獲取好這些 API 的地址,然后又重復上面的操作,分配虛擬內存,寫(xiě)入地址,最后才能夠在遠程線(xiàn)程中調用,
所以如果你所注入的代碼需要完成很復雜的功能的話(huà),還是使用 DLL 的遠程注入技術(shù)比較好,使用遠程注入代碼會(huì )搞死人的 !
使用 DLL 的遠程注入技術(shù)的病毒或者木馬通常都位于一個(gè) DLL 中,
在系統啟動(dòng)時(shí),通過(guò)另外的一個(gè) EXE 程序來(lái)在另外的一個(gè)進(jìn)程(宿主進(jìn)程,比如使用 Explorer.exe)中創(chuàng )建一個(gè)遠程線(xiàn)程,
然后再在這個(gè)遠程線(xiàn)程的線(xiàn)程處理函數中將這個(gè)帶有病毒或者木馬主體的 DLL 加載到宿主進(jìn)程中,
這樣的話(huà),這個(gè)帶有病毒或者木馬主體的 DLL 就會(huì )被宿主進(jìn)程加載,從而得以在宿主進(jìn)程中執行,
這樣,即使我們自己的 EXE(這個(gè)進(jìn)程通常被稱(chēng)之為 Loader)進(jìn)程被關(guān)閉了也不會(huì )影響到病毒或者木馬主體代碼的執行,
因為這些主體代碼位于 Explorer.exe 進(jìn)程中執行,而 Explorer.exe 基本上不會(huì )被關(guān)閉的。
并且使用這種方法,你的惡意代碼也很難被什么任務(wù)管理器之類(lèi)的發(fā)現,因為只要宿主進(jìn)程沒(méi)有終止運行,
那么這個(gè) DLL 也就不會(huì )在內存中卸載(當然被卸載還是可能的,殺軟就可以利用這點(diǎn)來(lái)將這個(gè) DLL 卸載掉)。
聯(lián)系客服