代碼注入之遠程線程篇

引子

         

前些日子由於項目要求,在網上到處找資料,於無意中發現了 CodeProject 上的一篇很老的文章,文章標題爲:

Three Ways to Inject Your Code into Another Process

這篇文章呢,出來很久咯,還是 03 年的文章了,可惜我弄底層弄得時間不久哦,不然應該早就看過這篇大作了,

由於是大作,而且出來的又久了,自然在網上也就到處流傳咯,

所以也有人將這篇文章翻譯成了中文版的,下面給出這篇大作的兩個鏈接:

中文版:http://www.vckbase.com/document/viewdoc/?id=1886

英文版:http://www.codeproject.com/KB/threads/winspy.aspx

然後呢,這邊由於老大給弄了蠻多好書過來了,其中一本就是所謂的駭客之類的東西,

雖然是繁體的,但是知識點都很不錯哦,所以也拿過來看了看,就發現其中對這個遠程線程的注入有很多的介紹,

而且貌似前些年的很多病毒或者木馬就是通過這屁東西來隱藏的,

看着看着就來勁了,而後呢,自己就根據書中的思路,

然後再結合自己的理解,將理解整理出了代碼,然後就出了這篇文章咯 !

然後注意一點的是,在 CodeProject 上的那篇文章中介紹了三種注入代碼技術,

第一種就是衆所周知的 Hook 了;

第二種是直接將所要執行的代碼全部拷貝到宿主進程中,即代碼遠程注入技術;

第三種則是 DLL 的遠程注入技術了,其通過在宿主進程加載自己寫的另外的一個 DLL 來實現注入;

然後在我的這篇博文中,我也只是總結前人的思想,然後再加入我自己的立即,

同時由於 Hook 太常見了,常見得不行了,所以我並不會介紹 Hook 了,而只介紹後面的兩種方式。

          

            

代碼遠程注入技術

    

Demo 的效果:

創建的項目爲 RemoteThreadCode,即遠程注入代碼,其實現的功能是當運行 RemoteThreadCode.exe 時,

會在 Explorer.exe 進程中創建一個線程,而這個創建的線程功能實現很簡單,

就是彈出一個消息框即 OK !

      

Demo 的效果展示:

當雙擊執行 RemoteThreadCode.exe 時,則會注入一個線程到 Explorer.exe 中

image

             

當點擊確定後,注入到 Explorer.exe 中的線程執行完畢,從而 WaitForSingleObject 等待成功 !

image

          

基本思路以及所對應的代碼:

1. 提升進程權限,如果權限不夠的話,很容易造成 OpenProcess 失敗;

   1: //=====================================================================================//
   2: //Name: bool AdjustProcessTokenPrivilege()                                             //
   3: //                                                                                     //
   4: //Descripion: 提升當前進程權限                                                                 //
   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: }
2. 確定你的宿主進程,即你所要注入代碼的進程,這個其實很好辦,你要是不想你的木馬或者病毒被別個一下子就結束了的話,

   最好是選擇系統要想運行,則必須開啓的那種進程,比如資源管理器進程 Explorer.exe,

   Windows 子系統進程 csrss.exe 等等,但是這裏注意的是,我注入 System 進程的時候造成了失敗哦,

   所以最好還是別拿 System 做實驗,而且如果你注入失敗了的話,是會造成宿主進程崩潰的,

   等下一不小心把 System 進程給弄崩潰了就不好了;

   1: //=====================================================================================//
   2: //Name: bool ProcessIsExplorer(DWORD dwProcessId)                                      //
   3: //                                                                                     //
   4: //Descripion: 判定一個進程是否爲 Explorer 進程                                                 //
   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. 打開宿主進程了(我這裏打開的是 Explorer.exe 進程),思路是首先變量當前系統下運行的所有的進程,

   然後遍歷獲取到得所有的進程的 PID,再調用 ProcessIsExplorer 函數來判斷這個進程是否爲 Explorer.exe 進程,

   如果是則記錄下這個進程的 PID 就可以了,這樣就獲得了 Explorer.exe 進程的 PID 了,

   再通過 OpenProcess 來打開這個 Explorer.exe 進程就 OK 了;

   1: //提升當前進程的權限
   2: AdjustProcessTokenPrivilege();
   3:  
   4: //第一個參數爲用來保存所有的進程 ID
   5: //第二個參數則是第一個參數的字節數
   6: //第三個參數則是寫入 dwProcess 數組的字節數
   7: EnumProcesses(dwProcess, sizeof(dwProcess), &dwNeeded);
   8:  
   9: //找到 explorer.exe 進程的 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. 在宿主進程中分配好存儲空間,這個存儲空間是用來存放我們將要創建的遠程線程的線程處理例程的,

   這裏需要注意的是:我們分配的內存必須標記必須帶有 EXECUTE,因爲分配的這塊內存是用來存放線程處理例程的,

   而線程處理例程必須得執行,所以必須得帶有 EXECUTE 標記,而至於 WRITE 標記的話,很明顯是需要的,

   因爲我們在後面的代碼中還必須調用 WriteProcessMemory 來將線程處理例程寫入到這塊內存中,自然其必須可寫;

   1: //在 hProcess 所代表的進程內部分配虛擬內存來容納我們將要創建的遠程線程
   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:     //關閉進程句柄
   8:     CloseHandle(hProcess);
   9: }

