国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁技術(shù)文章
文章詳情頁

《Undocumented Windows 2000 Secrets》翻譯 --- 第四章(3)

瀏覽:72日期:2023-08-27 15:07:50

第四章 探索 Windows 2000 的內(nèi)存管理機(jī)制

翻譯: Kendiv ( [email protected] )

更新: Sunday, February 17, 2005

聲明:轉(zhuǎn)載請注明出處,并保證文章的完整性,本人保留譯文的所有權(quán)利。

Memory Spy Device 示例

微軟對 Windows NT 和 2000 說的最多的就是它們是 安全的操作系統(tǒng) 。它們不但在網(wǎng)絡(luò)環(huán)境中加入了用戶驗證系統(tǒng),同時還加強(qiáng)了系統(tǒng)的穩(wěn)健性( robustness ),以進(jìn)一步降低錯誤應(yīng)用程序危及系統(tǒng)完整性的概率,這些錯誤的程序可能使用了非法的指針或者在其內(nèi)存數(shù)據(jù)結(jié)構(gòu)以外的地方進(jìn)行了寫入操作。這些在 Windows 3.x 上都是十分讓人頭疼得問題,因為 Windows 3.x 系統(tǒng)和所有的應(yīng)用程序共享單一的內(nèi)存空間。 Windows NT 為系統(tǒng)和應(yīng)用程序內(nèi)存以及并發(fā)的進(jìn)程提供了完全獨立的內(nèi)存空間。每個進(jìn)程都有其獨立的 4GB 地址空間,如 4-2 所示。無論何時發(fā)生任務(wù)切換,當(dāng)前的地址空間都會被換出( switch out ),同時另一個被映射進(jìn)來,它們各自使用不同的段寄存器、頁表和其他內(nèi)存管理結(jié)構(gòu)。這種設(shè)計避免了應(yīng)用程序無意中修改另一個程序所使用的內(nèi)存。由于每個進(jìn)程必然會要求訪問系統(tǒng)資源,所以在 4GB 空間中總是包含一些系統(tǒng)數(shù)據(jù)和代碼,并采用了一個不同的技巧來保護(hù)這些內(nèi)存區(qū)域不被惡意程序代碼所覆寫( overwritten )。

Windows 2000 的內(nèi)存分段

Windows 2000 繼承了 Windows NT 4.0 的基本內(nèi)存分段模型,默認(rèn)情況下,該模型將 4GB 地址空間劃分為相等的兩塊。低一半的地址范圍是: 0x00000000 ---- 0x7FFFFFFF ,其中包含運行于用戶模式(用 Intel 的術(shù)語來說是,是特權(quán)級 3 或 Ring 3 )的應(yīng)用程序的數(shù)據(jù)和代碼。高一半的地址范圍是: 0x80000000 --- 0xFFFFFFFF ,默認(rèn)全部保留給系統(tǒng)使用,位于這一范圍的代碼運行于內(nèi)核模式(即特權(quán)級為 0 或 Ring 0 )。特權(quán)級決定了代碼可以執(zhí)行什么操作以及可以訪問那一個塊內(nèi)存。這意味著對于低特權(quán)級的代碼來說,會被禁止執(zhí)行某些 CPU 指令或訪問某些內(nèi)存區(qū)域。例如,如果一個用戶模式下的程序觸及了任何 0x80000000 (即 4GB 地址空間中的高一半)以上的地址,系統(tǒng)會拋出一個異常并同時終止該程序的運行,不會給其任何機(jī)會。

圖 4-5. 用戶模式下不能訪問 0x80000000 以上的地址

