Windows内核架构与驱动开发的基本概念


***【在线视频教程】***

好文章,来自【福优学苑@音视频+流媒体】

Widnows操作系统概述

Windows家族

Win9X系列

Windows 95, 

Windows 98, 

Windows Me



WinNT系列

Windows NT 3.1, 

Windows NT 3.5

Windows NT 4.0

Windows 2000

Windows XP

Windows Server 2003

Windows Vista

Windows 7

Windows 10



Windows特性

可移植性

兼容性

健壮性

可扩展性

异步结构




用户模式与内核模式


image.png



操作系统与应用程序

image.png






Windows操作系统的分层结构

Windows操作系统总体架构

image.png



应用程序与Win32子系统


image.png

image.png




Native API

    大部分的Win32API都是通过NativeAPI实现的,NativeAPI函数一般都是Win32API函数前面加上Nt两个字符,例如CreateFile函数对应着NtCreateFile函数,这些Nt函数都是在“ntdll.dll”实现的,而多数Win32API都是在“kernel.dll”导出的,也有少部分GDI或窗口相关的函数是在“gdi32.dll”和“user32.dll”导出的。


    NativeAPI从用户模式穿越进入到内核模式调用系统服务,这个穿越过程是通过软中断的方式进入的。这个软中断的实现方法在不同版本的Windows实现方式略有不同,在Win2K下是通过“int 2eh”实现的,在WinXP是通过“sysenter”指令完成的。


    软中断会将NativeAPI的参数和系统服务号的参数一起传进内核模式,不同的Native API会对应不同的系统服务号,这个过程是由SSDT辅助完成的。

    系统服务函数一般和NativeAPI具有相同的名字,例如都是NtCreateFile,但它们的实现不同,系统服务调用是在“ntoskrnl.exe”导出的。


NTDLL.DLL

系统支持库,用于子系统DLL,包含以下两种类型的函数:

(1)系统服务分发存根,会调用WINDOWS执行体系统服务

(2)内部支持函数,供子系统、子系统DLL及其他的原生映像文件使用


第一组函数为执行体系统服务提供了接口,在用户模式下可以通过这些接口函数调用执行体的系统服务。对于每个这样的函数,NTDLL包含了一个同名的入口点。函数内部的代码包含了与处理器体系结构相关的模式切换指令,通过该指令可以转换到内核模式下,从而调用系统服务分发器,分发器检验了参数后,调用真正的内核模式系统服务,其中包括NTOSKRNL。


系统服务

image.png


执行体组件

执行体是NTOSKRNL.EXE的上层(内核是下层),包括以下函数

(1)可在用户模式下调用的导出函数。通过NTDLL被导出

(2)通过DEVICEIOCONTROL函数来调用的设备驱动器函数

(3)只能在内核模式下调用的导出函数,且已经文档化

(4)在内核模式下调用但未文档化

(5)定义为全局符号但是未被导出的函数,包括在NTOSKRNL内调用的内部支持函数

(6)未定义为全局符号,而是在一个模块内部的函数


执行体包括以下主要组件

(1)配置管理器。负责系统注册表的实现和管理

(2)进程和线程管理器,创建或终止进程和线程

(3)安全引用监视器,强制在本地计算机上实行安全策略

(4)I/O管理器,实现了与设备无关的I/O操作

(5)即插即用管理器

(6)电源管理器

(7)WDM 驱动管理,允许设备驱动程序发布有关性能和配置的信息

(8)调整缓存管理器,提高了以文件基础的I/O操作的性能

(9)内存管理器,实现了虚拟内存

(10)逻辑预取器,加速系统和进程的启动过程


另外执行体还包含四组主要的支持函数

(1)对象管理器

(2)LPC设施,在同一台机器上的客户进程和服务器进程之间传递消息

(3)公共运行库函数,比如:字符串处理、算术操作、数据类型转换以及安全结构处理

(4)执行体支持例程,比如系统内存分配、同步对象等


内核

内核被认为是Windows操作系统的心脏。

Windows的内核从执行体组件分割出来。和执行体组件相比,内核是非常小的。

内核为执行体组件提供最基本的支持,它负责提供进程和线程的调度,通过自旋锁(Spin Lock)提供对多CPU同步支持,提供中断处理等。


内核提供了以下功能:

》对内核对象的支持。

》对线程的调度。

》对多处理器同步的支持。

》中断处理函数的支持。

》对错误陷阱的支持。

》对其他硬件特殊功能的支持。


Windows内核执行在最高的特权之上,它被设计成可以并行地运行在多处理器商。内核在调度线程的时候不能被其他线程所打断,即不能允许线程的切换。但是内核可以被更高的中断请求级别(IRQL)所打断。


内核是由NTOSKRNL.EXE中的一组函数以及对于硬件体系结构的低层支持(比如中断和异常分发)构成,主要以C编写,对于特殊指令和寄存器,使用汇编

[内核对象]

提供一组定义明确的、可预知的操作系统低层原语和机制,从而使得执行体中的高层组件可以做它们需要做的事情。


实现

从内核外部来看,执行体将线程和其他可共享的资源表示为对象,这些对象要求一些策略开销,这些开销在内核中不存在,内核实现一组简单的对象,执行体层的大部分对象包装了一个或多个内核对象。


[硬件支持]

内核的另一个主要任务是将执行体和设备驱动程序从各种硬件体系结构中抽象出来,企图使公共代码最大化支持内核的这一功能,支持一组可移植的接口。




驱动程序

I/O管理器接收应用程序的请求后,创建相应的IRP,并传送至驱动程序进行处理,有如下几种处理的方法。


(1)根据IRP的请求,直接操作具体硬件,然后完成此IRP,并返回。

(2)将此IRP的请求,转发到更底层的驱动中去,并等待底层驱动的返回。