5. 將遠程線程處理例程寫入到 4 中在宿主進程中所分配的內存中,這個可以直接調用 WriteProcessMemory 來實現;

   1: //往我們在 hProcess 進程中分配的虛擬內存裏面寫入數據,這裏主要是將整個線程都寫進去
   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. 在宿主進程中分配好存儲空間,這個存儲空間是用來存放我們將要傳遞給遠程線程線程處理例程的參數,

   從下面的結構體中可以看出,其由三個參數組成,第一個參數代表要在對話框中顯示的內容,

   第二個參數代表要在對話框中顯示的標題,第三個參數則是 MessageBox 這個 API 的地址,

   因爲在 Explorer.exe 中 MessageBox 的地址會發生重定向,所以需要將其地址通過參數傳遞給線程處理例程;

   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 這個 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 所代表的進程中分配虛擬內存來容納線程的參數部分
   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. 將參數寫入到 6 中在宿主進程中所分配的內存中,同樣是調用 WriteProcessMemory 來完成;

   1: //往在 hProcess 進程中分配的虛擬內存中寫入參數數據
   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(宿主進程)中創建遠程線程;

   注意,當遠程線程沒有執行完時,不能夠通過 VirtualFreeEx 來將遠程進程中的內存釋放掉,

   你想啊,我他媽的線程都還在 Explorer.exe 裏面執行,你他媽的在外面把我佔的內存給釋放掉了,我還執行個屁啊 !

   所以這裏調用了 WaitForSingleObject 來等待這個遠程線程執行完畢,

   其執行完畢後再釋放在 Explorer.exe 中所分配的存儲空間 !

   1: HANDLE hThread;
   2: DWORD dwThreadId;
   3:  
   4: hThread = NULL;
   5: dwThreadId = 0;
   6:  
   7: //將已經寫入到 hProcess 進程中的線程以及線程的參數作爲 CreateRemoteThread 的參數,從而創建遠程線程
   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: //等待遠程線程結束
  19: WaitForSingleObject(hThread, INFINITE);
  20: CloseHandle(hThread);
  21:  
  22: //必須等到遠程線程結束後才能釋放宿主進程中所分配的內存,否則宿主進程會直接崩潰
  23: //釋放 VirtualAllocEx 分配的內存
  24: VirtualFreeEx(hProcess, pRemoteThread, 0, MEM_RELEASE);
  25: VirtualFreeEx(hProcess, pRemotePara, 0, MEM_RELEASE);
  26:  
  27: CloseHandle(hProcess);

