將代碼注入到進程的三種方式

來源:互聯網
上載者:User

[源碼下載]

 

介紹

        在Code Project網站有許多關於password spy的文章,但是這些都是基於Windows Hooks的,還有沒有其他方式能實現這種效果呢?是的,有,不過,先讓我們簡要的回顧一下。

要想讀控制項的內容,一般需要給它發送WM_GETTEXT 訊息.edit控制項就有這個特點。如果edit控制項是在別的進程裡,而且被設定成ES_PASSWORD類型,那麼我們想要達到的目的就不會成功。這種情況下只有擁有password控制項的進程才能實現WM_GETTEXT,所以我們的問題集中在了這一點上:

怎樣才能在外部的進程空間內執行::SendMessage( hPwdEdit, WM_GETTEXT, nMaxChars, psBuffer );

通常有三種方式解決這個問題:

1.把代碼寫到Dll內;然後,通過windows hooks將Dll映射到遠程進程。

2.把代碼寫道Dll內;然後,通過CreateRemoteThread & LoadLibrary 技術將Dll映射到遠程進程。

3.不單獨寫一個DLL;通過WriteProcessMemory將你的代碼直接拷貝到遠程進程。

 

I.Windows Hooks

Windows hools的主要用途是監控一些線程的訊息。

        1.Local hooks,監控屬於你的進程的所有線程訊息。

        2.Remote hooks:

                a.thread-specific,監控屬於其他的進程的線程訊息。

                b.system-width,監控當前運行在系統中的所有線程。

如果hook的線程屬於別的進程,你的hook程式必須放置在Dll內。然後系統將包含hook程式的dll映射到線程的地址空間,Windows映射全部的Dll,不僅僅是hook程式,這就是為什麼Windows hooks可以被用來將代碼注入到其他進程的地址空間中去。

這篇文章裡,我不去深入討論hooks(可以在MSDN內詳細的看一下SetWindowHookEx API函數)我還會給你兩個有用的線索,這在文檔裡是找不到的。

        1.在調用SetWindowHookEx成功後,系統自動將Dll映射到了線程的地址空間,但不會立即執行。因為Windows hooks全部是基於訊息,直到正確的事件返回Dll才被真正映射。例如:

        如果你安裝了一個Hook用來監控一些線程的所有訊息佇列(WH_CALLWNDPROC),在訊息被發送到線程之前DLL不會被映射到進程的地址空間。如果在訊息發送到線程之前,UnhoolWindowsHoolEx被調用,那麼Dll永遠都不會被映射到進程(儘管調用SetWondosHookEx成功了)。要想立即執行,在SetWondosHookEx成功之後立即給線程發送一個合適的event。

        在調用UnhoolWindowsHoolEx取消Dll映射的時候,也不會立即取消DLL映射。

   2.當你安裝Hooks的時候,他們會影響系統的總效能(尤其是system-wide hooks).如果你將thread-specific hooks單獨作為一個Dll映射機制,那麼你很容易就避免這個缺點,看下面的代碼片斷:

 