圖 4-5 展示了程序試圖讀取 0x80000000 地址時的情況。這種嚴(yán)格的訪問限制對于系統(tǒng)的完整性來說是好事,但對于調(diào)試工具就不是什么好消息了,因為調(diào)試工具需要訪問所有可用內(nèi)存。幸運的是,存在著一個簡單的方法:采用內(nèi)核驅(qū)動程序,和系統(tǒng)本身類似,它也運行于高特權(quán)級(即 Ring 3 ),因此它們可以執(zhí)行所有的 CPU 指令,可訪問所有的內(nèi)存區(qū)域。這其中的訣竅就是將一個 Spy 驅(qū)動程序注入系統(tǒng),用它來訪問需要的內(nèi)存,并將讀到的內(nèi)容發(fā)送到它的搭檔程序,該搭檔程序會在用戶模式下等待。當(dāng)然,內(nèi)核驅(qū)動程序不能讀取虛擬內(nèi)存地址,而且得不到分頁機(jī)制的支持。因此,這樣的驅(qū)動程序必須在訪問一個地址之前小心的檢查它,以避免出現(xiàn)藍(lán)屏死機(jī)( Blue Screen Of Death , BSOD )。相對于應(yīng)用程序引發(fā)的異常(僅會終止出現(xiàn)問題的程序),驅(qū)動程序引發(fā)的異常會停止整個系統(tǒng),并強(qiáng)迫進(jìn)行重啟。

設(shè)備 I/O 控制 Dispatcher ( Device I/O Control Dispatcher

本書光盤上有一個通用 Spy Device 的源代碼,該 Spy Device 作為內(nèi)核驅(qū)動程序?qū)崿F(xiàn)。可以在 srcw2k_spy 目錄下找到它的源代碼。這個設(shè)備基于第三章的驅(qū)動向?qū)a(chǎn)生的驅(qū)動程序骨架。其用戶模式下的接口為 w2k_spy.sys , w2k_spy.sys 采用 Win32 的設(shè)備 I/O 控制( IOCTL ),在第三章中曾簡要的談過 IOCTL 。 Spy Device 定義了一個名為 Devicew2k_spy 的設(shè)備和一個符號鏈接 DosDevicesw2k_spy ,定義符號鏈接是為了能在用戶模式下訪問該設(shè)備。非常可笑的是符號鏈接的名字空間居然是 DosDevice ,而在這兒,我們使用的可不是一個 DOS 設(shè)備驅(qū)動。這就像歷史上有名的 root ,原本是叫做石頭的 J 。安裝好符號鏈接后,驅(qū)動程序就可以被用戶模式下的任何模塊打開了,方法是:使用 Win32 API 函數(shù) CreateFile() ,路徑為 .w2k_spy 。字符串 . 是通用轉(zhuǎn)義符,表示本地設(shè)備。例如, .C : 指向本地硬盤上的 C :分區(qū)。從 SDK 的文檔中可了解 CreateFile() 的更多細(xì)節(jié)。

該驅(qū)動程序的頭文件有一部分已經(jīng)由 列表 4-2 列表 4-5 給出。這個文件有些像 DLL 的頭文件:它包含在編譯過程中,模塊所需的定義,而且還為客戶端程序提供了足夠的接口信息。 DLL 和驅(qū)動程序以及客戶端程序都包含相同的頭文件,但每個模塊會取出各自所需的定義以完成正確的操作。不過,頭文件的這種兩面性給內(nèi)核驅(qū)動程序帶來的麻煩要遠(yuǎn)多于給 DLL 帶來的,這都是因為微軟給驅(qū)動程序提供的特殊開發(fā)環(huán)境所致。不幸的是, DDK 中的頭文件并不能和 SDK 中的 Win32 文件兼容。至少在 C 工程,二者的頭文件是不能混合使用的。這樣的結(jié)果就是陷入了僵局,此種情況下,內(nèi)核驅(qū)動可以訪問的常量、宏和數(shù)據(jù)類型對于客戶端程序來說是卻是無法使用的。因此, w2k_spy.c 定義了一個名為 _W2K_SPY_SYS_ 的標(biāo)志常量, w2k_spy.h 通過 #ifdef…..#else…..#endif 來檢查該常量是否出現(xiàn),以決定需要補(bǔ)充哪些缺少的定義。這意味著,所有出現(xiàn)在 #ifdef _W2K_SPY_SYS_ 之后的定義僅可被驅(qū)動代碼看到,位于 #else 之后的則專用于客戶端程序。 w2k_spy.h 中條件語句之外的所有部分被這兩個模塊同時使用。

