1. 程式人生 > >win7 64 DLL的遠端注入技術 及注入dll函式呼叫

win7 64 DLL的遠端注入技術 及注入dll函式呼叫

一、DLL注入

DLL的遠端注入技術是目前Win32病毒廣泛使用的一種技術。使用這種技術的病毒體通常位於一個DLL中,在系統啟動的時候,一個EXE程式會將這個DLL載入至某些系統程序(如Explorer.exe)中執行
這樣一來,普通的程序管理器就很難發現這種病毒了,而且即使發現了也很難清除,因為只要病毒寄生的程序不終止執行,那麼這個DLL就不會在記憶體中解除安裝,
使用者也就無法在資源管理器中刪除這個DLL檔案,真可謂一箭雙鵰哉。記得2003年QQ尾巴病毒肆虐的時候,就已經有些尾巴病毒的變種在使用這種技術了。
到了2004年初,我曾經嘗試著模擬了一個QQ尾巴病毒,但獨是跳過了DLL的遠端載入技術

。直到最近在學校論壇上看到了幾位朋友在探討這一技術,便忍不住將這一塵封已久的技術從我的記憶中揀了出來,以滿足廣大的技術愛好者們。
在閱讀本文之前,你需要了解以下幾個API函式:

  ·OpenProcess - 用於開啟要寄生的目標程序。

  ·VirtualAllocEx/VirtualFreeEx - 用於在目標程序中分配/釋放記憶體空間

  ·WriteProcessMemory - 用於在目標程序中寫入要載入的DLL名稱

  ·CreateRemoteThread - 遠端載入DLL的核心內容,用於控制目標程序呼叫API函式。

  ·LoadLibrary - 目標程序通過呼叫此函式來載入病毒DLL。

 在此我只給出了簡要的函式說明,關於函式的詳細功能和介紹請參閱MSDN。

示例程式

  我將在以下的篇幅中用一個簡單的示例Virus.exe來實現這一技術。這個示例的介面如下圖:


(1)首先執行Target.exe,這個檔案是一個用Win32 Application嚮導生成的“Hello, World”程式,用來作為寄生的目標程序。

(2)然後在介面的編輯控制元件中輸入程序的名稱“Target.exe”,單擊“注入DLL”按鈕,這時候Virus.exe就會將當前目錄下的DLL.dll注入至Target.exe程序中。

(3)在注入DLL.dll之後,你也可以單擊“解除安裝DLL”來將已經注入的DLL解除安裝。

  模擬的病毒體DLL.dll

  這是一個簡單的Win32 DLL程式,它僅由一個入口函式DllMain組成:

  1. BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )  
  2. {  
  3.  switch ( fdwReason )  
  4.  {  
  5.   case DLL_PROCESS_ATTACH:  
  6.   {  
  7.    MessageBox( NULL, _T("DLL已進入目標程序。"), _T("資訊"), MB_ICONINFORMATION );  
  8.   }  
  9.   break;  
  10.   case DLL_PROCESS_DETACH:  
  11.   {  
  12.    MessageBox( NULL, _T("DLL已從目標程序解除安裝。"), _T("資訊"), MB_ICONINFORMATION );  
  13.   }  
  14.   break;  
  15.  }  
  16.  return TRUE;  
  17. }   


如你所見,這裡我在DLL被載入和解除安裝的時候呼叫了MessageBox,這是用來顯示我的遠端注入/解除安裝工作是否成功完成。而對於一個真正的病毒體來說,
它往往就是處理DLL_PROCESS_ATTACH事件,在其中加入了啟動病毒程式碼的部分:

  1. case DLL_PROCESS_ATTACH:  
  2. {  
  3.  StartVirus();  
  4. }  
  5. break;   

注入!
現在要開始我們的注入工作了。首先,我們需要找到目標程序:

  1. DWORD FindTarget(LPCTSTR lpszProcess)  
  2. {  
  3.  DWORD dwRet = 0;  
  4.  HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);  
  5.  PROCESSENTRY32 pe32;  
  6.  pe32.dwSize = sizeof( PROCESSENTRY32 );  
  7.  Process32First(hSnapshot, &pe32);  
  8.  do
  9.  {  
  10.   if ( lstrcmpi(pe32.szExeFile, lpszProcess) == 0 )  
  11.   {  
  12.    dwRet = pe32.th32ProcessID;  
  13.    break;  
  14.   }  
  15.  } while (Process32Next(hSnapshot, &pe32));  
  16.  CloseHandle( hSnapshot );  
  17.  return dwRet;  
  18. }   


