[程序設計]進(jìn)程注入的三種方法!

3年前 (2021-08-28)閱讀907回復0
褚雅琴
褚雅琴
  • 管理員
  • 發(fā)消息
  • 注冊排名1322
  • 經(jīng)驗值80
  • 級別管理員
  • 主題16
  • 回復0
樓主
印刷廠(chǎng)直印加工●彩頁(yè)1000張只需要69元●名片5元每盒-更多產(chǎn)品印刷報價(jià)?聯(lián)系電話(huà):138-1621-1622(微信同號)

一般來(lái)說(shuō)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,這個(gè)問(wèn)題有三種可能的解決方案:

  1. 把你的代碼放到一個(gè)DLL中;然后用 windows 鉤子把它映射到遠程進(jìn)程AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  2. 把你的代碼放到一個(gè)DLL中;然后用 CreateRemoteThread 和 LoadLibrary 把它映射到遠程進(jìn)程AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  3. 不用DLL,直接復制你的代碼到遠程進(jìn)程(使用WriteProcessMemory)并且用CreateRemoteThread執行之AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。在這里有詳細的說(shuō)明:

 ?、? Windows 鉤子

  示例程序:HookSpy 和 HookInjEx

  Windows鉤子的主要作用就是監視某個(gè)線(xiàn)程的消息流動(dòng)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。一般可分為:

  1. 局部鉤子,只監視你自己進(jìn)程中某個(gè)線(xiàn)程的消息流動(dòng)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  2. 遠程鉤子AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,又可以分為:

  a. 特定線(xiàn)程的AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,監視別的進(jìn)程中某個(gè)線(xiàn)程的消息;

  b. 系統級的,監視整個(gè)系統中正在運行的所有線(xiàn)程的消息AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   如果被掛鉤(監視)的線(xiàn)程屬于別的進(jìn)程(情況2a和2b),你的鉤子過(guò)程(hook procedure)必須放在一個(gè)動(dòng)態(tài)連接庫(DLL)中AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。系統把這包含了鉤子過(guò)程的DLL映射到被掛鉤的線(xiàn)程的地址空間。Windows會(huì )映射整個(gè)DLL而不僅僅是你的鉤子過(guò)程。這就是為什么windows鉤子可以用來(lái)向其他線(xiàn)程的地址空間注入代碼的原因了。

   在這里我不想深入討論鉤子的問(wèn)題(請看MSDN中對SetWindowsHookEx的說(shuō)明)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,讓我再告訴你兩個(gè)文檔中找不到的訣竅,可能會(huì )有用:

  1. 當SetWindowHookEx調用成功后,系統會(huì )自動(dòng)映射這個(gè)DLL到被掛鉤的線(xiàn)程,但并不是立即映射AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。因為所有的Windows鉤子都是基于消息的,直到一個(gè)適當的事件發(fā)生后這個(gè)DLL才被映射。比如:

  如果你安裝了一個(gè)監視所有未排隊的(nonqueued)的消息的鉤子(WH_CALLWNDPROC),只有一個(gè)消息發(fā)送到被掛鉤線(xiàn)程(的某個(gè)窗口)后這個(gè)DLL才被映射AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。也就是說(shuō),如果在消息發(fā)送到被掛鉤線(xiàn)程之前調用了UnhookWindowsHookEx那么這個(gè)DLL就永遠不會(huì )被映射到該線(xiàn)程(雖然SetWindowsHookEx調用成功了)。為了強制映射,可以在調用SetWindowsHookEx后立即發(fā)送一個(gè)適當的消息到那個(gè)線(xiàn)程。

   同理,調用UnhookWindowsHookEx之后,只有特定的事件發(fā)生后DLL才真正地從被掛鉤線(xiàn)程卸載AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  2. 當你安裝了鉤子后,系統的性能會(huì )受到影響(特別是系統級的鉤子)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。然而如果你只是使用的特定線(xiàn)程的鉤子來(lái)映射DLL而且不截獲如何消息的話(huà),這個(gè)缺陷也可以輕易地避免。看一下下面的代碼片段:

  BOOL APIENTRY DllMain( HANDLE hModule,

   DWORD ul_reason_for_call,

   LPVOID lpReserved )

   if( ul_reason_for_call == DLL_PROCESS_ATTACH )

   //用 LoadLibrary增加引用次數

   char lib_name[MAX_PATH];

   ::GetModuleFileName( hModule, lib_name, MAX_PATH );

   ::LoadLibrary( lib_name );

   // 安全卸載鉤子

   ::UnhookWindowsHookEx( g_hHook );

   return TRUE;

   我們來(lái)看一下AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。首先,我們用鉤子映射這個(gè)DLL到遠程線(xiàn)程,然后,在DLL被真正映射進(jìn)去后,我們立即卸載掛鉤(unhook)。一般來(lái)說(shuō)當第一個(gè)消息到達被掛鉤線(xiàn)程后,這DLL會(huì )被卸載,然而我們通過(guò)LoadLibrary來(lái)增加這個(gè)DLL的引用次數,避免了DLL被卸載。

   剩下的問(wèn)題是:使用完畢后如何卸載這個(gè)DLL?UnhookWindowsHookEx不行了,因為我們已經(jīng)對那個(gè)線(xiàn)程取消掛鉤(unhook)了AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。你可以這么做:

   ○在你想要卸載這個(gè)DLL之前再安裝一個(gè)鉤子;

   ○發(fā)送一個(gè)“特殊”的消息到遠程線(xiàn)程;

   ○在你的新鉤子的鉤子過(guò)程(hook procedure)中截獲該消息,調用FreeLibrary 和 (譯者注:對新鉤子調用)UnhookwindowsHookExAI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  現在,鉤子只在映射DLL到遠程進(jìn)程和從遠程進(jìn)程卸載DLL時(shí)使用,對被掛鉤線(xiàn)程的性能沒(méi)有影響AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。也就是說(shuō),我們找到了一種(相比第二部分討論的LoadLibrary技術(shù))WinNT和Win9x下都可以使用的,不影響目的進(jìn)程性能的DLL映射機制。

   但是,我們應該在何種情況下使用該技巧呢?通常是在DLL需要在遠程進(jìn)程中駐留較長(cháng)時(shí)間(比如你要子類(lèi)[subclass]另一個(gè)進(jìn)程中的控件)并且你不想過(guò)于干涉目的進(jìn)程時(shí)比較適合使用這種技巧AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。我在HookSpy中并沒(méi)有使用它,因為那個(gè)DLL只是短暫地注入一段時(shí)間――只要能取得密碼就足夠了。我在另一個(gè)例子HookInjEx中演示了這種方法。HookInjEx把一個(gè)DLL映射進(jìn)“explorer.exe”(當然,最后又從其中卸載),子類(lèi)了其中的開(kāi)始按鈕,更確切地說(shuō)我是把開(kāi)始按鈕的鼠標左右鍵點(diǎn)擊事件顛倒了一下。

   你可以在本文章的開(kāi)頭部分找到HookSpy和HookInjEx及其源代碼的下載包鏈接AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

 ?、? CreateRemoteThread 和 LoadLibrary 技術(shù)

  示例程序:LibSpy

   通常,任何進(jìn)程都可以通過(guò)LoadLibrary動(dòng)態(tài)地加載DLL,但是我們如何強制一個(gè)外部進(jìn)程調用該函數呢?答案是CreateRemoteThreadAI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  讓我們先來(lái)看看LoadLibrary和FreeLibrary的函數聲明:

  HINSTANCE LoadLibrary(

   LPCTSTR lpLibFileName // address of filename of library module

  BOOL FreeLibrary(

   HMODULE hLibModule // handle to loaded library module

  再和CreateRemoteThread的線(xiàn)程過(guò)程(thread procedure)ThreadProc比較一下:

  DWORD WINAPI ThreadProc(

   LPVOID lpParameter // thread data

   你會(huì )發(fā)現所有的函數都有同樣的調用約定(calling convention)、都接受一個(gè)32位的參數并且返回值類(lèi)型的大小也一樣AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。也就是說(shuō),我們可以把LoadLibrary/FreeLibrary的指針作為參數傳遞給CrateRemoteThread。

   然而AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,還有兩個(gè)問(wèn)題(參考下面對CreateRemoteThread的說(shuō)明)

   1. 傳遞給ThreadProc的lpStartAddress 參數必須為遠程進(jìn)程中的線(xiàn)程過(guò)程的起始地址AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   2. 如果把ThreadProc的lpParameter參數當做一個(gè)普通的32位整數(FreeLibrary把它當做HMODULE)那么沒(méi)有如何問(wèn)題,但是如果把它當做一個(gè)指針(LoadLibrary把它當做一個(gè)char*),它就必須指向遠程進(jìn)程中的內存數據AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   第一個(gè)問(wèn)題其實(shí)已經(jīng)迎刃而解了,因為L(cháng)oadLibrary和FreeLibrary都是存在于kernel32.dll中的函數,而kernel32可以保證任何“正?!边M(jìn)程中都存在,且其加載地址都是一樣的AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。(參看附錄A)于是LoadLibrary/FreeLibrary在任何進(jìn)程中的地址都是一樣的,這就保證了傳遞給遠程進(jìn)程的指針是個(gè)有效的指針。

   第二個(gè)問(wèn)題也很簡(jiǎn)單:把DLL的文件名(LodLibrary的參數)用WriteProcessMemory復制到遠程進(jìn)程AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   所以AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,使用CreateRemoteThread和LoadLibrary技術(shù)的步驟如下:

   1. 得到遠程進(jìn)程的HANDLE(使用OpenProcess)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   2. 在遠程進(jìn)程中為DLL文件名分配內存(VirtualAllocEx)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   3. 把DLL的文件名(全路徑)寫(xiě)到分配的內存中(WriteProcessMemory)

   4. 使用CreateRemoteThread和LoadLibrary把你的DLL映射近遠程進(jìn)程AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   5. 等待遠程線(xiàn)程結束(WaitForSingleObject),即等待LoadLibrary返回AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。也就是說(shuō)當我們的DllMain(是以DLL_PROCESS_ATTACH為參數調用的)返回時(shí)遠程線(xiàn)程也就立即結束了。

   6. 取回遠程線(xiàn)程的結束碼(GetExitCodeThtread),即LoadLibrary的返回值――我們DLL加載后的基地址(HMODULE)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   7. 釋放第2步分配的內存(VirtualFreeEx)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   8. 用CreateRemoteThread和FreeLibrary把DLL從遠程進(jìn)程中卸載AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。調用時(shí)傳遞第6步取得的HMODULE給FreeLibrary(通過(guò)CreateRemoteThread的lpParameter參數)。

   9. 等待線(xiàn)程的結束(WaitSingleObject)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   同時(shí),別忘了在最后關(guān)閉所有的句柄:第4、8步得到的線(xiàn)程句柄,第1步得到的遠程進(jìn)程句柄AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   現在我們看看LibSpy的部分代碼,分析一下以上的步驟是任何實(shí)現的AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。為了簡(jiǎn)單起見(jiàn),沒(méi)有包含錯誤處理和支持Unicode的代碼。

  HANDLE hThread;

  char szLibPath[_MAX_PATH]; // "LibSpy.dll"的文件名

   // (包含全路徑!);

  void* pLibRemote; // szLibPath 將要復制到地址

  DWORD hLibModule; //已加載的DLL的基地址(HMODULE);

  HMODULE hKernel32 = ::GetModuleHandle("Kernel32");

  //初始化 szLibPath

  // 1. 在遠程進(jìn)程中為szLibPath 分配內存

  // 2. 寫(xiě)szLibPath到分配的內存

  pLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath),

   MEM_COMMIT, PAGE_READWRITE );

  ::WriteProcessMemory( hProcess, pLibRemote, (void*)szLibPath,

   sizeof(szLibPath), NULL );

  // 加載 "LibSpy.dll" 到遠程進(jìn)程

  // (通過(guò) CreateRemoteThread LoadLibrary)

  hThread = ::CreateRemoteThread( hProcess, NULL, 0,

   (LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32,

   "LoadLibraryA" ),

   pLibRemote, 0, NULL );

  ::WaitForSingleObject( hThread, INFINITE );

  //取得DLL的基地址

  ::GetExitCodeThread( hThread, hLibModule );

  //掃尾工作

  ::CloseHandle( hThread );

  ::VirtualFreeEx( hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE );

  我們放在DllMain中的真正要注入的代碼(比如為SendMessage)現在已經(jīng)被執行了(由于DLL_PROCESS_ATTACH),所以現在可以把DLL從目的進(jìn)程中卸載了AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  // 從目標進(jìn)程卸載LibSpu.dll

  // (通過(guò) CreateRemoteThread FreeLibrary)

  hThread = ::CreateRemoteThread( hProcess, NULL, 0,

   (LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32,

   "FreeLibrary" ),

   (void*)hLibModule, 0, NULL );

  ::WaitForSingleObject( hThread, INFINITE );

  // 掃尾工作

  ::CloseHandle( hThread );

  進(jìn)程間通訊

   到目前為止,我們僅僅討論了任何向遠程進(jìn)程注入DLL,然而,在多數情況下被注入的DLL需要和你的程序以某種方式通訊(記住,那個(gè)DLL是被映射到遠程進(jìn)程中的,而不是在你的本地程序中!)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。以密碼間諜為例:那個(gè)DLL需要知道包含了密碼的的控件的句柄。很明顯,這個(gè)句柄是不能在編譯期間硬編碼(hardcoded)進(jìn)去的。同樣,當DLL得到密碼后,它也需要把密碼發(fā)回我們的程序。

   幸運的是,這個(gè)問(wèn)題有很多種解決方案:文件映射(Mapping),WM_COPYDATA,剪貼板等AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。還有一種非常便利的方法#pragma data_seg。這里我不想深入討論因為它們在MSDN(看一下Interprocess Communications部分)或其他資料中都有很好的說(shuō)明。我在LibSpy中使用的是#pragma data_seg。

   你可以在本文章的開(kāi)頭找到LibSpy及源代碼的下載鏈接AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

 ?、?CreateRemoteThread和WriteProcessMemory技術(shù)

  示例程序:WinSpy

   另一種注入代碼到其他進(jìn)程地址空間的方法是使用WriteProcessMemory APIAI無(wú)法打開(kāi)插圖 插圖包含非法操作數。這次你不用編寫(xiě)一個(gè)獨立的DLL而是直接復制你的代碼到遠程進(jìn)程(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相比AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,有一下不同:

  ●增加了hProcess參數AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。這是要在其中創(chuàng )建線(xiàn)程的進(jìn)程的句柄。

  ●CreateRemoteThread的lpStartAddress參數必須指向遠程進(jìn)程的地址空間中的函數AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。這個(gè)函數必須存在于遠程進(jìn)程中,所以我們不能簡(jiǎn)單地傳遞一個(gè)本地ThreadFucn的地址,我們必須把代碼復制到遠程進(jìn)程。

  ●同樣,lpParameter參數指向的數據也必須存在于遠程進(jìn)程中,我們也必須復制它AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   現在A(yíng)I無(wú)法打開(kāi)插圖 插圖包含非法操作數,我們總結一下使用該技術(shù)的步驟:

   1. 得到遠程進(jìn)程的HANDLE(OpenProcess)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   2. 在遠程進(jìn)程中為要注入的數據分配內存(VirtualAllocEx)、

   3. 把初始化后的INJDATA結構復制到分配的內存中(WriteProcessMemory)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   4. 在遠程進(jìn)程中為要注入的數據分配內存(VirtualAllocEx)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   5. 把ThreadFunc復制到分配的內存中(WriteProcessMemory)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   6. 用CreateRemoteThread啟動(dòng)遠程的ThreadFuncAI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   7. 等待遠程線(xiàn)程的結束(WaitForSingleObject)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   8. 從遠程進(jìn)程取回指執行結果(ReadProcessMemory 或 GetExitCodeThread)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   9. 釋放第2、4步分配的內存(VirtualFreeEx)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   10. 關(guān)閉第6、1步打開(kāi)打開(kāi)的句柄AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   另外AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,編寫(xiě)ThreadFunc時(shí)必須遵守以下規則:

   1. ThreadFunc不能調用除kernel32.dll和user32.dll之外動(dòng)態(tài)庫中的API函數AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。只有kernel32.dll和user32.dll(如果被加載)可以保證在本地和目的進(jìn)程中的加載地址是一樣的。(注意:user32并不一定被所有的Win32進(jìn)程加載!)參考附錄A。如果你需要調用其他庫中的函數,在注入的代碼中使用LoadLibrary和GetProcessAddress強制加載。如果由于某種原因,你需要的動(dòng)態(tài)庫已經(jīng)被映射進(jìn)了目的進(jìn)程,你也可以使用GetMoudleHandle代替LoadLibrary。同樣,如果你想在ThreadFunc中調用你自己的函數,那么就分別復制這些函數到遠程進(jìn)程并通過(guò)INJDATA把地址提供給ThreadFunc。

   2. 不要使用static字符串AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。把所有的字符串提供INJDATA傳遞。為什么?編譯器會(huì )把所有的靜態(tài)字符串放在可執行文件的“.data”段,而僅僅在代碼中保留它們的引用(即指針)。這樣,遠程進(jìn)程中的ThreadFunc就會(huì )執行不存在的內存數據(至少沒(méi)有在它自己的內存空間中)。

   3. 去掉編譯器的/GZ編譯選項AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。這個(gè)選項是默認的(看附錄B)。

   4. 要么把ThreadFunc和AfterThreadFunc聲明為static,要么關(guān)閉編譯器的“增量連接(incremental linking)”(看附錄C)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   5. ThreadFunc中的局部變量總大小必須小于4k字節(看附錄D)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。注意,當degug編譯時(shí),這4k中大約有10個(gè)字節會(huì )被事先占用。

   6. 如果有多于3個(gè)switch分支的case語(yǔ)句AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,必須像下面這樣分割開(kāi),或用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:

 ?。▍⒖几戒汦)

   如果你不按照這些游戲規則玩的話(huà)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,你注定會(huì )使目的進(jìn)程掛掉!記住,不要妄想遠程進(jìn)程中的任何數據會(huì )和你本地進(jìn)程中的數據存放在相同內存地址!(參看附錄F)

 ?。ㄔ?huà)如此:You will almost certainly crash the target process if you don't play by those rules. Just remember: Don't assume anything in the target process is at the same address as it is in your process.)

  GetWindowTextRemote(A/W)

   所有取得遠程edit中文本的工作都被封裝進(jìn)這個(gè)函數:GetWindowTextRemote(A/W):

  int GetWindowTextRemoteA( HANDLE hProcess, HWND hWnd, LPSTR lpString );

  int GetWindowTextRemoteW( HANDLE hProcess, HWND hWnd, LPWSTR lpString );

  參數:

  hProcess

   目的edit所在的進(jìn)程句柄

  hWnd

   目的edit的句柄

  lpString

   接收字符串的緩沖

  返回值:

   成功復制的字符數AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   讓我們看以下它的部分代碼,特別是注入的數據和代碼AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。為了簡(jiǎn)單起見(jiàn),沒(méi)有包含支持Unicode的代碼。

  INJDATA

  typedef LRESULT (WINAPI *SENDMESSAGE)(HWND,UINT,WPARAM,LPARAM);

  typedef struct {

   HWND hwnd; // handle to edit control

   SENDMESSAGE fnSendMessage; // pointer to user32!SendMessageA

   char psText[128]; // buffer that is to receive the password

  } INJDATA;

   INJDATA是要注入遠程進(jìn)程的數據AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。在把它的地址傳遞給SendMessageA之前,我們要先對它進(jìn)行初始化。幸運的是unse32.dll在所有的進(jìn)程中(如果被映射)總是被映射到相同的地址,所以SendMessageA的地址也總是相同的,這也保證了傳遞給遠程進(jìn)程的地址是有效的。

  ThreadFunc

  static DWORD WINAPI ThreadFunc (INJDATA *pData)

   pData-fnSendMessage( pData-hwnd, WM_GETTEXT, // 得到密碼

   sizeof(pData-psText),

   (LPARAM)pData-psText );

   return 0;

  // This function marks the memory address after ThreadFunc.

  // int cbCodeSize = (PBYTE) AfterThreadFunc - (PBYTE) ThreadFunc.

  static void AfterThreadFunc (void)

  ThreadFunc是遠程線(xiàn)程實(shí)際執行的代碼AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   ●注意AfterThreadFunc是如何計算ThreadFunc的代碼大小的AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。一般地,這不是最好的辦法,因為編譯器會(huì )改變你的函數中代碼的順序(比如它會(huì )把ThreadFunc放在A(yíng)fterThreadFunc之后)。然而,你至少可以確定在同一個(gè)工程中,比如在我們的WinSpy工程中,你函數的順序是固定的。如果有必要,你可以使用/ORDER連接選項,或者,用反匯編工具確定ThreadFunc的大小,這個(gè)也許會(huì )更好。

  如何用該技術(shù)子類(lèi)(subclass)一個(gè)遠程控件

  示例程序:InjectEx

   讓我們來(lái)討論一個(gè)更復雜的問(wèn)題:如何子類(lèi)屬于其他進(jìn)程的一個(gè)控件AI無(wú)法打開(kāi)插圖 插圖包含非法操作數?

   首先AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,要完成這個(gè)任務(wù),你必須復制兩個(gè)函數到遠程進(jìn)程:

   1. ThreadFuncAI無(wú)法打開(kāi)插圖 插圖包含非法操作數,這個(gè)函數通過(guò)調用SetWindowLong API來(lái)子類(lèi)遠程進(jìn)程中的控件,

   2. NewProc, 那個(gè)控件的新窗口過(guò)程(Window Procedure)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   然而,最主要的問(wèn)題是如何傳遞數據到遠程的NewProcAI無(wú)法打開(kāi)插圖 插圖包含非法操作數。因為NewProc是一個(gè)回調(callback)函數,它必須符合特定的要求(譯者注:這里指的主要是參數個(gè)數和類(lèi)型),我們不能再簡(jiǎn)單地傳遞一個(gè)INJDATA的指針作為它的參數。幸運的我已經(jīng)找到解決這個(gè)問(wèn)題的方法,而且是兩個(gè),但是都要借助于匯編語(yǔ)言。我一直都努力避免使用匯編,但是這一次,我們逃不掉了,沒(méi)有匯編不行的。

  解決方案1

  看下面的圖片:

   不知道你是否注意到了,INJDATA緊挨著(zhù)NewProc放在NewProc的前面?這樣的話(huà)在編譯期間NewProc就可以知道INJDATA的內存地址AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。更精確地說(shuō),它知道INJDATA相對于它自身地址的相對偏移,但是這并不是我們真正想要的。現在,N(xiāo)ewProc看起來(lái)是這個(gè)樣子:

  static LRESULT CALLBACK NewProc(

   HWND hwnd, // handle to window

   UINT uMsg, // message identifier

   WPARAM wParam, // first message parameter

   LPARAM lParam ) // second message parameter

   INJDATA* pData = (INJDATA*) NewProc; // pData 指向

   // NewProc;

   pData--; // 現在pData指向INJDATA;

   // 記住AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,INJDATA 在遠程進(jìn)程中剛好位于

   // NewProc的緊前面;

   // 子類(lèi)代碼

   //調用用來(lái)的的窗口過(guò)程;

   // fnOldProc (由SetWindowLong返回) 是被ThreadFunc(遠程進(jìn)程中的)初始化

   // 并且存儲在遠程進(jìn)程中的INJDATA里的;

   return pData-fnCallWindowProc( pData-fnOldProc,

   hwnd,uMsg,wParam,lParam );

   然而AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,還有一個(gè)問(wèn)題,看第一行:

  INJDATA* pData = (INJDATA*) NewProc;

   pData被硬編碼為我們進(jìn)程中NewProc的地址,但這是不對的AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。因為NewProc會(huì )被復制到遠程進(jìn)程,那樣的話(huà),這個(gè)地址就錯了。

   用C/C++沒(méi)有辦法解決這個(gè)問(wèn)題,可以用內聯(lián)的匯編來(lái)解決AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。看修改后的NewProc:

  static LRESULT CALLBACK NewProc(

   HWND hwnd, // handle to window

   UINT uMsg, // message identifier

   WPARAM wParam, // first message parameter

   LPARAM lParam ) // second message parameter

   // 計算INJDATA 的地址;

   // 在遠程進(jìn)程中AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,INJDATA剛好在

   //NewProc的前面;

   INJDATA* pData;

   _asm {

   call dummy

  dummy:

   pop ecx // - ECX 中存放當前的EIP

   sub ecx, 9 // - ECX 中存放NewProc的地址

   mov pData, ecx

   pData--;

   // 子類(lèi)代碼

   // 調用原來(lái)的窗口過(guò)程

   return pData-fnCallWindowProc( pData-fnOldProc,

   hwnd,uMsg,wParam,lParam );

   這是什么意思?每個(gè)進(jìn)程都有一個(gè)特殊的寄存器,這個(gè)寄存器指向下一條要執行的指令的內存地址,即32位Intel和AMD處理器上所謂的EIP寄存器AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。因為EIP是個(gè)特殊的寄存器,所以你不能像訪(fǎng)問(wèn)通用寄存器(EAX,EBX等)那樣來(lái)訪(fǎng)問(wèn)它。換句話(huà)說(shuō),你找不到一個(gè)可以用來(lái)尋址EIP并且對它進(jìn)行讀寫(xiě)的操作碼(OpCode)。然而,EIP同樣可以被JMP,CALL,RET等指令隱含地改變(事實(shí)上它一直都在改變)。讓我們舉例說(shuō)明32位的Intel和AMD處理器上CALL/RET是如何工作的吧:

   當我們用CALL調用一個(gè)子程序時(shí),這個(gè)子程序的地址被加載進(jìn)EIPAI無(wú)法打開(kāi)插圖 插圖包含非法操作數。同時(shí),在EIP被改變之前,它以前的值會(huì )被自動(dòng)壓棧(在后來(lái)被用作返回指令指針[return instruction-pointer])。在子程序的最后RET指令自動(dòng)把這個(gè)值從棧中彈出到EIP。

   現在我們知道了如何通過(guò)CALL和RET來(lái)修改EIP的值了AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,但是如何得到他的當前值?

  還記得CALL把EIP的值壓棧了嗎?所以為了得到EIP的值我們調用了一個(gè)“假(dummy)函數”然后彈出棧頂值AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。看一下編譯過(guò)的NewProc:

  Address OpCode/Params Decoded instruction

  :00401000 55 push ebp ; entry point of

   ; NewProc

  :00401001 8BEC mov ebp, esp

  :00401003 51 push ecx

  :00401004 E800000000 call 00401009 ; *a* call dummy

  :00401009 59 pop ecx ; *b*

  :0040100A 83E909 sub ecx, 00000009 ; *c*

  :0040100D 894DFC mov [ebp-04], ecx ; mov pData, ECX

  :00401010 8B45FC mov eax, [ebp-04]

  :00401013 83E814 sub eax, 00000014 ; pData--;

  :0040102D 8BE5 mov esp, ebp

  :0040102F 5D pop ebp

  :00401030 C21000 ret 0010

   a. 一個(gè)假的函數調用;僅僅跳到下一條指令并且(譯者注:更重要的是)把EIP壓棧AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   b. 彈出棧頂值到ECXAI無(wú)法打開(kāi)插圖 插圖包含非法操作數。ECX就保存的EIP的值;這也就是那條“pop ECX”指令的地址。

   c. 注意從NewProc的入口點(diǎn)到“pop ECX”指令的“距離”為9字節;因此把ECX減去9就得到的NewProc的地址了AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   這樣一來(lái),不管被復制到什么地方,N(xiāo)ewProc總能正確計算自身的地址了!然而,要注意從NewProc的入口點(diǎn)到“pop ECX”的距離可能會(huì )因為你的編譯器/鏈接選項的不同而不同,而且在Release和Degub版本中也是不一樣的AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。但是,不管怎樣,你仍然可以在編譯期知道這個(gè)距離的具體值。

   1. 首先,編譯你的函數AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   2. 在反匯編器(disassembler)中查出正確的距離值AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   3. 最后,使用正確的距離值重新編譯你的程序AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   這也是InjectEx中使用的解決方案AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。InjectEx和HookInjEx類(lèi)似,交換開(kāi)始按鈕上的鼠標左右鍵點(diǎn)擊事件。

  解決方案2

   在遠程進(jìn)程中把INJDATA放在NewProc的前面并不是唯一的解決方案AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。看一下下面的NewProc:

  static LRESULT CALLBACK NewProc(

   HWND hwnd, // handle to window

   UINT uMsg, // message identifier

   WPARAM wParam, // first message parameter

   LPARAM lParam ) // second message parameter

   INJDATA* pData = 0xA0B0C0D0; // 一個(gè)假值

   // 子類(lèi)代碼

   // 調用以前的窗口過(guò)程

   return pData-fnCallWindowProc( pData-fnOldProc,

   hwnd,uMsg,wParam,lParam );

   這里,0XA0B0C0D0僅僅是INJDATA在遠程進(jìn)程中的地址的占位符(placeholder)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。你無(wú)法在編譯期得到這個(gè)值,然而你在調用VirtualAllocEx(為INJDATA分配內存時(shí))后確實(shí)知道INJDATA的地址!(譯者注:就是VirtualAllocEx的返回值)

   我們的NewProc編譯后大概是這個(gè)樣子:

  Address OpCode/Params Decoded instruction

  :00401000 55 push ebp

  :00401001 8BEC mov ebp, esp

  :00401003 C745FCD0C0B0A0 mov [ebp-04], A0B0C0D0

  :0040100A ...

  :0040102D 8BE5 mov esp, ebp

  :0040102F 5D pop ebp

  :00401030 C21000 ret 0010

   編譯后的機器碼應該為:558BECC745FCD0C0B0A0......8BE55DC21000AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   現在A(yíng)I無(wú)法打開(kāi)插圖 插圖包含非法操作數,你這么做:

   1. 把INJDATA,ThreadFunc和NewFunc復制到目的進(jìn)程AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   2. 改變NewPoc的機器碼,讓pData指向INJDATA的真實(shí)地址AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   比如AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,假設INJDATA的的真實(shí)地址(VirtualAllocEx的返回值)為0x008a0000,你把NewProc的機器碼改為:

  558BECC745FCD0C0B0A0......8BE55DC21000 - 修改前的 NewProc 1

  558BECC745FC00008A00......8BE55DC21000 - 修改后的 NewProc

   也就是說(shuō)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,你把假值 A0B0C0D0改為INJDATA的真實(shí)地址2

   3. 開(kāi)始指向遠程的ThreadFunc,它子類(lèi)了遠程進(jìn)程中的控件AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   sup1; 你可能會(huì )問(wèn),為什么A0B0C0D0和008a0000在編譯后的機器碼中為逆序的AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。這時(shí)因為Intel和AMD處理器使用littl-endian標記法(little-endian notation)來(lái)表示它們的(多字節)數據。換句話(huà)說(shuō):一個(gè)數的低字節(low-order byte)在內存中被存放在最低位,高字節(high-order byte)存放在最高位。

  想像一下,存放在四個(gè)字節中的單詞“UNIX”,在big-endia系統中被存儲為“UNIX”,在little-endian系統中被存儲為“XINU”AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   sup2; 一些蹩腳的破解者用類(lèi)似的方法來(lái)修改可執行文件的機器碼,但是一個(gè)程序一旦載入內存,就不能再更改自身的機器碼(一個(gè)可執行文件的.text段是寫(xiě)保護的)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。我們能修改遠程進(jìn)程中的NewProc是因為它所處的那塊內存在分配時(shí)給予了PAGE_EXECUTE_READWRITE屬性。

   何時(shí)使用CreateRemoteThread和WriteProcessMemory技術(shù)

   通過(guò)CreateRemoteThread和WriteProcessMemory來(lái)注入代碼的技術(shù),和其他兩種方法相比,不需要一個(gè)額外的DLL文件,因此更靈活,但也更復雜更危險AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。一旦你的ThreadFunc中有錯誤,遠程線(xiàn)程會(huì )立即崩潰(看附錄F)。調試一個(gè)遠程的ThreadFunc也是場(chǎng)惡夢(mèng),所以你應該在僅僅注入若干條指令時(shí)才使用這個(gè)方法。要注入大量的代碼還是使用另外兩種方法吧。

   再說(shuō)一次,你可以在文章的開(kāi)頭部分下載到WinSpy,InjectEx和它們的源代碼AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   寫(xiě)在最后的話(huà)

   最后AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,我們總結一些目前還沒(méi)有提到的東西:

   方法 適用的操作系統 可操作的進(jìn)程進(jìn)程

   I. Windows鉤子 Win9x 和WinNT 僅限鏈接了USER32.DLL的進(jìn)程1

   II. CreateRemoteThread LoadLibrary 僅WinNT2 所有進(jìn)程3AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,包括系統服務(wù)4

   III. CreateRemoteThread WriteProcessMemory 近WinNT 所有進(jìn)程AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,包括系統服務(wù)

   1. 很明顯,你不能給一個(gè)沒(méi)有消息隊列的線(xiàn)程掛鉤AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。同樣SetWindowsHookEx也對系統服務(wù)不起作用(就算它們連接了USER32)。

   2. 在Win9x下沒(méi)有CreateRemoteThread和VirtualAllocEx(事實(shí)上可以在9x上模擬它們AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,但是到目前為止還只是個(gè)神話(huà))

   3. 所有進(jìn)程 = 所有的Win32進(jìn)程 + csrss.exe

   本地程序(native application)比如smss.exe, os2ss.exe, autochk.exe,不使用Win32 APIs,也沒(méi)有連接到kernel32.dllAI無(wú)法打開(kāi)插圖 插圖包含非法操作數。唯一的例外是csrss.exe,win32子系統自身。它是一個(gè)本地程序,但是它的一些庫(比如winsrv.dll)需要Win32 DLL包括kernel32.dll.

   4.如果你向注入代碼到系統服務(wù)或csrss.exe,在打開(kāi)遠程進(jìn)程的句柄(OpenProcess)之前把你的進(jìn)程的優(yōu)先級調整為“SeDebugprovilege”(AdjustTokenPrivileges)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

   大概就這些了吧AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。還有一點(diǎn)你需要牢記在心:你注入的代碼(特別是存在錯誤時(shí))很容易就會(huì )把目的進(jìn)程拖垮。記?。贺熑坞S權利而來(lái)(Power comes with responsibility)!

   這篇文章中的很多例子都和密碼有關(guān),看過(guò)這篇文章后你可能也會(huì )對Zhefu Zhang(譯者注:大概是一位中國人,張哲夫??)寫(xiě)的Supper Password Spy++感興趣AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。他講解了如何從IE的密碼框中得到密碼,也說(shuō)了如何保護你的密碼不被這種攻擊。

   最后一點(diǎn):讀者的反饋是文章作者的唯一報酬,所以如果你認為這篇文章有作用,請留下你的評論或給它投票AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。更重要的是,如果你發(fā)現有錯誤或bug;或你認為什么地方做得還不夠好,有需要改進(jìn)的地方;或有不清楚的地方也都請告訴我。

  感謝

   首先,我要感謝我在CodeGuru(這篇文章最早是在那兒發(fā)表的)的讀者,正是由于你們的鼓勵和支持這篇文章才得以從最初的1200單詞發(fā)展到今天這樣6000單詞的“龐然大物”AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。如果說(shuō)有一個(gè)人我要特別感謝的話(huà),他就是Rado Picha。這篇文章的一部分很大程度上得益于他對我的建議和幫助。最后,但也不能算是最后,感謝Susan Moore,他幫助我跨越了那個(gè)叫做“英語(yǔ)”的雷區,讓這篇文章更加通順達意。

  附錄

  A) 為什么kernel32.dll和user32.dll中是被映射到相同的內存地址AI無(wú)法打開(kāi)插圖 插圖包含非法操作數?

  我的假定:以為微軟的程序員認為這么做可以?xún)?yōu)化速度AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。讓我們來(lái)解釋一下這是為什么。

  一般來(lái)說(shuō),一個(gè)可執行文件包含幾個(gè)段,其中一個(gè)為“.reloc”段AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  當鏈接器生成EXE或DLL時(shí),它假定這個(gè)文件會(huì )被加載到一個(gè)特定的地址,也就是所謂的假定/首選加載/基地址(assumed/preferred load/base address)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。內存映像(image)中的所有絕對地址都時(shí)基于該“鏈接器假定加載地址”的。如果由于某些原因,映像沒(méi)有加載到這個(gè)地址,那么PE加載器(PE loader)就不得不修正該映像中的所有絕對地址。這就是“.reloc”段存在的原因:它包含了一個(gè)該映像中所有的“鏈接器假定地址”與真正加載到的地址之間的差異的列表(注意:編譯器產(chǎn)生的大部分指令都使用一種相對尋址模式,所以,真正需要重定位[relocation]的地方并沒(méi)有你想像的那么多)。如果,從另一方面說(shuō),加載器可以把映像加載到鏈接器首選地址,那么“.reloc”段就會(huì )被徹底忽略。

  但是,因為每一個(gè)Win32程序都需要kernel32.dll,大部分需要user32.dll,所以如果總是把它們兩個(gè)映射到其首選地址,那么加載器就不用修正kernel32.dll和user32.dll中的任何(絕對)地址,加載時(shí)間就可以縮短AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  讓我們用下面的例子來(lái)結束這個(gè)討論:

  把一個(gè)APP.exe的加載地址改為kernel32的(/base:"0x77e80000")或user32的(/base:"0x77e10000")首選地址AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。如果App.exe沒(méi)有引入UESE32,就強制LoadLibrary。然后編譯App.exe,并運行它。你會(huì )得到一個(gè)錯誤框(“非法的系統DLL重定位”),App.exe無(wú)法被加載。

  為什么?當一個(gè)進(jìn)程被創(chuàng )建時(shí),Win2000和WinXP的加載器會(huì )檢查kernel32.dll和user32.dll是否被映射到它們的首選地址(它們的名稱(chēng)是被硬編碼進(jìn)加載器的),如果沒(méi)有,就會(huì )報錯AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。在WinNT4 中ole32.dll也會(huì )被檢查。在WinNT3.51或更低版本中,則不會(huì )有任何檢查,kernel32.dll和user32.dll可以被加載到任何地方。唯一一個(gè)總是被加載到首選地址的模塊是ntdll.dll,加載器并不檢查它,但是如果它不在它的首選地址,進(jìn)程根本無(wú)法創(chuàng )建。

  總結一下:在WinNT4或更高版本的操作系統中:

  ●總被加載到它們的首選地址的DLL有:kernel32.dll,user32.dll和ntdll.dllAI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  ●Win32程序(連同csrss.exe)中一定存在的DLL:kernel32.dll和ntdll.dllAI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  ●所有進(jìn)程中都存在的dll:ntdll.dllAI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  B) /GZ編譯開(kāi)關(guān)

  在Debug時(shí),/GZ開(kāi)關(guān)默認是打開(kāi)的AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。它可以幫你捕捉一些錯誤(詳細內容參考文檔)。但是它對我們的可執行文件有什么影響呢?

  當/GZ被使用時(shí),編譯器會(huì )在每個(gè)函數,包含函數調用中添加額外的代碼(添加到每個(gè)函數的最后面)來(lái)檢查ESP棧指針是否被我們的函數更改過(guò)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。但是,等等,ThreadFunc中被添加了一個(gè)函數調用?這就是通往災難的道路。因為,被復制到遠程進(jìn)程中的ThreadFunc將調用一個(gè)在遠程進(jìn)程中不存在的函數。

  C) static函數和增量連接(Incremental linking)

  增量連接可以縮短連接的時(shí)間,在增量編譯時(shí),每個(gè)函數調用都是通過(guò)一個(gè)額外的JMP指令來(lái)實(shí)現的(一個(gè)例外就是被聲明為static的函數!)這些JMP允許連接器移動(dòng)函數在內存中的位置而不用更新調用該函數的CALLAI無(wú)法打開(kāi)插圖 插圖包含非法操作數。但是就是這個(gè)JMP給我們帶來(lái)了麻煩:現在ThreadFunc和AfterThreadFunc將指向JMP指令而不是它們的真實(shí)代碼。所以,當計算ThreadFunc的大小時(shí):

  const int cbCodeSize = ((LPBYTE) AfterThreadFunc - (LPBYTE) ThreadFunc);

  你實(shí)際得到的將是指向ThreadFunc和AfterThreadFunc的JMP指令之間的“距離”AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。現在假設我們的ThreadFunc在004014C0,和其對應的JMP指令在00401020

  :00401020 jmp 004014C0

  :004014C0 push EBP ; ThreadFunc的真實(shí)地址

  :004014C1 mov EBP, ESP

  然后AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,

  WriteProcessMemory( .., ThreadFunc, cbCodeSize, ..);

  將把“JMP 004014C0”和其后的cbCodeSize范圍內的代碼而不是ThreadFunc復制到遠程進(jìn)程AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。遠程線(xiàn)程首先會(huì )執行“JMP 004010C0”,然后一直執行到這個(gè)進(jìn)程代碼的最后一條指令(譯者注:這當然不是我們想要的結果)。

  然而,如果一個(gè)函數被聲明為static,就算使用增量連接,也不會(huì )被替換為JMP指令AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。這就是為什么我在規則#4中說(shuō)把ThreadFunc和AfterThreadFunc聲明為static或禁止增量連接的原因了。(關(guān)于增量連接的其他方面請參看Matt Pietrek寫(xiě)的“Remove Fatty Deposits from Your Applications Using Our 32-bit Liposuction Tools”)

  D) 為什么ThreadFunc只能有4K的局部變量AI無(wú)法打開(kāi)插圖 插圖包含非法操作數?

  局部變量總是保存在棧上的AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。假設一個(gè)函數有256字節的局部變量,當進(jìn)入該函數時(shí)(更確切地說(shuō)是在functions prologue中),棧指針會(huì )被減去256。像下面的函數:

  void Dummy(void) {

   BYTE var[256];

   var[0] = 0;

   var[1] = 1;

   var[255] = 255;

  會(huì )被編譯為類(lèi)似下面的指令:

  :00401000 push ebp

  :00401001 mov ebp, esp

  :00401003 sub esp, 00000100 ; change ESP as storage for

   ; local variables is needed

  :00401006 mov byte ptr [esp], 00 ; var[0] = 0;

  :0040100A mov byte ptr [esp+01], 01 ; var[1] = 1;

  :0040100F mov byte ptr [esp+FF], FF ; var[255] = 255;

  :00401017 mov esp, ebp ; restore stack pointer

  :00401019 pop ebp

  :0040101A ret

  請注意在上面的例子中ESP(棧指針)是如何被改變的AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。但是如果一個(gè)函數有多于4K的局部變量該怎么辦?這種情況下,棧指針不會(huì )被直接改變,而是通過(guò)一個(gè)函數調用來(lái)正確實(shí)現ESP的改變。但是就是這個(gè)“函數調用”導致了ThreadFunc的崩潰,因為它在遠程進(jìn)程中的拷貝將會(huì )調用一個(gè)不存在的函數。

  讓我們來(lái)看看文檔關(guān)于棧探針(stack probes)和/Gs編譯選項的說(shuō)明:

  “/Gssize選項是一個(gè)允許你控制棧探針的高級特性AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。棧探針是編譯器插入到每個(gè)函數調用中的一系列代碼。當被激活時(shí),棧探針將溫和地按照存儲函數局部變量所需要的空間大小來(lái)移動(dòng)

  如果一個(gè)函數需要大于size指定的局部變量空間,它的棧探針將被激活AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。默認的size為一個(gè)頁(yè)的大?。ㄔ?0x86上為4k)。這個(gè)值可以使一個(gè)Win32程序和Windows NT的虛擬內存管理程序和諧地交互,在運行期間向程序棧增加已提交的內存總數。

  我能確定你們對上面的敘述(“棧探針將溫和地按照存儲函數局部變量所需要的空間大小來(lái)移動(dòng)”)感到奇怪AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。這些編譯選項(他們的描述!)有時(shí)候真的讓人很惱火,特別是當你想真的了解它們是怎么工作的時(shí)候。打個(gè)比方,如果一個(gè)函數需要12kb的空間來(lái)存放局部變量,棧上的內存是這樣“分配”的

  sub esp, 0x1000 ; 先“分配”4 Kb

  test [esp], eax ; touches memory in order to commit a

   ; new page (if not already committed)

  sub esp, 0x1000 ; “分配”第二個(gè) 4 Kb

  test [esp], eax ; ...

  sub esp, 0x1000

  test [esp], eax

  注意棧指針是如何以4Kb為單位移動(dòng)的,更重要的是每移動(dòng)一步后使用test對棧底的處理(more importantly, how the bottom of the stack is "touched" after each step)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。這可以確保了在“分配”下一個(gè)頁(yè)之前,包含棧底的頁(yè)已經(jīng)被提交。

  繼續閱讀文檔的說(shuō)明:

  “每一個(gè)新的線(xiàn)程會(huì )擁有(receives)自己的??臻g,這包括已經(jīng)提交的內存和保留的內存AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。默認情況下每個(gè)線(xiàn)程使用1MB的保留內存和一個(gè)頁(yè)大小的以提交內存。如果有必要,系統將從保留內存中提交一個(gè)頁(yè)。”(看MSDN中GreateThread dwStackSize “Thread Stack Size”)

  ..現在為什么文檔中說(shuō)“這個(gè)值可以使一個(gè)Win32程序和Windows NT的虛擬內存管理程序和諧地交互”也很清楚了AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  E) 為什么我要把多于3個(gè)case分支的swith分割開(kāi)來(lái)呢AI無(wú)法打開(kāi)插圖 插圖包含非法操作數?

  同樣AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,用例子來(lái)說(shuō)明會(huì )簡(jiǎn)單些:

  int Dummy( int arg1 )

   int ret =0;

   switch( arg1 ) {

   case 1: ret = 1; break;

   case 2: ret = 2; break;

   case 3: ret = 3; break;

   case 4: ret = 0xA0B0; break;

   return ret;

  將會(huì )被編譯為類(lèi)似下面的代碼:

  Address OpCode/Params Decoded instruction

   ; arg1 - ECX

  :00401000 8B4C2404 mov ecx, dword ptr [esp+04]

  :00401004 33C0 xor eax, eax ; EAX = 0

  :00401006 49 dec ecx ; ECX --

  :00401007 83F903 cmp ecx, 00000003

  :0040100A 771E ja 0040102A

  ; JMP to one of the addresses in table ***

  ; note that ECX contains the offset

  :0040100C FF248D2C104000 jmp dword ptr [4*ecx+0040102C]

  :00401013 B801000000 mov eax, 00000001 ; case 1: eax = 1;

  :00401018 C3 ret

  :00401019 B802000000 mov eax, 00000002 ; case 2: eax = 2;

  :0040101E C3 ret

  :0040101F B803000000 mov eax, 00000003 ; case 3: eax = 3;

  :00401024 C3 ret

  :00401025 B8B0A00000 mov eax, 0000A0B0 ; case 4: eax = 0xA0B0;

  :0040102A C3 ret

  :0040102B 90 nop

  ; 地址表 ***

  :0040102C 13104000 DWORD 00401013 ; jump to case 1

  :00401030 19104000 DWORD 00401019 ; jump to case 2

  :00401034 1F104000 DWORD 0040101F ; jump to case 3

  :00401038 25104000 DWORD 00401025 ; jump to case 4

  看到switch-case是如何實(shí)現的了嗎AI無(wú)法打開(kāi)插圖 插圖包含非法操作數?

  它沒(méi)有去測試每個(gè)case分支,而是創(chuàng )建了一個(gè)地址表(address table)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。我們簡(jiǎn)單地計算出在地址表中偏移就可以跳到正確的case分支。想想吧,這真是一個(gè)進(jìn)步,假設你有一個(gè)50個(gè)分支的switch語(yǔ)句,假如沒(méi)有這個(gè)技巧,你不的不執行50次CMP和JMP才能到達最后一個(gè)case,而使用地址表,你可以通過(guò)一次查表即跳到正確的case。使用算法的時(shí)間復雜度來(lái)衡量:我們把O(2n)的算法替換成了O(5)的算法,其中:

  1. O代表最壞情況下的時(shí)間復雜度AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  2. 我們假設計算偏移(即查表)并跳到正確的地址需要5個(gè)指令AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  現在,你可能認為上面的情況僅僅是因為case常量選擇得比較好,(1,2,3,4,5)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。幸運的是,現實(shí)生活中的大多數例子都可以應用這個(gè)方案,只是偏移的計算復雜了一點(diǎn)而已。但是,有兩個(gè)例外:

  ●如果少于3個(gè)case分支AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,或

  ●如果case常量是完全相互無(wú)關(guān)的AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。(比如 1, 13, 50, 1000)。

  最終的結果和你使用普通的if-else if是一樣的AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  有趣的地方:如果你曾經(jīng)為case后面只能跟常量而迷惑的話(huà),現在你應該知道為什么了吧AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。這個(gè)值必須在編譯期間就確定下來(lái),這樣才能創(chuàng )建地址表。

  回到我們的問(wèn)題AI無(wú)法打開(kāi)插圖 插圖包含非法操作數!

  注意到0040100C處的JMP指令了嗎AI無(wú)法打開(kāi)插圖 插圖包含非法操作數?我們來(lái)看看Intel的文檔對十六進(jìn)制操作碼FF的說(shuō)明:

  Opcode Instruction Description

  FF /4 JMP r/m32 Jump near, absolute indirect, address given in r/m32

  JMP使用了絕對地址!也就是說(shuō),它的其中一個(gè)操作數(在這里是0040102C)代表一個(gè)絕對地址AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。還用多說(shuō)嗎?現在遠程的ThreadFunc會(huì )盲目第在地址表中004101C然后跳到這個(gè)錯誤的地方,馬上使遠程進(jìn)程掛掉了。

  F) 到底是什么原因使遠程進(jìn)程崩潰了AI無(wú)法打開(kāi)插圖 插圖包含非法操作數?

  如果你的遠程進(jìn)程崩潰了AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,原因可能為下列之一:

  1. 你引用了ThreadFunc中一個(gè)不存在的字符串AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

  2. ThreadFunc中一個(gè)或多個(gè)指令使用了絕對尋址(看附錄E中的例子)

  3. ThreadFunc調用了一個(gè)不存在的函數(這個(gè)函數調用可能是編譯器或連接器添加的)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。這時(shí)候你需要在反匯編器中尋找類(lèi)似下面的代碼:

  :004014C0 push EBP ; entry point of ThreadFunc

  :004014C1 mov EBP, ESP

  :004014C5 call 0041550 ; 在這里崩潰了

   ; remote process

  :00401502 ret

  如果這個(gè)有爭議的CALL是編譯器添加的(因為一些不該打開(kāi)的編譯開(kāi)關(guān)比如/GZ打開(kāi)了)AI無(wú)法打開(kāi)插圖 插圖包含非法操作數,它要么在ThreadFunc的開(kāi)頭要么在ThreadFunc接近結尾的地方

  不管在什么情況下,你使用CreateRemoteThread WriteProcessMemory技術(shù)時(shí)必須萬(wàn)分的小心,特別是編譯器/連接器的設置,它們很可能會(huì )給你的ThreadFunc添加一些帶來(lái)麻煩的東西AI無(wú)法打開(kāi)插圖 插圖包含非法操作數。

0
0
收藏0
回帖

[程序設計]進(jìn)程注入的三種方法! 期待您的回復!

取消
載入表情清單……
載入顏色清單……
插入網(wǎng)絡(luò )圖片

取消確定

圖片上傳中
編輯器信息
提示信息