9. 編寫好遠程線程的線程處理例程即可;

   1: //=====================================================================================//
   2: //Name: bool RemoteThreadProc(LPVOID lpParameter)                                      //
   3: //                                                                                     //
   4: //Descripion: 遠程線程處理例程                                                                 //
   5: //=====================================================================================//
   6: DWORD WINAPI RemoteThreadProc(PRemotePara pRemotePara)
   7: {
   8:     //這個 MessageBox 的地址必須由外部參數傳入,因爲在其他進程中需要重定向
   9:     typedef int (WINAPI *MESSAGEBOXA)(HWND, LPCSTR, LPCSTR, UINT);
  10:  
  11:     MESSAGEBOXA MessageBoxA;
  12:     MessageBoxA = (MESSAGEBOXA)pRemotePara->m_dwMessageBoxAddr;
  13:  
  14:     //調用 MessageBoxA 來打印消息
  15:     MessageBoxA(NULL, pRemotePara->m_msgContent, pRemotePara->m_msgTitle, MB_OK);
  16:  
  17:     return 0;
  18: }

          

           

DLL 遠程注入技術

      

Demo 的效果:

創建的項目爲 RemoteThreadDll,即遠程注入 DLL,其實現的功能是當運行 RemoteThreadDll.exe 時,

會在 Explorer.exe 進程中創建一個線程,而這個創建的線程功能實現則相對於上面的遠程注入代碼來說複雜一點,

在線程的處理例程中,首先是由線程參數傳遞過來的 LoadLibrary 的地址

和 GetProcAddress 的地址來找到 LoadLibrary 和 GetProcAddress API,

然後再通過 LoadLibrary(“MyDllName”) 來加載到我自己的 DLL,

然後再通過 GetProcAddress 在這個 DLL 中找到我的 DLL 所公開的函數,

再就是調用這個公開的函數了,我新建的 DLL 項目爲 – ZacharyDll.dll,

該 DLL 中導出了兩個函數,一個函數用來彈出一個對話框,一個函數則是用來打印出調試信息;

           

Demo 的效果展示:

當雙擊執行 RemoteThreadCode.exe 時,則會注入一個線程到 Explorer.exe 中,

而後注入的這個線程就會調用我自己的 ZacharyDll.dll,

再調用 ZacharyDll.dll 中導出的兩個函數了,一個輸出調試信息,一個彈出對話框:

image

當點擊確定後,注入到 Explorer.exe 中的線程執行完畢,從而 WaitForSingleObject 等待成功 !

image

         

基本思路以及所對應的代碼:

1. 提升進程權限,如果權限不夠的話,很容易造成 OpenProcess 失敗;

   這一部分的代碼同上面的遠程注入代碼是一樣的;

    

2. 確定你的宿主進程,即你所要注入代碼的進程,這個其實很好辦,你要是不想你的木馬或者病毒被別個一下子就結束了的話,

   最好是選擇系統要想運行,則必須開啓的那種進程,比如資源管理器進程 Explorer.exe,

   Windows 子系統進程 csrss.exe 等等,但是這裏注意的是,我注入 System 進程的時候造成了失敗哦,

   所以最好還是別拿 System 做實驗,而且如果你注入失敗了的話,是會造成宿主進程崩潰的,

   等下一不小心把 System 進程給弄崩潰了就不好了;

   這一部分的代碼同上面的遠程注入代碼是一樣的;

            

3. 打開宿主進程了(我這裏打開的是 Explorer.exe 進程),思路是首先變量當前系統下運行的所有的進程,

   然後遍歷獲取到得所有的進程的 PID,再調用 ProcessIsExplorer 函數來判斷這個進程是否爲 Explorer.exe 進程,

   如果是則記錄下這個進程的 PID 就可以了,這樣就獲得了 Explorer.exe 進程的 PID 了,

   再通過 OpenProcess 來打開這個 Explorer.exe 進程就 OK 了;

   這一部分的代碼同上面的遠程注入代碼是一樣的;

           

4. 在宿主進程中分配好存儲空間,這個存儲空間是用來存放我們將要創建的遠程線程的線程處理例程的,

   這裏需要注意的是:我們分配的內存必須標記必須帶有 EXECUTE,因爲分配的這塊內存是用來存放線程處理例程的,

   而線程處理例程必須得執行,所以必須得帶有 EXECUTE 標記,而至於 WRITE 標記的話,很明顯是需要的,

   因爲我們在後面的代碼中還必須調用 WriteProcessMemory 來將線程處理例程寫入到這塊內存中,自然其必須可寫;

   這一部分的代碼同上面的遠程注入代碼是一樣的;

         