(3)接受到IRP请求,不是急于完成。而是分配新的IRP发到其他驱动程序中,并等待返回。


驱动程序处理IRP的过程往往不是单独操作,而是将以上几种操作结合在一起。

[设备驱动程序]

可加载的内核模式模块,通过以.SYS结尾,在I/O管理器与相应的硬件之间建立起链接,设备驱动程序位于以下三种执行环境之一:

1、在发起I/O功能的用户线程环境中

2、在内核模式系统线程的环境中

3、作为一个中断的结果

通过调用HAL来维护硬件是设备驱动程序的工作,分为以下类型

1、硬件设备驱动程序

2、文件系统驱动程序

3、文件系统过滤器驱动程序

4、网络重定向器和服务器

5、协议驱动程序

6、内核流式过滤器驱动程序

从WDM的角度有以下几种:

1、总线型驱动程序

2、功能型驱动程序

3、过滤型驱动程序



硬件抽象层

[硬件抽象层HAL]

设备驱动程序调用HAL例程来保持可移植性



Windows与微内核

什么是微内核?

微内核结构由一个非常简单的硬件抽象层和一组比较关键的原语或系统调用组成,这些原语仅仅包括了建立一个系统必需的几个部分,如 线程管理,地址空间和进程间通信等。

image.png

image.png

微核的目标是将系统服务的实现和系统的基本操作规则分离开来。例如,进程的输入/输出锁定服务可以由运行在微核之外的一个服务组件来提供。这些非常模块化的用户态服务用于完成操作系统中比较高级的操作,这样的设计使内核中最核心的部分的设计更简单。一个服务组件的失效并不会导致整个系统的崩溃,内核需要做的,仅仅是重新启动这个组件,而不必影响其它的部分。


从应用程序到驱动程序


打开Windows的设备管理器,可以发现这里罗列着计算机里安装的所有设备,这些设备有的是真实的物理设备。

例如:网卡设备、显卡设备等。有些设备则是虚拟设备。

例如,自己编写的驱动,它没有对应着PC的任何设备,而完全是虚拟出来的“假”设备。虚拟光驱也是这样的虚拟设备。还有些设备介于真实物理设备和虚拟设备之间,比如磁盘的卷设备,磁盘对应磁盘设备,磁盘上的分区又会产生卷设备,这个完全是逻辑上的概念,对卷设备的所有操作,全部会转化成磁盘设备的操作。

image.png


PC上的设备千差万别,所实现的功能完全不同,如何用统一的接口操作不同的设备,是一个很麻烦的问题。Windows的设计者们为了简化对不同设备的操作,实现对不同设备统一接口,将所有设备以普通文件看待。也就是说在Windows中,无论何种设备,都用操作文件的办法去操作设备。


对所有设备的操作统一成和文件操作一样的操作,这一方法非常巧妙。文件操作和设备操作有很多类似的地方。例如,二者都有打开、关闭、读、写、取消等操作。下表列举了文件操作和设备操作所使用的的Win32 API函数。


image.png


下面更深入的介绍一下,Win32 API是如何一步步对设备驱动程序进行读写操作的。

下图是对Windows架构简图的简化,可以很清晰地看到应用程序到驱动程序是怎样操作设备的。

image.png


这里以CreateFile API为例,其他操作设备的API类似。首先应用程序调用CreateFile API,这个API是由Win32子系统的三大模块中的Kernel32.dll实现的。CreateFile函数会调用Ntdll.dll中的NtCreateFile函数,其中NtCreateFile是未文档化的函数,程序员最好不要直接使用这个未文档化的函数。


NtCreateFile的作用是穿越用户模式的边界,进入到内核模式,这个步骤是通过软中断实现的。进入到内核模式后,会调用系统的服务函数,这里会调用同名的系统服务NtCreateFile。(这里很容易搞混,虽然都叫NtCreateFile,但一个是位于用户模式的Native API,另一个是位于内核模式的系统服务调用)


NtCreateFile系统函数调用通过I/O管理器,创建IRP并传输到设备的驱动程序中。IRP(I/O Request Package)即输入输出请求包,是驱动程序开发中重要的数据结构。驱动程序的运行,完全是靠IRP驱动的。下面会有对IRP的专门介绍,这里可以将IRP理解为一个消息。这个消息告诉驱动程序,是需要读操作还是写操作。


驱动程序根据IRP,进行相应的操作。这些操作一般是对设备的直接操作,例如对端口的读操作。对端口的读操作根据不同的硬件平台,实现方法会有所不同,Windows根据不同的硬件平台,会有不同的硬件抽象层(HAL)。硬件抽象层提供一组宏,如READ_PORT_BUFFER_UCHAR。例如,对32位X86系列CPU中的Windows,READ_PORT_BUFFER_UCHAR被解释为汇编代码IN操作。


回想这个复杂的过程,经过了多个层次的交互,只是为了执行一个读写端口的操作。对于有过DOS变成经验的程序员来说,在DOS中操作硬件完全可以不使用驱动,直接使用IN汇编代码就可以实现。事实的确如此,但Windows这样做完全是为了安全的考虑。所有直接操作硬件的指令,如读写物理内存、读写端口都认为是危险的操作,必须经过驱动才能完成。


试想一下,如果应用程序能任意执行IN和OUT汇编指令,那么就可轻易地对磁盘进行控制,这会给操作系统带来安全隐患。又例如,如果能直接读写物理内存,黑客会很容易地对当前进程以外的其他进程进行内存读写,那么盗取账号密码将会变得非常简单。


因此,在应用程序中无法执行IN汇编指令,而必须通过驱动程序来执行。在一层层地调用中,每层又会严格地检查,保证参数的合法性。



好文章,来自【福优学苑@音视频+流媒体】
***【在线视频教程】***