在第三章中,在討論我的驅(qū)動向?qū)r,我給出了向?qū)傻尿?qū)動程序骨架,如 列表 3-3 所示。由該驅(qū)動向?qū)傻男碌尿?qū)動工程均開始于 DeviceDispatcher() 函數(shù)。該函數(shù)接受一個設(shè)備上下文指針,以及一個指向 IRP ( I/O 請求包)的指針,該 IRP 隨后將會被分派。向?qū)У臉影宕a已經(jīng)處理了基本的 I/O 請求: IRP_MJ_CREATE 、 IRP_MJ_CLEANUP 和 IRP_MJ_CLSE ,當(dāng)客戶要關(guān)閉一個設(shè)備時,會給該設(shè)備發(fā)送這些 I/O 請求。 DeviceDispatcher() 針對這些請求只是簡單的返回 STATUS_SUCCESS ,因此設(shè)備可以被正確的打開和關(guān)閉。對于某些設(shè)備,這種動作已經(jīng)足夠,但有些設(shè)備還需要初始化和清理代碼,這些代碼多少都有些復(fù)雜。對于其他的請求,第三章中的驅(qū)動程序骨架總是返回 STATUS_NOT_IMPLEMENTED 。擴(kuò)展該骨架代碼的第一步是修改默認(rèn)的動作,以便處理更多的 I/O 請求。就像 w2k_spy.sys 的主要任務(wù)之一:通過 IOCTL 調(diào)用將在用戶模式下無法訪問的數(shù)據(jù)發(fā)送給 Win32 應(yīng)用程序,因此首先需要在 DeviceDispatcher() 中添加處理 IRP_MJ_DEVICE_CONTROL 的函數(shù)。 列表 4-6 給出了更新后的代碼。

NTSTATUS DeviceDispatcher (PDEVICE_CONTEXT pDeviceContext,

PIRP pIrp)

{

PIO_STACK_LOCATION pisl;

DWord dInfo = 0;

NTSTATUS ns = STATUS_NOT_IMPLEMENTED;

pisl = IoGetCurrentIrpStackLocation (pIrp);

switch (pisl->MajorFunction)

{

case IRP_MJ_CREATE:

case IRP_MJ_CLEANUP:

case IRP_MJ_CLOSE:

{

ns = STATUS_SUCCESS;

break;

}

case IRP_MJ_DEVICE_CONTROL:

{

ns = SpyDispatcher (pDeviceContext,

pisl->Parameters.DeviceIoControl.IoControlCode,

pIrp->AssociatedIrp.SystemBuffer,

pisl->Parameters.DeviceIoControl.InputBufferLength,

pIrp->AssociatedIrp.SystemBuffer,

pisl->Parameters.DeviceIoControl.OutputBufferLength,

&dInfo);

break;

}

}

pIrp->IoStatus.Status = ns;

pIrp->IoStatus.Information = dInfo;

IoCompleteRequest (pIrp, IO_NO_INCREMENT);

return ns;

}

列表 4-6. 為 Dispatcher 增加處理的 IRP_MJ_DEVICE_CONTROL 函數(shù)

列表 4-6 中的 IOCTL 處理代碼非常簡單,它僅調(diào)用了 SpyDispatcher() ,并將一個擴(kuò)展后的 IRP 結(jié)構(gòu)和當(dāng)前 I/O 堆棧位置作為參數(shù)傳遞給 SpyDispatcher() 。 SpyDispatcher() 在 列表 4-7 中給出,該函數(shù)需要如下的參數(shù):

l pDeviceContext 一個驅(qū)動程序的設(shè)備上下文指針。驅(qū)動程序向?qū)峁┝说幕?Device_Context 結(jié)構(gòu),該結(jié)構(gòu)中包含驅(qū)動程序和設(shè)備對象指針(參見 列表 3-4 )。不過, Spy 驅(qū)動程序在該結(jié)構(gòu)中增加了一對私有成員。