5. 將遠程線程處理例程寫入到 4 中在宿主進程中所分配的內存中,這個可以直接調用 WriteProcessMemory 來實現;

   這一部分的代碼同上面的遠程注入代碼是一樣的;

        

6. 在宿主進程中分配好存儲空間,這個存儲空間是用來存放我們將要傳遞給遠程線程線程處理例程的參數,

   從下面的結構體中可以看出,其由六個參數組成,

   第一個參數代表 ZacharyDll.dll 中導出的 PrintMessageBox 的名稱,

   第二個參數代表 ZacharyDll.dll 中導出的 PrintDbgStr 的名稱題,

   第三個參數則是 ZacharyDll.dll 所在的路徑,

   第四個參數則是代表 LoadLibrary 的地址,

   第五個參數代表 FreeLibrary 的地址,

   第六個參數代表 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 三個 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 的名稱保存到 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 名稱(從而可以將 DLL 和 Loader EXE 放在同一個目錄下運行了)
  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:     //在宿主進程中分配虛擬內存來容納遠程線程所需要的參數
  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. 將參數寫入到 6 中在宿主進程中所分配的內存中,同樣是調用 WriteProcessMemory 來完成;

   1: //將遠程線程所攜帶的參數寫入到宿主進程中所分配的虛擬內存
   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(宿主進程)中創建遠程線程;

   注意,當遠程線程沒有執行完時,不能夠通過 VirtualFreeEx 來將遠程進程中的內存釋放掉,

   你想啊,我他媽的線程都還在 Explorer.exe 裏面執行,你他媽的在外面把我佔的內存給釋放掉了,我還執行個屁啊 !

   所以這裏調用了 WaitForSingleObject 來等待這個遠程線程執行完畢,

   其執行完畢後再釋放在 Explorer.exe 中所分配的存儲空間 !

   這一部分的代碼同上面的遠程注入代碼是類似的;

              