BOOL APIENTRY DllMain( HANDLE hModule,                       DWORD  ul_reason_for_call,                       LPVOID lpReserved ){    if( ul_reason_for_call == DLL_PROCESS_ATTACH )    {        // Increase reference count via LoadLibrary        char lib_name[MAX_PATH];         ::GetModuleFileName( hModule, lib_name, MAX_PATH );        ::LoadLibrary( lib_name );        // Safely remove hook        ::UnhookWindowsHookEx( g_hHook );    }        return TRUE;}

 

   那麼,發生了什麼了?首先我們通過Windows hooks將Dll映射到遠程進程。然後,在Dll被真正的映射之後,我們Unhook它,正常情況下,Dll將要被取消映射,同時,第一個到達被hook的線程的訊息也被啟用, 我們可以通過LoadLibrary增加Dll的引用計數來阻止取消映射。

   隨之而來的問題是:現在怎麼去卸載Dll,一旦結速了,UnhookWindowsHookEx不會做這些事情的,因為我們已經unhook了線程,可以這樣去做:  

   1.在你想要取消Dll映射之前,安裝另一個Hook。

   2.發送一個特殊的訊息到遠程線程

   3.在你的hook程式中,捕獲訊息,在回複訊息中, 調用FreeLibrary & UnhookWindowsHookEx

 

   現在,hooks只在映射/取消映射的時候使用, 在這期間不會對"hooked"線程產生影響。 在第二部分中我們還會討論比LoadLibrary更好的機制來使Dll映射不影響目標進程。然而,相對於LoadLibrary技術,這種解決方式只能在Winnt和Win9x下運行。

        但是,什麼時候才使用這些竅門呢?當Dll不得不被放到遠程進程中很長時間時(例如,你將一個控制項變成另一個進程的子類),你可能想盡量小的影響目標進程。我不在HookSpy中使用它,因為此時Dll只會注入片刻。

 

II.CreateRemoteThread & LoadLibrary技術

通常,所有的進程都能通過LoadLibrary動態匯入Dll。但是,怎麼才能讓外部進程調用這個函數?回答是用CreateRemoteThread.

首先,讓我們看一下LoadLibrary and FreeLibrary APIs 的聲明:

HINSTANCE LoadLibrary(  LPCTSTR lpLibFileName   // address of filename of library module);BOOL FreeLibrary(  HMODULE hLibModule      // handle to loaded library module);

現在,跟ThreadProc(線程程式,用來傳給CreateRemoteThread)比較一下:

DWORD WINAPI ThreadProc(
  LPVOID lpParameter   // thread data
);

正如你所看到的,所有的函數都使用同樣的調用轉換,都接受一個32位的參數。傳回值的大小也是一樣的。換句話說,我們可以將LoadLibrary/FreeLibrary 作為線程函數傳遞給CreateRemoteThread.

 

然而,還有兩個問題:

    1.CreateRemoteThread 裡的lpStartAddress參數必須是遠程進程的線程程式的首地址。

         2.如果lpParameter(傳遞給ThreadFunc)被認為是一個普通的32位值,(FreeLibrary 認為它是一個HMODULE),所有事情就好說了,然而,如果lpParameter 被描述成一個指標,(LoadLibraryA 將它描述成一個指向字串的指標),那麼它就必須指向遠程進程的一些資料。

第二個問題好解決,用WriteProcessMemory拷貝Dll的模組名(需要LoadLibrary)。

 

下面是使用CreateRemoteThread & LoadLibrary 技術的步驟:

1.得到遠程進程的一個控制代碼(OpenPreocess).

2.在遠程進程裡給Dll名稱分配記憶體(VirtualAllocEx).

3.將Dll名稱,包括全路徑,寫到分配的記憶體裡(WritePreocessMemory).

4.通過CreateRemoteThread & LoadLibrary將你的Dll映射到遠程進程.

5.等待遠程進程結束(WaitForSingleObject);也就是等到LoadLibrary返回.當我們的DllMain返回的時候線程就結束了(調用原因DLL_PROCESS_ATTACH).

6.返回遠程線程的結束碼,(GetExitCodeThread).注意這個值是LoadLibrary的傳回值,

7.釋放在步驟#2分配的記憶體(VirtualFreeEx).

8.從遠程進程卸載DLL,將#6得到的返回控制代碼HMODULE傳遞給FreeLibrary(lpParameter in CreateRemoteThread).

  注意:如果你將Dll注入任何新的線程,確保在卸載的時候全部結束。

9.等待線程結束(WaitForSingleObject).

 

而且,一旦完成,別忘了關閉所有的控制代碼,對於在#4和#8建立的線程和在步驟#1返回的遠程進程的控制代碼。

HANDLE hThread;char szLibPath[_MAX_PATH]; // The name of our "LibSpy.dll" module // (including full path!);void* pLibRemote; // The address (in the remote process) where // szLibPath will be copied to;DWORD hLibModule; // Base address of loaded module (==HMODULE);HMODULE hKernel32 = ::GetModuleHandle("Kernel32");// initialize szLibPath//...// 1. Allocate memory in the remote process for szLibPath// 2. Write szLibPath to the allocated memorypLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath), MEM_COMMIT, PAGE_READWRITE );::WriteProcessMemory( hProcess, pLibRemote, (void*)szLibPath, sizeof(szLibPath), NULL );// Load "LibSpy.dll" into the remote process// (via CreateRemoteThread & LoadLibrary)hThread = ::CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32, "LoadLibraryA" ), pLibRemote, 0, NULL );::WaitForSingleObject( hThread, INFINITE );// Get handle of the loaded module::GetExitCodeThread( hThread, &hLibModule );// Clean up::CloseHandle( hThread );::VirtualFreeEx( hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE );
// Unload "LibSpy.dll" from the target process// (via CreateRemoteThread & FreeLibrary)hThread = ::CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32, "FreeLibrary" ), (void*)hLibModule, 0, NULL );::WaitForSingleObject( hThread, INFINITE );// Clean up::CloseHandle( hThread );

III.The CreateRemoteThread & WriteProcessMemory 技術

   這種方法是將代碼拷貝到要注入進程的地址空間,然後在進程的上下文中執行這些代碼,包括遠程線程的使用。這就不必再使用分離的DLL了。可以用API函數WriteProcessMemory將代碼直接拷貝到遠程地址空間,用CreateRemoteThread啟動代碼執行。   

   先看一個CreateRemoteThread的聲明       

HANDLE CreateRemoteThread( HANDLE hProcess, // handle to process to create thread in LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to security // attributes DWORD dwStackSize, // initial thread stack size, in bytes LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread // function LPVOID lpParameter, // argument for new thread DWORD dwCreationFlags, // creation flags LPDWORD lpThreadId // pointer to returned thread identifier);

 

   用它和CreateThread的聲明相比,有下面幾點不同:

  • hProcess參數在CreateRemoteThread內是多加的。它是建立線程的進程控制代碼。
  • lpStartAddress參數是CreateRemoteThread 內描述遠程地址空間內的線程的起始地址。函數必須存在於遠程空間內。我們首先必須將代碼拷貝到遠程空間。
  • 同樣的, lpParameter指標必須存在於遠程空間, 所以我們也要將它拷貝到那兒。  

    現在我們將這種技術總結為如下幾步:

    1.取得遠程空間的控制代碼(OpenProcess).

    2.在遠程地址空間為注入的資料分配記憶體(VirtualAllocEx)

    3.將自訂結構INJDATA拷貝到分配的記憶體裡(WriteProcessMemory)

    4.在遠程地址空間為注入的代碼分配記憶體。

    5.將ThreadFunc拷貝到分配的記憶體裡。

    6.通過CreateRemoteThread啟動遠端ThreadFunc。

    7.等待遠程線程的結束(WaitForSingleObject). 

    8.從遠程進程返回結果(ReadProcessMemory or GetExitCodeThread). 

    9.釋放在步驟2#和步驟#4分配的記憶體(VirtualFreeEx);

    10.關閉在步驟#1和步驟#6返回的控制代碼(CloseHandle).

 

   ThreadFunc必須服從下面的要求:

    1.ThreadFunc 只能調用kernel32.dll 和 user32.dll內部的函數,只有kernel32.dll 和 user32.dll(注意user32沒有被映射的每一個win32進程)能保證本地和目標進程有同樣的載入地址。如果你需要其他庫中的函數,將LoadLibrary and GetProcAddress的地址傳入注入代碼,如果有其他有爭議的DLL已經被映射到了進程空間你也可以用GetModuleHandle 代替of LoadLibrary,

    同樣的,如果你想從ThreadFunc內調用你自己的子程式。可以將地址先通過INJDATA傳入到ThreadFunc,再經過ThreadFunc載入到遠程空間。

    2.不要使用任何靜態字串,最好將所有的字串通過INJDATA傳遞給ThreadFunc.

      為什麼呢?編譯器將所有的靜態字串放入到了可執行檔的".data"段,並且只是在代碼內引用。這樣的話,遠程空間中的ThreadFunc的拷貝將指向一些不存在的地址,(最少不在它的地址空間內).

    3.將/GZ編譯開關關掉,在debug裡它是預設的。

    4.要不將ThreadFunc and AfterThreadFunc設為靜態,要不取消增量連結。

    5.在ThreadFunc內本地變數必須小於page-worth(4kb)。注意在debug中有效4kb中的大約10位元組是內部變數使用的。

    6.如果switch塊大於3個case,就要把它象下面這樣分開。

      或者改用if-else if

switch( expression ) {
    case constant1: statement1; goto END;
    case constant2: statement2; goto END;
    case constant3: statement2; goto END;
}
switch( expression ) {
    case constant4: statement4; goto END;
    case constant5: statement5; goto END;
    case constant6: statement6; goto END;
}
END:

如果不使用這些規則,很可能使目標進程崩潰,記住,不要將目標進程的空間當成你自己進程的空間。

 

 

[源址]http://www.codeproject.com/KB/threads/winspy.aspx

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.