l dCode 指定了 IOCTL 編碼,以確定 Spy 設(shè)備需要執(zhí)行的命令。一個 IOCTL 編碼是一個 32 位整數(shù),它包含 4 個位域,如 4-6 所示。

l pInput 指向一個輸入緩沖區(qū),用于給 IOCTL 提供輸入數(shù)據(jù)。

l dInput 輸入緩沖區(qū)的大小。

l pOutput 指向用來接收 IOCTL 輸出數(shù)據(jù)的輸出緩沖區(qū)。

l dOutput 輸出緩沖區(qū)的大小

l pdInfo 指向一個 DWORD 變量,該變量保存寫入輸出緩沖區(qū)中的字節(jié)數(shù)。

圖 4-6. 設(shè)備 I/O 控制編碼的結(jié)構(gòu)

根據(jù)所用的 IOCTL 使用的傳輸模式,輸入 / 輸出緩沖區(qū)會以不同的方式從系統(tǒng)傳遞給驅(qū)動程序。 Spy 設(shè)備使用已緩存的 I/O ( buffered I/O ),系統(tǒng)將輸入數(shù)據(jù)復(fù)制到一個安全的緩沖區(qū)(此緩沖區(qū)由系統(tǒng)自動分配)中,在返回時,將指定數(shù)目的數(shù)據(jù)從同樣的系統(tǒng)緩沖區(qū)中復(fù)制到調(diào)用者提供的輸出緩沖區(qū)中。一定要牢記:在這種情況下,輸入和輸出緩沖區(qū)是重疊的,因此 IOCTL 的處理代碼必須在向輸出緩沖區(qū)中寫入任何數(shù)據(jù)之前,保存所有它稍后可能需要使用的輸入數(shù)據(jù)。系統(tǒng) I/O 緩沖區(qū)的指針保存在 IRP 結(jié)構(gòu)中的 SystemBuffer 成員中(參見 ntddk.h )。輸入 / 輸出緩沖區(qū)的大小保存在一個不同的地方,它們是 IRP 的參數(shù)成員 DeviceIoControl 的一部分,分別為 InputBufferLength 和 OutputBufferLength 。 DeviceIoControl 子結(jié)構(gòu)還通過其 IoControlCode 成員提供了 IOCTL 編碼。有關(guān) Windows NT/2000 的 IOCTL 的傳輸模式的信息以及它們?nèi)绾蝹魅?/ 傳出數(shù)據(jù),請參考我在 Windows Developer's Journal(Schreiber 1997) 發(fā)表的文章“ A Spy Filter Driver for Windows NT ”。

NTSTATUS SpyDispatcher (PDEVICE_CONTEXT pDeviceContext,

DWORD dCode,

PVOID pInput,

DWORD dInput,

PVOID pOutput,

DWORD dOutput,

PDWORD pdInfo)