9. 編寫好遠程線程的線程處理例程即可;

   1: //=====================================================================================//
   2: //Name: bool RemoteThreadProc(LPVOID lpParameter)                                      //
   3: //                                                                                     //
   4: //Descripion: 遠程線程處理例程                                                                 //
   5: //=====================================================================================//
   6: DWORD WINAPI RemoteThreadProc(PRemotePara pRemotePara)
   7: {
   8:     //對於從參數 pRemotePara 中傳過來的 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:     //這兩個 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 這三個 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 中通過 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. 編寫好要注入的 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: //彈出一個對話框
  19: void PrintMessageBox()
  20: {
  21:     MessageBox(NULL, MESSAGE_CONTENT, MESSAGE_TITLE, MB_OK);
  22: }
  23:  
  24: //打印語句
  25: void PrintDebugString()
  26: {
  27:     //直接打印出 5 條同樣的消息
  28:     for(int i=0; i<5; i++)
  29:     {
  30:         OutputDebugString("In ZacharyDll - PrintDebugString !");
  31:     }
  32: }

                

                

兩種注入技術的優點和缺點總結

                

使用代碼遠程注入技術的話,其相對於使用 DLL 遠程注入技術來說,

有一個優點,就是其不會無緣無故的讓宿主進程加載其他的 DLL,

爲什麼說這是一個優點 ? 那是因爲很多的監控軟件或者殺軟都在實時監控着每個進程,

當一個進程中加載了其他的 DLL 時(加載 DLL 的動作相對來說是比較大的)很容易被發覺,

而且當一個 DLL 被加載到進程中以後,可以利用很多的工具,

比如 Process Explorer 之類的將該進程所加載的 DLL 枚舉出來,這樣你所注入的 DLL 也就暴露無遺了 !

而如果你使用的是代碼注入,那相對來說會安靜很多(畢竟是小動作),而且也不會讓宿主進程加載其他的 DLL,

所以相對來說,其成功的可能性會更高,但是使用代碼遠程注入有一個致命的弱點,

那就是你所注入的代碼中所使用的 API 都必須要重定向,

而且如果是自定義的函數的話,則必須將這個函數也全部拷貝到宿主進程中,

注入的代碼中所使用的全局變量以及所使用的字符串都必須重新拷貝到宿主進程中,

聽起來貌似沒什麼,但是事實上,這屁東西會搞得很複雜,就比如你在注入的代碼中所使用的一個字符串,

你也必須先在宿主進程中分配虛擬內存,然後將這個字符串寫入到這個新分配的虛擬內存中,

如果你字符串多的話,這樣的操作會煩死人去,而且這還僅僅是字符串,對於你所需要使用的一些 Win32 API,

你也都得先獲取好這些 API 的地址,然後又重複上面的操作,分配虛擬內存,寫入地址,最後才能夠在遠程線程中調用,

所以如果你所注入的代碼需要完成很複雜的功能的話,還是使用 DLL 的遠程注入技術比較好,使用遠程注入代碼會搞死人的 !

                     

使用 DLL 的遠程注入技術的病毒或者木馬通常都位於一個 DLL 中,

在系統啓動時,通過另外的一個 EXE 程序來在另外的一個進程(宿主進程,比如使用 Explorer.exe)中創建一個遠程線程,

然後再在這個遠程線程的線程處理函數中將這個帶有病毒或者木馬主體的 DLL 加載到宿主進程中,

這樣的話,這個帶有病毒或者木馬主體的 DLL 就會被宿主進程加載,從而得以在宿主進程中執行,

這樣,即使我們自己的 EXE(這個進程通常被稱之爲 Loader)進程被關閉了也不會影響到病毒或者木馬主體代碼的執行,

因爲這些主體代碼位於 Explorer.exe 進程中執行,而 Explorer.exe 基本上不會被關閉的。

並且使用這種方法,你的惡意代碼也很難被什麼任務管理器之類的發現,因爲只要宿主進程沒有終止運行,

那麼這個 DLL 也就不會在內存中卸載(當然被卸載還是可能的,殺軟就可以利用這點來將這個 DLL 卸載掉)。

     

         

Demo 展望:

            

上面的這種注入技術用來實現木馬或者其他的惡意程序其實是比較方便的,

首先由一個 Loader.exe (這個 Loader 可以通過其他的方式來設置爲隨機器自動啓動,

這可以通過修改 system.ini 或者註冊表或者服務之類的來實現)

來通過 CreateRemoteThread 來在 Explorer.exe 進程中創建一個遠程線程,

而後由這個遠程線程神不知鬼不覺的調用一個另外的 DLL,

然後在另外的那個 DLL 中,我們就可以做很多的事情了啊,比如最簡單的,設個全局鍵盤鉤子,用來捕獲鍵盤的記錄,

再進行一定的解析就有可能獲取用戶有效的密碼之類的信息,同時,既然 DLL 都注入到 Explorer.exe 中了,

那麼還可以做很多邪惡的事情,比如在其中悄悄的掃描用戶磁盤上的文件,只要是 .jpg, .png 之類的就全部給記錄下來,

或者再在 DLL 中創建個什麼 SOCKET 之類的,並且將這些什麼 .png 啊,.jpg 啊之類的圖片數據悄悄的傳出去,

然後說不準第二個豔照門就出來了 ~

當然這些都是後話了,不過有了上面的這幾個技術,要實現這種簡單的木馬功能還是很容易了哦 !

當然,這裏再提一下的是,這樣做不道德 ! 針對女朋友的屬於合法行爲 ! 哈哈哈 !

然後還有一個要提一下的是,現在這種注入方式,殺軟或者安全衛士是能夠捕捉到了,也就是過不了殺軟或安全衛士這一關了哦 !

畢竟 03 年就有大牛弄這種東西了,這麼些年了,人家做反病毒做安全的也不是吃飯的 !

不過如果將上面的技術和 Rootkit 做個結合,可能效果會很不一樣了哦,尚待研究 ~

      

             

下載 CodeInject.zip

      

                        

版權所有,迎轉載,但轉載請註明: 轉載自  Zachary.XiaoZhen - 夢想的天空

       

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章