?? 反病毒引擎設計.htm
字號:
;產生一個異常<BR>當然,后面還有恢復原來中斷入口地址和異常處理幀的代碼。<BR><BR><BR>剛才所討論的技術僅限于WIN9X,想在WINNT/2000下進入Ring0則沒有這么容易。主要的原因是WINNT/2000沒有上述的漏洞,它們的系統代碼頁面(2G--4G)有很好的頁保護。大于0x80000000的虛擬地址對于用戶程序是不可見的。如果你用Softice的PAGE命令查看這些地址的頁屬性,你會發現S位,這說明這些地址僅可從核心態訪問。所以想在IDT,GDT隨意構造描述符,運行時修改內核是根本做不到的。所能做的僅是通過加載一個驅動程序,使用它來做你在Ring3下做不到的事情。病毒可以在它們加載的驅動中修改內核代碼,或為病毒本身創建調用門(利用NT由Ntoskrnl.exe導出的未公開的系統服務KeI386AllocateGdtSelectors,KeI386SetGdtSelector,KeI386ReleaseGdtSelectors)。如Funlove病毒就利用驅動來修改系統文件(Ntoskrnl.exe,Ntldr)以繞過安全檢查。但這里面有兩個問題,其一是驅動程序從哪里來,現代病毒普遍使用一個稱為“Drop”的技術,即在病毒體本身包含驅動程序二進制碼(可以進行壓縮或動態構造文件頭),在病毒需要使用時,動態生成驅動程序并將它們扔到磁盤上,然后馬上通過在SCM(服務控制管理器)注冊并最終調用StartService來使驅動程序得以運行;其二是加載一個驅動程序需要管理員身份,普通帳號在調用上述的加載函數時會返回失敗(安全子系統要檢查用戶的訪問令牌(Token)中有無SeLoadDriverPrivilege特權),但多數用戶在大多時候登錄時會選擇管理員身份,否則連病毒實時監控驅動也同樣無法加載,所以留給病毒的機會還是很多的。
<BR><BR>1.2.2駐留病毒<BR>駐留病毒是指那些在內存中尋找合適的頁面并將病毒自身拷貝到其中且在系統運行期間能夠始終保持病毒代碼的存在。駐留病毒比那些直接感染(Direct-action)型病毒更具隱蔽性,它通常要截獲某些系統操作來達到感染傳播的目的。進入了核心態的病毒可以利用系統服務來達到此目的,如CIH病毒通過調用一個由VMM導出的服務VMMCALL
_PageAllocate在大于0xC0000000的地址上分配一塊頁面空間。而處于用戶態的程序要想在程序退出后仍駐留代碼的部分于內存中似乎是不可能的,因為無論用戶程序分配何種內存都將作為進程占用資源的一部分,一旦進程結束,所占資源將立即被釋放。所以我們要做的是分配一塊進程退出后仍可保持的內存。<BR><BR>病毒寫作小組29A的成員GriYo
運用的一個技術很有創意:他通過CreateFileMappingA
和MapViewOfFile創建了一個區域對象并映射它的一個視口到自己的地址空間中去,并把病毒體搬到那里,由于文件映射所在的虛擬地址處于共享區域(能夠被所有進程看到,即所有進程用于映射共享區內虛擬地址的頁表項全都指向相同的物理頁面),所以下一步他通過向Explorer.exe中注入一段代碼(利用WriteProcessMemory來向其它進程的地址空間寫入數據),而這段代碼會從Explorer.exe的地址空間中再次申請打開這個文件映射。如此一來,即便病毒退出,但由于Explorer.exe還對映射頁面保持引用,所以一份病毒體代碼就一直保持在可以影響所有進程的內存頁面中直至Explorer.exe退出。
<BR><BR>另外還可以通過修改系統動態連接模塊(DLL)來進行駐留。WIN9X下系統DLL(如Kernel32.dll
映射至BFF70000)處于系統共享區域(2G-3G),如果在其代碼段空隙中寫入一小段病毒代碼則可以影響其它所有進程。但Kernel32.dll的代碼段在用戶態是只能讀不能寫的。所以必須先通過特殊手段修改其頁保護屬性;而在WINNT/2000下系統DLL所在頁面被映射到進程的私有空間(如Kernel32.dll
映射至77ED0000)中,并具有寫時拷貝屬性,即沒有進程試圖寫入該頁面時,所有進程共享這個頁面;而當一個進程試圖寫入該頁面時,系統的頁面錯誤處理代碼將收到處理器的異常,并檢查到該異常并非訪問違例,同時分配給引發異常的進程一個新頁面,并拷貝原頁面內容于其上且更新進程的頁表以指向新分配的頁。這種共享內存的優化給病毒的寫作帶來了一定的麻煩,病毒不能象在WIN9X下那樣僅修改Kernel32.dll一處代碼便可一勞永逸。它需要利用WriteProcessMemory來向每個進程映射Kernel32.dll的地址寫入病毒代碼,這樣每個進程都會得到病毒體的一個副本,這在病毒界被稱為多進程駐留或每進程駐留(Muti-Process
Residence or Per-Process Residence )。
<BR><BR>1.2.3截獲系統操作<BR>截獲系統操作是病毒慣用的伎倆。DOS時代如此,WINDOWS時代也不例外。在DOS下,病毒通過在中斷向量表中修改INT21H的入口地址來截獲DOS系統服務(DOS利用INT21H來提供系統調用,其中包括大量的文件操作)。而大部分引導區病毒會接掛INT13H(提供磁盤操作服務的BIOS中斷)從而取得對磁盤訪問的控制。WINDOWS下的病毒同樣找到了鉤掛系統服務的辦法。比較典型的如CIH病毒就是利用了IFSMGR.VXD(可安裝文件系統)提供的一個系統級文件鉤子來截獲系統中所有文件操作,我會在相關章節中詳細討論這個問題,因為WIN9X下的實時監控也主要利用這個服務。除此之外,還有別的方法。但效果沒有這個系統級文件鉤子好,主要是不夠底層,會丟失一些文件操作。<BR><BR>其中一個方法是利用APIHOOK,鉤掛API函數。其實系統中并沒有現成的這種服務,有一個SetWindowsHookEx可以鉤住鼠標消息,但對截獲API函數則無能為力。我們能做的是自己構造這樣的HOOK。方法其實很簡單:比如你要截獲Kernel32.dll導出的函數CreateFile,只須在其函數代碼的開頭(BFF7XXXX)加入一個跳轉指令到你的鉤子函數的入口,在你的函數執行完后再跳回來。如下圖所示:
<BR><BR>;; Target
Function(要截獲的目標函數)<BR> ……<BR> TargetFunction:(要截獲的目標函數入口)<BR> jmp
DetourFunction(跳到鉤子函數,5個字節長的跳轉指令)<BR> TargetFunction+5:<BR> push
edi<BR> ……<BR> ;;
Trampoline(你的鉤子函數)<BR> ……<BR> TrampolineFunction:(你的鉤子函數執行完后要返回原函數的地方)<BR> push
ebp<BR> mov ebp,esp<BR> push ebx<BR> push
esi(以上幾行是原函數入口處的幾條指令,共5個字節)<BR> jmp
TargetFunction+5(跳回原函數)<BR> ……<BR> 但這種方法截獲的僅僅是很小一部分文件打開操作。<BR><BR>在WIN9X下還有一個鮮為人知的截獲文件操作的辦法,說起來這應該算是WIN9X的一大后門。它就是Kernel32.dll中一個未公開的叫做VxdCall0的API函數。反匯編這個函數的代碼如下:<BR><BR>mov
eax,dword ptr [esp+00000004h] ;取得服務代號<BR><BR>pop dword ptr [esp]
;堆棧修正<BR><BR>call fword ptr cs:[BFFC9004]
;通過一個調用門調用3B段某處的代碼<BR><BR>如果我們繼續跟蹤下去,則會看到:<BR><BR>003B:XXXXXXXX int 30h
;這是個用以陷入VWIN32.VXD的保護模式回調<BR><BR>有關VxdCall的詳細內容,請參看Matt Pietrek的《Windows
95 System Programming
Secrets》。<BR><BR>當服務代號為0X002A0010時,保護模式回調會陷入VWIN32.VXD中一個叫做VWIN32_Int21Dispatch的服務。這正說明了WIN9X還在依賴于MSDos,盡管微軟聲稱WIN9X不再依賴于MSDos。調用規范如下:<BR><BR> my_int21h:push
ecx<BR> push eax ;類似DOS下INT21H的AX中傳入的功能號<BR> push
002A0010h<BR> call dword ptr
[ebp+a_VxDCall]<BR> ret<BR> 我們可以將上面VxdCall0函數的入口處第三條遠調用指令訪問的Kernel32.dll數據段中用戶態可寫地址BFFC9004Υ媧⒌?FWORD'六個字節改為指向我們自己鉤子函數的地址,并在鉤子中檢查傳入服務號和功能號來確定是否是請求VWIN32_Int21Dispatch中的某個文件服務。著名的HPS病毒就利用了這個技術在用戶態下直接截獲系統中的文件操作,但這種方法截獲的也僅僅是一小部分文件操作。<BR><BR>1.2.4加密變形病毒<BR>加密變形病毒是虛擬機一章的重點內容,將放到相關章節中介紹。<BR><BR>1.2.5反跟蹤/反虛擬執行病毒<BR>反跟蹤/反虛擬執行病毒和虛擬機聯系密切,所以也將放到相應的章節中介紹。<BR><BR>1.2.6直接API調用<BR>直接API調用是當今WIN32病毒常用的手段,它指的是病毒在運行時直接定位API函數在內存中的入口地址然后調用之的一種技術。普通程序進行API調用時,編譯器會將一個API調用語句編譯為幾個參數壓棧指令后跟一條間接調用語句(這是指Microsoft編譯器,Borland編譯器使用JMP
<BR><BR>DWORD PTR [XXXXXXXXh])形式如下:<BR><BR> push
arg1<BR> push arg2<BR> ……<BR> call dword
ptr[XXXXXXXXh]<BR>地址XXXXXXXXh在程序映象的導入(Import
Section)段中,當程序被加載運行時,由裝入器負責向里面添入API函數的地址,這就是所謂的動態鏈接機制。病毒由于為了避免感染一個可執行文件時在文件的導入段中構造病毒體代碼中用到的API的鏈接信息,它選擇運用自己在運行時直接定位API函數地址的代碼。其實這些函數地址對于操作系統的某個版本是相對固定的,但病毒不能依賴于此。現在較為流行的做法是先定位包含API函數的動態連接庫的裝入基址,然后在其導出段(Export
Section)中尋找到需要的API地址。后面一步幾乎沒有難度,只要你熟悉導出段結構即可。關鍵在于第一步--確定DLL裝入地址。其實系統DLL裝入基址對于操作系統的某個版本也是固定的,但病毒為確保其穩定性仍不能依賴這一點。目前病毒大都利用一個叫做結構化異常處理的技術來捕獲病毒體引發的異常。這樣一來病毒就可以在一定內存范圍內搜索指定的DLL(DLL使用PE格式,頭部有固定標志),而不必擔心會因引發頁面錯誤而被操作系統殺掉。<BR><BR>由于異常處理和后面的反虛擬執行技術密切相關,所以特將結構化異常處理簡單解釋如下:<BR><BR>共有兩類異常處理:最終異常處理和每線程異常處理。<BR><BR>其一:最終異常處理<BR><BR>當你的進程中無論哪個線程發生了異常,操作系統將調用你在主線程中調用SetUnhandledExceptionFilter建立的異常處理函數。你也無須在退出時拆去你安裝的處理代碼,系統會為你自動清除。<BR><BR> PUSH
OFFSET FINAL_HANDLER <BR> CALL SetUnhandledExceptionFilter
<BR> ……<BR> CALL ExitProcess
<BR> ;************************************
<BR> FINAL_HANDLER: <BR> …… <BR> ;(eax=-1
reload context and continue) <BR> MOV EAX,1 <BR> RET
;program entry point <BR> ……<BR> ;code covered by
final handler <BR> ……<BR> ;code to provide a polite
exit <BR> ……<BR> ;eax=1 stops display of closure box
<BR> ;eax=0 enables display of the box
<BR> 其二:每線程異常處理<BR><BR>FS中的值是一個十六位的選擇子,它指向包含線程重要信息的數據結構TIB,線程信息塊。其的首雙字節指向我們稱為ERR的結構:<BR><BR>1st
dword +0 pointer to next err structure<BR><BR>(下一個err結構的指針) <BR><BR>2nd
dword +4 pointer to own exception
handler<BR><BR>(當前一級的異常處理函數的地址)<BR><BR>所以異常處理是呈練狀的,如果你自己的處理函數捕捉并處理了這個異常,那么當你的程序發生了異常時,操作系統就不會調用它缺省的處理函數了,也就不會出現一個討厭的執行了非法操作的紅叉。<BR><BR>下面是cih的異常段:<BR><BR>MyVirusStart:<BR> push
ebp<BR> lea eax, [esp-04h*2]<BR> xor ebx,
ebx<BR> xchg eax, fs:[ebx]
;交換現在的err結構和前一個結構的地址<BR> ; eax=前一個結構的地址<BR> ;
fs:[0]=現在的err結構指針(在堆棧上)<BR> call
@0<BR> @0:<BR> pop ebx<BR> lea ecx,
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -