分享
 
 
 

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

王朝system·作者佚名  2006-11-24
窄屏简体版  字體: |||超大  

第四章 探索 Windows 2000 的内存管理机制

翻译: Kendiv ( [url=http://www.pccode.net].net"fcczj@263.net )

更新: Sunday, February 17, 2005

声明:转载请注明出处,并保证文章的完整性,本人保留译文的所有权利。

Memory Spy Device 示例

微软对 Windows NT 和 2000 说的最多的就是它们是 安全的操作系统 。它们不但在网络环境中加入了用户验证系统,同时还加强了系统的稳健性( robustness ),以进一步降低错误应用程序危及系统完整性的概率,这些错误的程序可能使用了非法的指针或者在其内存数据结构以外的地方进行了写入操作。这些在 Windows 3.x 上都是十分让人头疼得问题,因为 Windows 3.x 系统和所有的应用程序共享单一的内存空间。 Windows NT 为系统和应用程序内存以及并发的进程提供了完全独立的内存空间。每个进程都有其独立的 4GB 地址空间,如 图 4-2 所示。无论何时发生任务切换,当前的地址空间都会被换出( switch out ),同时另一个被映射进来,它们各自使用不同的段寄存器、页表和其他内存管理结构。这种设计避免了应用程序无意中修改另一个程序所使用的内存。由于每个进程必然会要求访问系统资源,所以在 4GB 空间中总是包含一些系统数据和代码,并采用了一个不同的技巧来保护这些内存区域不被恶意程序代码所覆写( overwritten )。

Windows 2000 的内存分段

Windows 2000 继承了 Windows NT 4.0 的基本内存分段模型,默认情况下,该模型将 4GB 地址空间划分为相等的两块。低一半的地址范围是: 0x00000000 ---- 0x7FFFFFFF ,其中包含运行于用户模式(用 Intel 的术语来说是,是特权级 3 或 Ring 3 )的应用程序的数据和代码。高一半的地址范围是: 0x80000000 --- 0xFFFFFFFF ,默认全部保留给系统使用,位于这一范围的代码运行于内核模式(即特权级为 0 或 Ring 0 )。特权级决定了代码可以执行什么操作以及可以访问那一个块内存。这意味着对于低特权级的代码来说,会被禁止执行某些 CPU 指令或访问某些内存区域。例如,如果一个用户模式下的程序触及了任何 0x80000000 (即 4GB 地址空间中的高一半)以上的地址,系统会抛出一个异常并同时终止该程序的运行,不会给其任何机会。

图 4-5. 用户模式下不能访问 0x80000000 以上的地址

图 4-5 展示了程序试图读取 0x80000000 地址时的情况。这种严格的访问限制对于系统的完整性来说是好事,但对于调试工具就不是什么好消息了,因为调试工具需要访问所有可用内存。幸运的是,存在着一个简单的方法:采用内核驱动程序,和系统本身类似,它也运行于高特权级(即 Ring 3 ),因此它们可以执行所有的 CPU 指令,可访问所有的内存区域。这其中的诀窍就是将一个 Spy 驱动程序注入系统,用它来访问需要的内存,并将读到的内容发送到它的搭档程序,该搭档程序会在用户模式下等待。当然,内核驱动程序不能读取虚拟内存地址,而且得不到分页机制的支持。因此,这样的驱动程序必须在访问一个地址之前小心的检查它,以避免出现蓝屏死机( Blue Screen Of Death , BSOD )。相对于应用程序引发的异常(仅会终止出现问题的程序),驱动程序引发的异常会停止整个系统,并强迫进行重启。

设备 I/O 控制 Dispatcher ( Device I/O Control Dispatcher )

本书光盘上有一个通用 Spy Device 的源代码,该 Spy Device 作为内核驱动程序实现。可以在 \src\w2k_spy 目录下找到它的源代码。这个设备基于第三章的驱动向导所产生的驱动程序骨架。其用户模式下的接口为 w2k_spy.sys , w2k_spy.sys 采用 Win32 的设备 I/O 控制( IOCTL ),在第三章中曾简要的谈过 IOCTL 。 Spy Device 定义了一个名为 \Device\w2k_spy 的设备和一个符号链接 \DosDevices\w2k_spy ,定义符号链接是为了能在用户模式下访问该设备。非常可笑的是符号链接的名字空间居然是 \DosDevice ,而在这儿,我们使用的可不是一个 DOS 设备驱动。这就像历史上有名的 root ,原本是叫做石头的 J 。安装好符号链接后,驱动程序就可以被用户模式下的任何模块打开了,方法是:使用 Win32 API 函数 CreateFile() ,路径为 \.w2k_spy 。字符串 \. 是通用转义符,表示本地设备。例如, \.C : 指向本地硬盘上的 C :分区。从 SDK 的文档中可了解 CreateFile() 的更多细节。

该驱动程序的头文件有一部分已经由 列表 4-2 到 列表 4-5 给出。这个文件有些像 DLL 的头文件:它包含在编译过程中,模块所需的定义,而且还为客户端程序提供了足够的接口信息。 DLL 和驱动程序以及客户端程序都包含相同的头文件,但每个模块会取出各自所需的定义以完成正确的操作。不过,头文件的这种两面性给内核驱动程序带来的麻烦要远多于给 DLL 带来的,这都是因为微软给驱动程序提供的特殊开发环境所致。不幸的是, DDK 中的头文件并不能和 SDK 中的 Win32 文件兼容。至少在 C 工程,二者的头文件是不能混合使用的。这样的结果就是陷入了僵局,此种情况下,内核驱动可以访问的常量、宏和数据类型对于客户端程序来说是却是无法使用的。因此, w2k_spy.c 定义了一个名为 _W2K_SPY_SYS_ 的标志常量, w2k_spy.h 通过 #ifdef…..#else…..#endif 来检查该常量是否出现,以决定需要补充哪些缺少的定义。这意味着,所有出现在 #ifdef _W2K_SPY_SYS_ 之后的定义仅可被驱动代码看到,位于 #else 之后的则专用于客户端程序。 w2k_spy.h 中条件语句之外的所有部分被这两个模块同时使用。

在第三章中,在讨论我的驱动向导时,我给出了向导生成的驱动程序骨架,如 列表 3-3 所示。由该驱动向导生成的新的驱动工程均开始于 DeviceDispatcher() 函数。该函数接受一个设备上下文指针,以及一个指向 IRP ( I/O 请求包)的指针,该 IRP 随后将会被分派。向导的样板代码已经处理了基本的 I/O 请求: IRP_MJ_CREATE 、 IRP_MJ_CLEANUP 和 IRP_MJ_CLSE ,当客户要关闭一个设备时,会给该设备发送这些 I/O 请求。 DeviceDispatcher() 针对这些请求只是简单的返回 STATUS_SUCCESS ,因此设备可以被正确的打开和关闭。对于某些设备,这种动作已经足够,但有些设备还需要初始化和清理代码,这些代码多少都有些复杂。对于其他的请求,第三章中的驱动程序骨架总是返回 STATUS_NOT_IMPLEMENTED 。扩展该骨架代码的第一步是修改默认的动作,以便处理更多的 I/O 请求。就像 w2k_spy.sys 的主要任务之一:通过 IOCTL 调用将在用户模式下无法访问的数据发送给 Win32 应用程序,因此首先需要在 DeviceDispatcher() 中添加处理 IRP_MJ_DEVICE_CONTROL 的函数。 列表 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 函数

列表 4-6 中的 IOCTL 处理代码非常简单,它仅调用了 SpyDispatcher() ,并将一个扩展后的 IRP 结构和当前 I/O 堆栈位置作为参数传递给 SpyDispatcher() 。 SpyDispatcher() 在 列表 4-7 中给出,该函数需要如下的参数:

l pDeviceContext 一个驱动程序的设备上下文指针。驱动程序向导提供了的基本 Device_Context 结构,该结构中包含驱动程序和设备对象指针(参见 列表 3-4 )。不过, Spy 驱动程序在该结构中增加了一对私有成员。

l dCode 指定了 IOCTL 编码,以确定 Spy 设备需要执行的命令。一个 IOCTL 编码是一个 32 位整数,它包含 4 个位域,如 图 4-6 所示。

l pInput 指向一个输入缓冲区,用于给 IOCTL 提供输入数据。

l dInput 输入缓冲区的大小。

l pOutput 指向用来接收 IOCTL 输出数据的输出缓冲区。

l dOutput 输出缓冲区的大小

l pdInfo 指向一个 DWORD 变量,该变量保存写入输出缓冲区中的字节数。

图 4-6. 设备 I/O 控制编码的结构

根据所用的 IOCTL 使用的传输模式,输入 / 输出缓冲区会以不同的方式从系统传递给驱动程序。 Spy 设备使用已缓存的 I/O ( buffered I/O ),系统将输入数据复制到一个安全的缓冲区(此缓冲区由系统自动分配)中,在返回时,将指定数目的数据从同样的系统缓冲区中复制到调用者提供的输出缓冲区中。一定要牢记:在这种情况下,输入和输出缓冲区是重叠的,因此 IOCTL 的处理代码必须在向输出缓冲区中写入任何数据之前,保存所有它稍后可能需要使用的输入数据。系统 I/O 缓冲区的指针保存在 IRP 结构中的 SystemBuffer 成员中(参见 ntddk.h )。输入 / 输出缓冲区的大小保存在一个不同的地方,它们是 IRP 的参数成员 DeviceIoControl 的一部分,分别为 InputBufferLength 和 OutputBufferLength 。 DeviceIoControl 子结构还通过其 IoControlCode 成员提供了 IOCTL 编码。有关 Windows NT/2000 的 IOCTL 的传输模式的信息以及它们如何传入 / 传出数据,请参考我在 Windows Developer's Journal(Schreiber 1997) 发表的文章“ 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,

[1] [2] 下一页

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有