天天看點

遠端線程入門

  遠端線程作為一項"合法"的代碼注入技術,在windows上被大量使用, 它的本質就是把一塊可執行代碼寫入到對方程序,然後讓其起運作起來。

      一般它的實作過程是這樣的, 通過VirtualAllocEx在目标程序配置設定記憶體空間,然後通過WriteProcessMemory将我們的可執行代碼寫入到目标程序,最後通過CreateRemoteThread讓我們的可執行代碼在目标程序裡運作起來。

     一般實作遠端線程有2種方法, 一種是《windows核心程式設計》裡介紹的,通過線程函數和LoadLibrary API函數申明的相似性, 直接在目标程序裡調用LoadLibrary加載我們DLL,這樣我們隻要在DLL_PROCESS_ATTACH裡執行我們的代碼就可以了。代碼如下, 通過InjectLib在目标程序加載我們的DLL, 通過EjectLib解除安裝我們的DLL:

///////////////////////////////////////////////////////////////////////////////

BOOL WINAPI InjectLibW(DWORD dwProcessId, PCWSTR pszLibFile) 

{

   BOOL fOk = FALSE; // Assume that the function fails

   HANDLE hProcess = NULL, hThread = NULL;

   PWSTR pszLibFileRemote = NULL;

   __try {

      // Get a handle for the target process.

      hProcess = OpenProcess(

         PROCESS_QUERY_INFORMATION |   // Required by Alpha

         PROCESS_CREATE_THREAD     |   // For CreateRemoteThread

         PROCESS_VM_OPERATION      |   // For VirtualAllocEx/VirtualFreeEx

         PROCESS_VM_WRITE,             // For WriteProcessMemory

         FALSE, dwProcessId);

      if (hProcess == NULL)

      {

          __leave;

      }

      // Calculate the number of bytes needed for the DLL's pathname

      int cch = 1 + lstrlenW(pszLibFile);

      int cb  = cch * sizeof(WCHAR);

      // Allocate space in the remote process for the pathname

      pszLibFileRemote = (PWSTR) 

         VirtualAllocEx(hProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);

      if (pszLibFileRemote == NULL)

      {      

         __leave;

      // Copy the DLL's pathname to the remote process's address space

      if (!WriteProcessMemory(hProcess, pszLibFileRemote, 

         (PVOID) pszLibFile, cb, NULL))

      // Get the real address of LoadLibraryW in Kernel32.dll

      PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)

         GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");

      if (pfnThreadRtn == NULL)

      // Create a remote thread that calls LoadLibraryW(DLLPathname)

      hThread = CreateRemoteThread(hProcess, NULL, 0, 

         pfnThreadRtn, pszLibFileRemote, 0, NULL);

      if (hThread == NULL)

      // Wait for the remote thread to terminate

      WaitForSingleObject(hThread, INFINITE);

      fOk = TRUE; // Everything executed successfully

   }

   __finally { // Now, we can clean everthing up

      // Free the remote memory that contained the DLL's pathname

      if (pszLibFileRemote != NULL) 

         VirtualFreeEx(hProcess, pszLibFileRemote, 0, MEM_RELEASE);

      if (hThread  != NULL) 

         CloseHandle(hThread);

      if (hProcess != NULL) 

         CloseHandle(hProcess);

   return(fOk);

}

BOOL WINAPI InjectLibA(DWORD dwProcessId, PCSTR pszLibFile) 

   // Allocate a (stack) buffer for the Unicode version of the pathname

   PWSTR pszLibFileW = (PWSTR) 

      _alloca((lstrlenA(pszLibFile) + 1) * sizeof(WCHAR));

   // Convert the ANSI pathname to its Unicode equivalent

   wsprintfW(pszLibFileW, L"%S", pszLibFile);

   // Call the Unicode version of the function to actually do the work.

   return(InjectLibW(dwProcessId, pszLibFileW));

BOOL WINAPI EjectLibW(DWORD dwProcessId, PCWSTR pszLibFile) 

   HANDLE hthSnapshot = NULL;

      // Grab a new snapshot of the process

      hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);

      if (hthSnapshot == INVALID_HANDLE_VALUE)

      // Get the HMODULE of the desired library

      MODULEENTRY32W me = { sizeof(me) };

      BOOL fFound = FALSE;

      BOOL fMoreMods = Module32FirstW(hthSnapshot, &me);

      for (; fMoreMods; fMoreMods = Module32NextW(hthSnapshot, &me)) {

         fFound = (lstrcmpiW(me.szModule,  pszLibFile) == 0) || 

                  (lstrcmpiW(me.szExePath, pszLibFile) == 0);

         if (fFound) break;

      if (!fFound)

         PROCESS_CREATE_THREAD     | 

         PROCESS_VM_OPERATION,  // For CreateRemoteThread

      {    

         GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "FreeLibrary");

         pfnThreadRtn, me.modBaseAddr, 0, NULL);

   __finally { // Now we can clean everything up

      if (hthSnapshot != NULL) 

         CloseHandle(hthSnapshot);

      if (hThread     != NULL) 

      if (hProcess    != NULL) 

BOOL WINAPI EjectLibA(DWORD dwProcessId, PCSTR pszLibFile)

   return(EjectLibW(dwProcessId, pszLibFileW));

 上面這種方法注入的代碼它的優點是開發比較簡單,我們隻要用C++寫一個DLL,然後調用InjectLibW(processID, dllName)就可以了,但是因為代碼是運作在一個DLL裡,别人可以通過一些枚舉子產品的工具看到我們的DLL,是以隐蔽性不是很好。

      還有一種遠端線程的實作方法是羅雲彬《

Windows環境下32位彙編語言程式設計

》裡介紹的,

我們不通過DLL,而是直接把可執行代碼拷貝到目标程序後運作,是以它是真正的遠端線程,通過這種方法,我們的代碼和目标程序已經完全融為一體,其他人根本無法察覺。

      用這種方法實作, 它的要點是:

      (1) Kernel32.DLL加載的基址在任何程序裡都是一樣的(其實上一種LoadLibrary方法也用到了這點), 是以GetProcAddress,GetModuleHandleA(W), LoadLibraryA(W)這些API的位址在任何程序裡都是一樣的, 是以我們在其他程序中用和本程序相同的位址調用這些API。

    (2) 因為涉及到全局變量的重定位問題, 是以注入的代碼需要用彙編編寫, 并用以下彙編解決重定位問題