{

SPY_MEMORY_BLOCK smb;

SPY_PAGE_ENTRY spe;

SPY_CALL_INPUT sci;

PHYSICAL_ADDRESS pa;

DWORD dValue, dCount;

BOOL fReset, fPause, fFilter, fLine;

PVOID pAddress;

PBYTE pbName;

HANDLE hObject;

NTSTATUS ns = STATUS_INVALID_PARAMETER;

MUTEX_WAIT (pDeviceContext->kmDispatch);

*pdInfo = 0;

switch (dCode)

{

case SPY_IO_VERSION_INFO:

{

ns = SpyOutputVersionInfo (pOutput, dOutput, pdInfo);

break;

}

case SPY_IO_OS_INFO:

{

ns = SpyOutputOsInfo (pOutput, dOutput, pdInfo);

break;

}

case SPY_IO_SEGMENT:

{

if ((ns = SpyInputDword (&dValue,

pInput, dInput))

== STATUS_SUCCESS)

{

ns = SpyOutputSegment (dValue,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_INTERRUPT:

{

if ((ns = SpyInputDword (&dValue,

pInput, dInput))

== STATUS_SUCCESS)

{

ns = SpyOutputInterrupt (dValue,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_PHYSICAL:

{

if ((ns = SpyInputPointer (&pAddress,

pInput, dInput))

== STATUS_SUCCESS)

{

pa = MmGetPhysicalAddress (pAddress);

ns = SpyOutputBinary (&pa, PHYSICAL_ADDRESS_,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_CPU_INFO:

{

ns = SpyOutputCpuInfo (pOutput, dOutput, pdInfo);

break;

}

case SPY_IO_PDE_ARRAY:

{

ns = SpyOutputBinary (X86_PDE_ARRAY, SPY_PDE_ARRAY_,

pOutput, dOutput, pdInfo);

break;

}

case SPY_IO_PAGE_ENTRY:

{

if ((ns = SpyInputPointer (&pAddress,

pInput, dInput))

== STATUS_SUCCESS)

{

SpyMemoryPageEntry (pAddress, &spe);

ns = SpyOutputBinary (&spe, SPY_PAGE_ENTRY_,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_MEMORY_DATA:

{

if ((ns = SpyInputMemory (&smb,

pInput, dInput))

== STATUS_SUCCESS)

{

ns = SpyOutputMemory (&smb,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_MEMORY_BLOCK:

{

if ((ns = SpyInputMemory (&smb,

pInput, dInput))

== STATUS_SUCCESS)

{

ns = SpyOutputBlock (&smb,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_HANDLE_INFO:

{

if ((ns = SpyInputHandle (&hObject,

pInput, dInput))

== STATUS_SUCCESS)

{

ns = SpyOutputHandleInfo (hObject,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_HOOK_INFO:

{

ns = SpyOutputHookInfo (pOutput, dOutput, pdInfo);

break;

}

case SPY_IO_HOOK_INSTALL:

{

if (((ns = SpyInputBool (&fReset,

pInput, dInput))

== STATUS_SUCCESS)

&&

((ns = SpyHookInstall (fReset, &dCount))

== STATUS_SUCCESS))

{

ns = SpyOutputDword (dCount,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_HOOK_REMOVE:

{

if (((ns = SpyInputBool (&fReset,

pInput, dInput))

== STATUS_SUCCESS)

&&

((ns = SpyHookRemove (fReset, &dCount))

== STATUS_SUCCESS))

{

ns = SpyOutputDword (dCount,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_HOOK_PAUSE:

{

if ((ns = SpyInputBool (&fPause,

pInput, dInput))

== STATUS_SUCCESS)

{

fPause = SpyHookPause (fPause);

ns = SpyOutputBool (fPause,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_HOOK_FILTER:

{

if ((ns = SpyInputBool (&fFilter,

pInput, dInput))

== STATUS_SUCCESS)

{

fFilter = SpyHookFilter (fFilter);

ns = SpyOutputBool (fFilter,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_HOOK_RESET:

{

SpyHookReset ();

ns = STATUS_SUCCESS;

break;

}

case SPY_IO_HOOK_READ:

{

if ((ns = SpyInputBool (&fLine,

pInput, dInput))

== STATUS_SUCCESS)

{

ns = SpyOutputHookRead (fLine,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_HOOK_WRITE:

{

SpyHookWrite (pInput, dInput);

ns = STATUS_SUCCESS;

break;

}

case SPY_IO_MODULE_INFO:

{

if ((ns = SpyInputPointer (&pbName,

pInput, dInput))

== STATUS_SUCCESS)

{

ns = SpyOutputModuleInfo (pbName,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_PE_HEADER:

{

if ((ns = SpyInputPointer (&pAddress,

pInput, dInput))

== STATUS_SUCCESS)

{

ns = SpyOutputPeHeader (pAddress,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_PE_EXPORT:

{

if ((ns = SpyInputPointer (&pAddress,

pInput, dInput))

== STATUS_SUCCESS)

{

ns = SpyOutputPeExport (pAddress,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_PE_SYMBOL:

{

if ((ns = SpyInputPointer (&pbName,

pInput, dInput))

== STATUS_SUCCESS)

{

ns = SpyOutputPeSymbol (pbName,

pOutput, dOutput, pdInfo);

}

break;

}

case SPY_IO_CALL:

{

if ((ns = SpyInputBinary (&sci, SPY_CALL_INPUT_,

pInput, dInput))

== STATUS_SUCCESS)

{

ns = SpyOutputCall (&sci,

pOutput, dOutput, pdInfo);

}

break;

}

}

MUTEX_RELEASE (pDeviceContext->kmDispatch);

return ns;

}

列表 4-7. Spy 驅(qū)動程序的內(nèi)部命令 Dispatcher

#define CTL_CODE (DeviceType, Function, Method, Access)

(( (DeviceType) << 16 ) | ( Access << 14 ) | ( (Function) << 2 ) (Method) )

列表 4-8. 用來構(gòu)建 I/O 控制編碼的 CTL_CODE() 宏

DDK 的主要頭文件 ntddk.h 和 SDK 中的 Win32 文件 winioctl.h 均定義了一個簡單但非常有用的宏 ---- CTL_CLOSE() ,如 列表 4-8 所示。該宏可方便的建立 4-6 所示的 IOCTL 編碼。該編碼中的四個部分分別服務(wù)于以下四個目的:

1. DeviceType 這是一個 16 位的設(shè)備類型 ID 。 ntddk.h 列出了一對預(yù)定義的類型,由符號常量 FILE_DEVICE_* 表示。 0x0000 到 0x7FFF 保留給微軟內(nèi)部使用,開發(fā)人員可使用 0x8000 到 0xFFFF 。 Spy 驅(qū)動程序定義了它自己的設(shè)備 ID : FILE_DEVICE_SPY ,其值為 0x8000 。

2. 2 位的訪問檢查值用來確定 IOCTL 操作所需的訪問權(quán)限。可能的值有: FILE_ANY_ACCESS (0) , FILE_READ_ACCESS (1) , FILE_WRITE_ACCESS (2) 和最后兩個的組合: FILE_READ_ACCESS | FILE_WRITE_ACCESS (3) 。詳見 ntddk.h 。

3. 12 個位的 ID 表示所選擇的操作函數(shù),所選操作將由設(shè)備來執(zhí)行。 0x0000 到 0x7FFF 保留給微軟內(nèi)部使用,開發(fā)人員可使用 0x8000 到 0xFFFF 。 Spy 設(shè)備采用的 IOCTL 函數(shù) ID 位于 0x8000 到 0xFFFF 。

4. 傳輸模式占用 2 個位,可在四個可用 I/O 傳輸模式中選擇一個,這四個模式為: METHOD_BUFFERED (0) , METHOD_IN_DIRECT (1) , METHOD_OUT_DIRECT (2) 和 METHOD_NETTHER (3) ,可在 ntddk.h 中找到這些定義。 Spy 設(shè)備針對所有請求使用 METHOD_BUFFERED ,這是一個非常安全但有些慢的模式,因為數(shù)據(jù)需要在客戶端和系統(tǒng)緩沖區(qū)之間進(jìn)行復(fù)制。因為 Memory Spy 對 I/O 的處理速度并不敏感,所以選擇安全是一個不錯的注意。如果你希望知道其他模式的細(xì)節(jié),請參考我在 Windows Developer's Journal(Schreiber 1997) 發(fā)表的文章“ A Spy Filter Driver for Windows NT ”

表 4-2 列出了 w2k_spy.sys 支持的所有 IOCTL 函數(shù)。 0 到 10 的函數(shù) ID 為最基本的內(nèi)存探測函數(shù),絕大部分的任務(wù)都會用到它們;本章稍候?qū)⒂懻撍鼈儭JS嗟暮瘮?shù) ID 從 11 到 23 分屬于不同的 IOCTL 組,在下一章我們將討論它們,在下一章,我們將討論 Native API hook 和在用戶模式下調(diào)用內(nèi)核。注意某些 IOCTL 編碼需要寫入權(quán)限,由第 15 號位表示(參見 4-6 )。確切的說,所有形如 0x80006nnn 的 IOCTL 命令只需讀權(quán)限,而形如 0x8000Ennn 的命令需要讀 / 寫權(quán)限。典型的要求讀權(quán)限的例子是 CreateFile() ,它通過指定 dwDesiredAccess 參數(shù)為 GENERIC_READ 和 GENERIC_WRITE 的組合來打開設(shè)備。

表 4-2 最左面的函數(shù)名稱同樣出現(xiàn)在 SpyDispatcher() (見 列表 4-7 )中那個龐大的 switch/case 語句中。這些函數(shù)首先獲取設(shè)備的 dispatcher mutex ,這樣就能保證,如果一個以上的客戶端或一個多線程的程序和設(shè)備通訊時,在同一時間只有一個請求被執(zhí)行。 MUTEX_WAIT() 是 KeWaitForMutexObject() 的外包宏( wrapper marco ), KeWaitForMutexObject() 至少需要 5 個參數(shù)。 KeWaitForMutexObject() 本身也是一個宏,它將傳入的參數(shù)向前傳遞給 KeWaitForSingleObject() 。 列表 4-9 給出了 MUTEX_WAIT() 以及它的伙伴 MUTEX_RELEASE() 和 MUTEX_INITIALIZE() 。在 mutex 對象變?yōu)橛行盘枺?signaled )狀態(tài)后,根據(jù)接收到的 IOCTL 編碼, SpyDispatcher() 會轉(zhuǎn)向不同的分支,每個分支都包含多種簡單的代碼序列。

表 4-2. w2k_spy.sys 支持的 IOCTL 函數(shù)

函數(shù)名稱

ID

IOCTL 編碼

SPY_IO_VERSION_INFO

0

0x80006000

返回 Spy 的版本信息

SPY_IO_OS_INFO

1

0x80006004

返回操作系統(tǒng)的版本信息

SPY_IO_SEGMENT

2

0x80006008

返回一個段的屬性

SPY_IO_INTERRUPT

3

0x8000600C

返回一個中斷門的屬性

SPY_IO_PHYSICAL

4

0x80006010

線性地址轉(zhuǎn)換為物理地址

SPY_IO_CPU_INFO

5

0x80006014

返回特殊 CPU 寄存器的值

SPY_IO_PDE_ARRAY

6

0x80006018

返回位于 0xC0300000 的 PDE 數(shù)組

SPY_IO_PAGE_ENTRY

7

0x8000601C

Return the PDE or PTE of a linear address

SPY_IO_MEMORY_DATA

8

0x80006020

返回內(nèi)存塊中的內(nèi)容

SPY_IO_MEMORY_BLOCK

9

0x80006024

返回內(nèi)存塊中的內(nèi)容

SPY_IO_HANDLE_INFO

10

0x80006028

從句柄中查找對象屬性

SPY_IO_HOOK_INFO

11

0x8000602C

返回有關(guān) Native API Hook 的信息

SPY_IO_HOOK_INSTALL

12

0x8000E030

安裝 Native API Hook

SPY_IO_HOOK_REMOVE

13

0x8000E034

移除一個 Native API Hook

SPY_IO_HOOK_PAUSE

14

0x8000E038

暫停 / 恢復(fù) Hook 協(xié)議

SPY_IO_HOOK_FILTER

15

0x8000E03C

允許 / 禁止 Hook 協(xié)議過濾器

SPY_IO_HOOK_RESET

16

0x8000E040

清除 Hook 協(xié)議

SPY_IO_HOOK_READ

17

0x8000E044

從 Hook 協(xié)議中讀取數(shù)據(jù)

SPY_IO_HOOK_WRITE

18

0x8000E048

向 Hook 協(xié)議中寫入輸入

SPY_IO_MODULE_INFO

19

0x8000E04C

返回已加載模塊的信息

SPY_IO_PE_HEADER

20

0x8000E050

返回 IMAGE_NT_HEADERS 數(shù)據(jù)

SPY_IO_PE_EXPORT

21

0x8000E054

返回 IMAGE_EXPORT_DirectorY 數(shù)據(jù)

SPY_IO_PE_SYMBOL

22

0x8000E058

返回導(dǎo)出的系統(tǒng)符號的地址

SPY_IO_CALL

23

0x8000E05C

調(diào)用已加載模塊中的一個函數(shù)

#define MUTEX_INITIALIZE(_mutex)

KeInitializeMutex

(&(_mutex), 0)

#define MUTEX_WAIT(_mutex)

KeWaitForMutexObject

(&(_mutex), Executive, KernelMode, FALSE, NULL)

#define MUTEX_RELEASE(_mutex)

KeReleaseMutex

(&(_mutex), FALSE)

列表 4-9. 管理 Kernel-Mutex 的宏

SpyDispatcher() 使 用一對幫助函數(shù)來讀取輸入?yún)?shù),以獲取被請求的數(shù)據(jù),并將產(chǎn)生的數(shù)據(jù)寫入調(diào)用者提供的輸出緩沖區(qū)中。就像前面提到的,內(nèi)核模式的驅(qū)動程序總是過分挑剔的對 待它接受到的來自用戶模式的參數(shù)。以驅(qū)動程序的觀點來看,所有用戶模式下的代碼都是有害的,它們除了讓系統(tǒng)崩潰就什么都不知道了。這種多少有些多疑癥的觀 點并不是荒謬的 ---- 僅有很小的比率會導(dǎo)致整個系統(tǒng)立即終止,同時出現(xiàn)藍(lán)屏。因此,如果一個客戶端程序說:“這是我的緩沖區(qū) ----- 它最多可容納 4,096 個字節(jié)”,驅(qū)動程序不會接受這個緩沖區(qū) ---- 即使該緩沖區(qū)指向有效的內(nèi)存,并且其大小也是正確的。在 IOCTL 的可緩沖的 I/O 模式( Buffered I/O )下(如果 IOCTL 編碼的模式部分為 METHOD_BUFFERED ),系統(tǒng)會很小心的檢查并分配一個足夠容納所有輸入 / 輸出數(shù)據(jù)的緩沖區(qū)。然而,其他的 I/O 傳輸模式,尤其是 METHOD_NETTHER ,驅(qū)動程序會接受原始的用戶模式的緩沖區(qū)指針。

標(biāo)簽: Windows系統(tǒng)
主站蜘蛛池模板: 久久人视频 | 亚洲欧美18v中文字幕高清 | 日韩精品午夜视频一区二区三区 | www毛片com| 欧美成人性毛片免费版 | 一级看片| 欧美在线一区二区三区欧美 | 综合图片亚洲网友自拍10p | 免费萌白酱国产一区二区三区 | 欧美日韩精品一区二区免费看 | 97精品国产综合久久久久久欧美 | 久草在线观看资源 | 99久久精品6在线播放 | 波多结衣一区二区三区 | 欧美国产精品一区二区免费 | 国产精品免费看久久久久 | 日产国产精品亚洲系列 | 久久国产成人亚洲精品影院老金 | 亚洲一区欧洲一区 | 久久一区二区三区免费播放 | 日韩精品免费一区二区 | 加勒比heyzo 加勒比久久综合 | 欧美精品hdvdeosex4k | 伊人青| 久久99中文字幕 | 国产精品福利午夜h视频 | 日韩欧美一区二区不卡看片 | 欧美日韩在线视频播放 | 国产性做久久久久久 | 国产图片亚洲精品一区 | 亚洲天堂日韩在线 | 1024香蕉国产在线视频 | 加勒比日本道 | 欧美精品日日鲁夜夜添 | 亚洲视频在线观看网址 | 亚洲精品91香蕉综合区 | 久久精品国产一区二区三区不卡 | 九九黄色影院 | 欧美一级专区免费大片野外交 | 精产网红自拍在线 | 伊在人亚洲香蕉精品区 |