2.2.1 Windows 内核结构

《Windows内核原理与实现》第2章Windows 系统总述,本章将简要地介绍WRK(Windows Research Kernel),这是Microsoft 提供的一套可以编译和运行的Windows 内核源代码,本书后面章节的绝大多数讲解都将参考WRK 中的源代码。本节为大家介绍Windows 内核结构。

2.2.1 Windows 内核结构

正如图2.2 所示,Windows 内核分为三层,与硬件直接打交道的这一层称为硬件抽象层(Hardware Abstraction Layer,简称HAL),这一层的用意是把所有与硬件相关联的代码逻辑隔离到一个专门的模块中,从而使上面的层次尽可能做到独立于硬件平台。HAL之上是内核层,有时候也称为微内核(micro-kernel),这一层包含了基本的操作系统原语和功能,如线程和进程、线程调度、中断和异常的处理、同步对象和各种同步机制。在内核层之上则是执行体(executive)层,这一层的目的是提供一些可供上层应用程序或内核驱动程序直接调用的功能和语义。Windows 内核的执行体包含一个对象管理器,用于一致地管理执行体中的对象。执行体层和内核层位于同一个二进制模块中,即内核基本模块,其名称为ntoskrnl.exe。

内核层和执行体层的分工是,内核层实现操作系统的基本机制,而所有的策略决定则留给执行体。执行体中的对象绝大多数封装了一个或者多个内核对象,并且通过某种方式(比如对象句柄)暴露给应用程序。这种设计体现了机制与策略分离的思想。图2.3 显示了Windows 内核的详细组成结构。

 
(点击查看大图)图2.3 Windows 内核的组成结构

Windows 内核为用户模式代码提供了一组系统服务,供应用程序使用内核中的功能。应用程序通常并不直接调用这些系统服务,而是通过一组系统DLL,最终通过ntdll.dll 切换到内核模式下的执行体API 函数中,以调用内核中的系统服务。Ntdll.dll 是连接用户模式代码和内核模式系统服务的桥梁。对于内核提供的每一个系统服务,该DLL 都提供一个相应的存根函数,这些存根函数的名称以“Nt”作为前缀,例如NtCreateProcess、NtOpenFile和NtSetTimer。另外,ntdll.dll 还提供了许多系统级的支持函数,比如映像加载器函数(以“Ldr”为前缀)、Windows 子系统进程通信函数(以“Csr”为前缀)、调试函数(以“Dbg”为前缀)、系统事件函数(以“Etw”为前缀),以及一般的运行支持函数(以“Rtl”为前缀)和字符串支持函数等。

执行体API 函数接收的参数来自于各种应用程序,因此,为了确保系统的健壮性,以及抵抗来自用户模式的恶意攻击,所有的执行体API 函数必须保证参数的有效性。这意味着它们必须在恰当的时刻检查参数的值,若是指针的话,还必须保证调用者可以访问指针所指的内存。通常,执行体系统服务函数会在其开始处,对所接收的参数逐一探查它们的可访问性。例如,以下代码就演示了这一做法:

 
 
  1. PreviousMode = KeGetPreviousMode();  
  2. if (PreviousMode != KernelMode) {  
  3. try {  
  4. ProbeForWrite(InputInformation,  
  5. InputInformationLength,  
  6. sizeof(ULONG));  
  7. if (ARGUMENT_PRESENT(ReturnLength)) {  
  8. ProbeForWriteUlong(ReturnLength);  
  9. }  
  10. } except(EXCEPTION_EXECUTE_HANDLER) {  
  11. return GetExceptionCode();  
  12. }  

这里InputInformation 和ReturnLength 是该系统服务的直接参数,InputInformation 是个指针参数,它的可用长度由另一个参数InputInformationLength 来指定,因此,这段代码通过ProbeForWrite 来探查这一输入数据是否可写;另外,ReturnLength 是一个ULONG类型的输出参数,这段代码通过ProbeForWriteUlong 来验证该参数是否可写。一旦探查函数发生访问违例,则说明这两个参数有问题,于是该系统服务就将直接返回违例异常代码。

正如前文所述,用户模式和内核模式代码所能访问的地址空间有所不同。在32 位系统上,用户模式代码只能访问2 GB 以下的虚拟内存地址空间,而内核模式代码可以访问当前进程整个4 GB 虚拟地址范围。2 GB 以下称为进程地址空间,2 GB 以上称为系统地址空间。实际上,在两者之间有一块特殊的64 KB 地址空间,位于0x7fff0000~0x7fffffff,在两种模式下都不可访问。上述代码片段中的ProbeForWrite<Xxx>函数首先检查目标内存地址是否越过了此特殊区域,若越过则访问违例;然后,试图将目标地址处的值赋回该地址,即触发一次该地址处的读和写操作,若该内存地址处当前线程不可写,则引发异常,从而使代码片段中的except 子句截获控制。Windows 通过这种方法来捕捉到“用户模式代码传递一个系统空间地址”或者“传递一个无效内存地址”的情形,从而确保执行体函数接收到的参数已被检验过。

然而,执行体API 函数并不总是要检验参数的有效性,如果在调用该API 函数以前的模式是内核模式,那么,它不需要检验参数的有效性。执行体不会用坏的参数来调用它自己的服务,只有当执行体API 函数接收到一个或多个来自用户模式的参数时,它才使用Probe 函数族检查参数的有效性。每个线程都维护着一个状态值,用以说明它以前的处理器模式。当从用户模式切换到内核模式时,该值将被设置为UserMode,从而满足上述代码中的if 条件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值