這裡我使用了Tool Help函式庫,當然如果你是NT系統的話,也可以選擇PSAPI函式庫。
這段程式碼的目的就是通過給定的程序名稱來在當前系統中查詢相應的程序,並返回該程序的ID。得到程序ID後,就可以呼叫OpenProcess來開啟目標程序了:

  1. // 開啟目標程序
  2. HANDLE hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |  
  3.  PROCESS_VM_WRITE, FALSE, dwProcessID );   


現在有必要說一下OpenProcess第一個引數所指定的三種許可權。在Win32系統下,每個程序都擁有自己的4G虛擬地址空間,各個程序之間都相互獨立。如果一個程序需要完成跨程序的工作的話,那麼它必須擁有目標程序的相應操作許可權。

在這裡,PROCESS_CREATE_THREAD表示我可以通過返回的程序控制代碼在該程序中建立新的執行緒,也就是呼叫CreateRemoteThread的許可權

同理,PROCESS_VM_OPERATION則表示在該程序中分配/釋放記憶體的許可權,也就是呼叫VirtualAllocEx/VirtualFreeEx的許可權
PROCESS_VM_WRITE表示可以向該程序的地址空間寫入資料,也就是呼叫WriteProcessMemory的許可權

至此目標程序已經開啟,那麼我們該如何來將DLL注入其中呢?在這之前,我請你看一行程式碼,是如何在本程序內顯式載入DLL的:

  1. HMODULE hDll = LoadLibrary( "DLL.dll" );   

那麼,如果能控制目標程序呼叫LoadLibrary,不就可以完成DLL的遠端注入了麼?的確是這樣,我們可以通過CreateRemoteThread將LoadLibrary作為目標程序的一個執行緒來啟動,這樣就可以完成“控制目標程序呼叫LoadLibrary”的工作了。到這裡,也許你會想當然地寫下類似這樣的程式碼:

  1. DWORD dwID;  
  2. LPVOID pFunc = LoadLibraryA;  
  3. HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0,  
  4.  (LPTHREAD_START_ROUTINE)pFunc,   
  5. (LPVOID)"DLL.dll", 0, &dwID );   


不過結果肯定會讓你大失所望——注入DLL失敗

那麼現在讓我們來分析一下失敗的原因吧。我是前說過,在Win32系統下,每個程序都擁有自己的4G虛擬地址空間,各個程序之間都是相互獨立的。在這裡,我們當作引數傳入的字串"DLL.dll"其實是一個數值,它表示這個字串位於Virus.exe地址空間之中的地址而這個地址在傳給Target.exe之後,它指向的東西就失去了有效性

舉個例子來說,譬如A、B兩棟大樓,我住在A樓的401;
那麼B樓的401住的是誰我當然不能確定——也就是401這個門牌號在B樓失去了有效性,而且如果我想要入住B樓的話,
我就必須請B樓的樓長為我在B樓中安排新的住處(當然這個新的住處是否401也就不一定了)。

  由此看來,我就需要做這麼一系列略顯繁雜的手續——

首先在Target.exe目標程序中分配一段記憶體空間;
然後向這段空間寫入我要載入的DLL名稱;

最後再呼叫CreateRemoteThread。

這段程式碼就成了這樣:

  1. // 向目標程序地址空間寫入DLL名稱
  2. DWORD dwSize, dwWritten;  
  3. dwSize = lstrlenA( lpszDll ) + 1;  
  4. LPVOID lpBuf = VirtualAllocEx( hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE );  
  5. if ( NULL == lpBuf )  
  6. {  
  7.  CloseHandle( hProcess );  
  8.  // 失敗處理
  9. }  
  10. if ( WriteProcessMemory( hProcess, lpBuf, (LPVOID)lpszDll, dwSize, &dwWritten ) )  
  11. {  
  12.  // 要寫入位元組數與實際寫入位元組數不相等,仍屬失敗
  13.  if ( dwWritten != dwSize )  
  14.  {  
  15.   VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT );  
  16.   CloseHandle( hProcess );  
  17.   // 失敗處理
  18.  }  
  19. }  
  20. else
  21. {  
  22.  CloseHandle( hProcess );  
  23.  // 失敗處理
  24. }  
  25. // 使目標程序呼叫LoadLibrary,載入DLL
  26. DWORD dwID;  
  27. LPVOID pFunc = LoadLibraryA;  
  28. HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0,   
  29. (LPTHREAD_START_ROUTINE)pFunc, lpBuf, 0, &dwID );   


