眾所周知,在Windows95/98
的Win32 on Intel x86 體系中利用了處理器的三環保護模型中的零環(Ring0,最高權限級別)和三環(Ring3,最低權限級別)。一般應用程序都運行在Ring3
下,受到嚴格的" 保護",只能規矩地使用Win32API。如果我們想進行一些系統級的操作,例如在嵌入匯編中使用諸如"Mov
EAX,CR0",或像在DOS 下那樣調用一些必不可少的系統服務(如BIOS,DPMI
服務)而用"Int xx",都會導致" 非法操作"。但這種能力有時是必不可少的,一到這種時候Microsoft
就" 建議編寫一個VxD"。VxD 大家早有所聞了,在VxD
里,不但可以執行CPU 的所有指令,而且可以調用VMM(
虛擬機管理器)和其他VxD
提供的上千個系統級服務。獲得這一能力的最本質原因在于它運行在Ring0,與系統內核同一級別。但是它體系的復雜性、開發工具的不易獲得、幫助文檔的不完備,使Microsoft
排除了一大批程序員和競爭對手。而將在Windows2000(Windows98
也開始支持)中取代VxD 的WDM 對Win95 程序員也是個噩夢,它需要了解Windows
NT 核心驅動模型。
有沒有簡單一些的辦法呢?我們可以令一個普通Win32
應用程序運行在Ring0 下,從而獲得VxD
的能力嗎?答案是肯定的。下面我們就簡述一下這一技巧,有關Intel
x86 保護模式的基礎知識請大家看有關書籍。
首先此技巧基于以下理論根據:
一、SIDT 指令(將中斷描述符表寄存器IDTR --64 位寬,16 ~47Bit
存有中斷描述符表IDT
基地址--的內容存入指定地址單元)不是特權指令,就是說我們可以在Ring3
下執行該指令,獲得IDT 的基地址,從而修改IDT,增加一個中斷門安置我們的中斷服務,一旦Ring3
程序中產生此中斷,VMM
就會調用此中斷服務程序,而此中斷服務程序就運行在Ring0
下了。這一點與在DOS 下非常相似。
二、Windows95 Win32 應用程序運行一個映射到全部4G
內存的段中,選擇子為0137h,Ring0 中的VxD 運行在另一個映射到全部4G
內存的段中,選擇子028h,這兩個段除了選擇子決定的訪問權限不同外,沒什么不同,各自段中相同的偏移量對應了相同的線性地址。所以我們放在Win32
應用程序中的中斷服務程序可以以Ring3 的段偏移量被Ring0 中的VMM
尋址。
下面我們以具體例子進一步說明,程序中有詳細注釋。
這是一個Win32 Console Program(控制臺應用程序),雖然運行中看起來很像DOS
筐中運行的實模式DOS 程序,但它是貨真價實的運行在Ring3 下的Win32
程序。用Visual C ++5.0 AppWizard 創建一個Win32 Console Program 項目,
添加以下.CPP 文件, 編譯即可。
#include<conio.h>
#include<iostream.h>
#include<windows.h>
#include<vmm.h>
// 若無DDK 帶下劃線的可略去,
這些語句演示了調用VMM/VXD 服務
DWORDLONG IDTR,SavedGate;
WORD OurGate[4]={0,0x0028,0xee00,0x0000};
// 中斷門描述符格式如下:
DWORD _eax,_ecx,_cr0;
WORD vmmver;
HVM sysvm;
void nothing()
{
//Used to test call in Ring0
sysvm=Get_Sys_VM_Handle();
}
void __declspec( naked ) Ring0Proc(void)
// 中斷例程,運行在Ring0
{
_asm{
mov _eax,eax //
mov _ecx,ecx //
mov eax, CR0
// 測試Ring3 中不能執行的特權指令
mov _cr0,eax //
}
VMMCall(Get_VMM_Version);
// 調用VMM 服務
_asm{
mov vmmver,ax
}
nothing();
// 測試在運行于Ring0 的
中斷例程中調用子
_asm iretd
// 中斷返回, 與在實模式
編程無本質區別
}
void main() // 主程序
{
_asm{
mov eax, offset Ring0Proc
mov [OurGate], ax // 將中斷函數的地址
shr eax, 16 // 填入新造的中斷門
mov [OurGate +6], ax // 描述符
sidt fword ptr IDTR
// 將中斷描述符表寄存器(IDTR)
的內容取出
mov ebx, dword ptr [IDTR +2]
// 取出中斷描述符表(IDT)基地址
add ebx, 8 *9
// 計算Int 9 的描述符應放置的地址選用
Int9 是因為它在Win32 保護模式下未占用
mov edi, offset SavedGate
mov esi, ebx
movsd // 保存原來的Int 9 描述符到
movsd //SavedGate 以便恢復
mov edi, ebx
mov esi, offset OurGate
movsd // 替換原來的中斷門描述符
movsd // 以安裝中斷服務例程
mov eax,0x6200
// 用以測試放在EAX 中的數據
能否正確傳到Ring0 中斷
mov ecx,0
// 用以測試放在ECX 中的數據
能否正確傳到Ring0 中斷
mov ecx,0
// 用以測試放在ECX 中的數據
能否正確傳到Ring0 中斷
// 因為很多VxD 服務都用
此二寄存器傳遞參數
int 9h
// 人為觸發中斷, 平時會出現
保護錯誤藍屏或非法操
// 作對話框,現在安裝了
// 中斷服務例程后,就會通過
//VMM 在Ring0 調用中斷服務例程
?。璕ing0Proc
mov edi, ebx
mov esi, offset SavedGate
movsd // 恢復原來的中斷門描述符
movsd
}
cout<<"CR0="<<_cr0<
運行結果:
此方法的好處一是回避了奇特的VxD文件格式,不用使用匯編語言編程,二是應用程序不用帶一個單獨的VxD
文件,干凈利索。
值得一提的是,許多文章描述著名的CIH 病毒都說其利用了VxD
技術,是說它帶一個單獨的VxD文件嗎?顯然不可能,實際上CIH
病毒使用的就是以上技巧,進入Ring0后調用VMM
服務分配一塊內存,把自身拷貝進去,然后用IFS VxD 的IFSMgr_InstallFileSystemApiHook
服務安裝文件系統監視以感染其他文件,只不過CIH 病毒安裝的是Int 3
中斷,這跟在DOS 下沒什么兩樣。我們也可以對此了解以更好地防范CIH
病毒。
最后還要指出其缺陷,這個技巧僅能用在Windows95/97/98 下,在Windows
NT 下行不通。不過聽說CIH 病毒的作者已經開發了NT 版的CIH
病毒,說明NT 中也有類似的" 后門"?! ?/p>