需要說的有兩點:

第一,由於我要在目標程序中為ANSI字串來分配記憶體空間,所以這裡凡是和目標程序相關的部分,都明確使用了字尾為“A”的API函式——當然,如果要使用Unicode字串的話,可以換作字尾是“W”的API;
第二,在這裡LoadLibrary的指標我是取的本程序的LoadLibraryA的地址,這是因為LoadLibraryA/LoadLibraryW位於kernel32.dll之中,
而Win32下每個應用程式都會把kernel32.dll載入到程序地址空間中一個固定的地址,所以這裡的函式地址在Target.exe中也是有效的

在呼叫LoadLibrary完畢之後,我們就可以做收尾工作了:

  1. // 等待LoadLibrary載入完畢
  2. WaitForSingleObject( hThread, INFINITE );  
  3. // 釋放目標程序中申請的空間
  4. VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT );  
  5. CloseHandle( hThread );  
  6. CloseHandle( hProcess );   


在此解釋一下WaitForSingleObject一句。由於我們是通過CreateRemoteThread在目標程序中另外開闢了一個LoadLibrary的執行緒,所以我們必須等待這個執行緒執行完畢才能夠釋放那段先前申請的記憶體。

好了,現在你可以嘗試著整理這些程式碼並編譯執行。執行Target.exe,然後開啟一個有模組檢視功能的程序檢視工具(在這裡我使用我的July)來檢視Target.exe的模組,
你會發現在注入DLL之前,Target.exe中並沒有DLL.dll的存在:

 在呼叫了注入程式碼之後,DLL.dll就位於Target.exe的模組列表之中了:

二、矛盾相生(解毒)

1、法一:與DLL注入過程類似

記得2004年初我將QQ尾巴病毒成功模擬後,有很多網友詢問我如何才能防毒,不過我都沒有回答——因為當時我研究的重點並非病毒的寄生特性。這一寄生特性直到今天可以說我才模擬完畢,那麼,我就將解毒的方法也一併公開吧。

和DLL的注入過程類似,只不過在這裡使用了兩個API:GetModuleHandle和FreeLibrary。出於篇幅考慮,我略去了與注入部分相似或相同的程式碼:

  1. // 使目標程序呼叫GetModuleHandle,獲得DLL在目標程序中的控制代碼
  2. DWORD dwHandle, dwID;  
  3. LPVOID pFunc = GetModuleHandleA;  
  4. HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)  
  5. pFunc, lpBuf, 0, &dwID );  
  6. // 等待GetModuleHandle執行完畢
  7. WaitForSingleObject( hThread, INFINITE );  
  8. // 獲得GetModuleHandle的返回值
  9. GetExitCodeThread( hThread, &dwHandle );  
  10. // 釋放目標程序中申請的空間
  11. VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT );  
  12. CloseHandle( hThread );  
  13. // 使目標程序呼叫FreeLibrary,解除安裝DLL
  14. pFunc = FreeLibrary;  
  15. hThread = CreateRemoteThread( hProcess, NULL, 0,  
  16.  (LPTHREAD_START_ROUTINE)pFunc,  
  17.  (LPVOID)dwHandle, 0, &dwID );  
  18. // 等待FreeLibrary解除安裝完畢
  19. WaitForSingleObject( hThread, INFINITE );  
  20. CloseHandle( hThread );  
  21. CloseHandle( hProcess );   

用這個方法可以解除安裝一個程序中的DLL模組,當然包括那些非病毒體的DLL。所以,這段程式碼還是謹慎使用為好

在完成解除安裝之後,如果沒有別的程式載入這個DLL,你就可以將它刪除了。

2、法二:採用查殺工具

2、1.安裝木馬查殺工具。對於多數人而言,要想通過手工查殺DLL木馬是不太現實的,因此安裝一款可以查殺此類木馬的反病毒軟體,是非常有必要的。這裡還要提醒讀者的是,應及時升級病毒資料庫,這樣才能保證有效地查殺絕大部分木馬病毒。當然,我們也可以在計算機中安裝那些專門針對木馬的查殺工具,例如木馬克星。

2、2.檢視是否有不明埠開放以及對埠通訊進行監控。只要木馬進行連線,接受/傳送資料則必然會開啟埠,DLL木馬也不例外,我們可以通過“Netstat-ano”命令來檢視TCP/UDP埠的連線,以及開放埠的程序識別符號;

也可以直接使用程序埠檢視工具Fport.exe來檢視與埠對應的程序,以發現是否有不明的連線和埠開放。另外,有些DLL木馬通過埠劫持或者埠重用的方法來進行通訊,所以僅是檢視埠還是不夠的,有必要的話,我們可使用嗅探器來了解開啟的埠到底在傳輸些什麼資料。

2、3.檢查系統目錄下是否有可疑的DLL檔案。安裝好系統和所有應用程式之後,可對系統目錄下System32資料夾中的EXE和DLL檔案作一記錄:在命令提示符下執行“dir*.exe>bak1.txt&dir*.dll>bak2.txt”,將所有的EXE和DLL檔案資訊匯出成TXT檔案儲存。當日後發現異常時,可以使用相同的命令再次備份,並使用FC命令比較兩次的EXE檔案和DLL檔案。通過這種方法,我們可以發現可疑的EXE和DLL檔案,同時通過檔案的大小、建立時間來判斷是否為DLL木馬。

2、4.檢視系統程序呼叫的DLL檔案。當我們懷疑有DLL木馬插入到系統程序,可以使用一些第三方程序工具來檢視程序所呼叫的DLL檔案,然後進一步確認是否中了DLL木馬。此類檢視工具有程序獵手、程序間諜等等。另外,我們也可以使用XP系統自帶的命令列工具TaskList,來顯示程序呼叫的DLL檔案,並將這些資訊匯出成TXT檔案儲存,以便隨時進行比較。


但我們就要利用這種木馬 因為他比較難查殺 所以\~~~~~~~~~~~~~~

3、法三:找出寄生的EXE

警報,病毒無法清除

如果系統中被植入了DLL木馬,將會出現什麼情況呢?

首先,系統被黑客遠端控制,出現數據丟失等情況;其次,防毒軟體會報警系統中有病毒,但是卻無法進行清除。例如,在電腦中運行了上面製作的上興遠控木馬後,筆者電腦上安裝的防毒軟體提示發現病毒,但是在刪除DLL病毒檔案時卻失敗了。即使我們手工刪除病毒檔案,也會因為DLL檔案正在使用中,所以也無法徹底刪除。即使進入安全模式,得到的也是同樣的結果。
另外,由於DLL木馬是插入到系統程序中的,因此通過工作管理員等程序工具,也無法發現任何木馬程序,這給木馬的清除帶來了很大的困難!
無法刪除病毒檔案,無法查詢到木馬程序,那麼到底該如何清除DLL注入式木馬呢?

無所遁形,揪出木馬藏身之所

雖然木馬沒有自己的程序,但是有一個宿主程序,只要結束宿主程序,停止DLL檔案的呼叫,就可以刪除DLL檔案,進而清除木馬。因此,清除DLL木馬的第一步,就是找到木馬注入的宿主程序。那麼,如何才能找到木馬注入的宿主程序呢?且讓我們細細看來。

以清除“上興遠控木馬”為例,從防毒軟體的報警提示中已經知道木馬DLL檔名為“rejoice.dll”。因此,就可以通過一些檢視程序呼叫DLL檔案的程序管理工具,找到該檔案的宿主程序,此類工具很多,比如大名鼎鼎的ICESword

執行IceSword後,點選左側邊欄“檢視→程序”,就可以在其右側視窗中看到所有程序列表。右鍵點選某程序,在彈出選單中選擇“模組資訊”命令,即可看到該程序呼叫的所有DLL檔案


提示:DLL注入式木馬通常是將DLL檔案,載入到explorer.exe、svchost.exe、winlogon.exe、iexplore.exe等系統程序中的。因此在查詢DLL宿主檔案時,可以關閉其它無關的程式,然後依次檢查這幾個程序中的DLL檔案。

不過一個一個的檢查系統程序,確實有些麻煩,如何才能快速的定位木馬的宿主程序呢?可以使用一款名為“procexp”的程序管理工具。執行procexp後,在程式介面中間顯示的是樹狀程序關係列表,下方是每個程序的詳細資訊。點選選單“Find→Find DLL”命令,開啟DLL檔案查詢對話方塊,在“DLL Substring”中輸入要查詢的關鍵詞,這裡輸入剛才防毒軟體掃描出的DLL檔名“rejoice.dll”。然後點選“Search”按鈕,在下方的列表中就可以看到該DLL檔案是被哪個程序呼叫的了。


牛刀小試,清除普通程序DLL注入木馬

對於大部分DLL注入木馬,其注入到“iexplore.exe”和“explorer.exe”這兩個程序。對於這類普通程序的DLL木馬,清除將是非常方便的。

如果DLL檔案是注入到“iexplore.exe”程序中,由於此程序就是IE瀏覽器程序,因此就需要先關掉所有IE視窗和相關程式,然後直接找到DLL檔案進行刪除就可以了。

如果DLL檔案是注入到“explorer.exe”程序中,那麼就略顯麻煩一些。由於此程序用於顯示桌面和資源管理器,因此,當通過工作管理員結束掉“explorer.exe”程序時,桌面無法看到,桌面上所有圖示消失掉,"我的電腦"、"網路上的芳鄰"等所有圖示都不見了,同時,也無法開啟資源管理器找到木馬檔案進行刪除。怎麼辦呢?

實際上,解決的方法也很簡單。在工作管理員中點選選單“檔案→新任務執行”,開啟“建立新任務”對話方塊,點選“瀏覽”按鈕,通過瀏覽對話方塊就可以開啟DLL檔案所在的路徑。然後選擇“檔案型別”為“所有檔案”,即可顯示並刪除DLL檔案了。


在瀏覽對話方塊中刪除DLL檔案

除惡務盡,清除特殊DLL注入木馬

除了以上所說的注入普通程序的DLL木馬之外,還有許多木馬注入到系統裡關鍵程序中,比如svchost.exe、smss.exe、winlogon.exe程序。這些程序使用普通方式無法結束,使用特殊工具結束掉程序後,卻又很可能造成系統崩潰無法正常執行的情況。對於這些木馬,我們可以通過以下兩種方法進行清除。

1.使用IceSword解除安裝DLL檔案

IceSword的功能十分強大,我們曾在以前作過介紹,在這裡,就可以利用它解除安裝掉已經插入到正在執行的系統程序中的DLL檔案。在IceSword的程序列表顯示視窗中,右鍵點選DLL木馬宿主程序,選擇彈出命令“模組資訊”。在程序模組資訊對話方塊中找到DLL木馬檔案,選擇檔案後點擊“強制解除”命令,即可將系統程序中的DLL木馬檔案解除安裝掉了


解除安裝系統程序中的DLL木馬

4、法四:定時列舉,發現不是本身所用的就強制Free掉

防止DLL注入我用的是另一種方法:

//允許的模組列表
theApp.m_szAuthorizedList = " USER32.DLL RPCRT4.DLL KERNEL32.DLL GDI32.DLL ADVAPI32.DLL NTDLL.DLL MSVCRT.DLL SETUPAPI.DLL CFGMGR32.DLL WINMM.DLL SHELL32.DLL SHLWAPI.DLL MSVCR80.DLL MFC80.DLL OLE32.DLL COMCTL32.DLL OLEACC.DLL OLEAUT32.DLL ATL80.DLL MSVCM80.DLL MFC80CHS.DLL MSVCP80.DLL MFCM80.DLL MFCM80U.DLL MFC80U.DLL VCOMP.DLL SECUR32.DLL MSVCRT.DLL MSVC