Windows系统架构

系统架构

 

 

 

关键组件
环境子系统和子系统DLL
  环境子系统向应用程序提供环境和应用程序编程接口(Appplication Programming Interface, API)。Windows 2000/XP支持三种环境子系统:Win32、POSIX和OS/2,其中最重要的环境子系统是Win32子系统,其他子系统都要通过Win32子系统接收用户的输入和显示输出。环境子系统的作用是将基本的执行体系统服务的某些子集提供给应用程序。 用户应用程序调用系统服务时必须通过一个或多个子系统动态链接库作为中介才可以完成。

  子系统由会话管理器(Session Manager)(Smss.exe)进程启动。

Ntdll.dll
  Ntdll.dll是一个特殊的系统支持库,主要用于子系统DLL。包含两种类型的函数:

系统服务分发存根(stub),在用户模式下可通过这些函数调用windows执行体的系统服务。
内部支持函数,供子系统、子系统DLL及其他的原生镜像文件使用。
执行体
  是Ntoskrnl.exe中的上层,包含了基本的操作系统服务,比如内存管理、进程和线程管理、安全性、I/O、网络和跨进程通信等。

内核
  由Ntoskrnl.exe的一组函数以及对于硬件体系架构的低层支持(如中断和异常分发)构成。这些函数提供了一些最基本的机制,如线程调度和同步服务供执行体组件使用,但是所有的策略决定都留给了执行体,除了线程调度和分发。

硬件抽象层(HAL)
  Windows内部组件以及用户编写的驱动程序通过hal.dll访问硬件。
  Ntoskrnl与hal相互链接。

设备驱动程序
硬件设备驱动程序
文件系统驱动程序
文件系统过滤驱动程序
网络重定向器和服务器
协议驱动程序
内核流式驱动程序

  安装驱动程序是在系统中添加用户编写的内核模式代码的唯一方法。

系统进程
Idle进程(空闲进程,无实际的用户模式映像文件)
System进程
会话管理器(Smss.exe,第一个用户模式进程)
本地会话管理器(Lsm.exe)
Windows子系统(Csrss.exe)
会话0初始化(Wininit.exe)
登录进程(Winlogon.exe)
服务控制管理器(Services.exe)和它创建的子服务进程(如Svchost.exe)
本地安全认证服务器(Lsass.exe)

深入解析Windows操作系统笔记——CH1概念和术语

1.概念和工具

本章主要介绍Windows操作系统的关键概念和术语

1.概念和工具

1.1操作系统版本

1.2基础概念和术语

1.2.1Windows API

1.2.2 服务、函数和例程

1.2.3 进程、线程和作业

1.2.3.1 进程

1.2.3.2 线程

1.2.3.3 虚拟地址描述符

1.2.3.4 作业

1.2.4 虚拟内存

1.2.5 内核模式和用户模式

1.2.6 终端服务及多个会话

1.2.7 对象和句柄

1.2.8 安全性

1.2.9 注册表

1.2.10 UNICODE

1.3 挖掘Windows内部机理

参考

 

1.1操作系统版本

Windows操作系统版本已经很多了

 

1.2      基础概念和术语

1.2.1    Windows API

Windows API(应用程序接口),是针对Windows操作系统的系统编程接口。

Windows API有几个分类:

n  基本服务

n  组件服务

n  用户界面服务

n  图形和多媒体服务

n  消息和协议

n  Web服务

本书重点介绍基本服务(比如,进程和线程,内存管理,I/O,安全性)。

关于.Net和WinFX

.NET FrameWork由框架类库(FCL)和一个提供托管代码运行环境的(CLR)组成。

CLR提供即时编译,类型检查,垃圾回收和代码访问安全性等。

托管代码:在最初编译时,将源代码编译成中间代码(IL),然后在运行时,使用运行库编译器在受控的环境下,将中间代码编译成机器码。

在微软体系下,认为不是托管代码就是非托管代码。

CLR是一个典型的COM服务器,建立在Windows API之上。

 

而WinFX就是新的Windows API(为vista特别设计的),也提供了托管代码的功能,但是缺容易混淆,后来改为.Net FrameWork 3。(关于WinFX的介绍在第6版中已经没有了)。

.NET FrameWork是对API的一种扩展。

1.2.2 服务、函数和例程

主要介绍一些书中提到的专业术语:

n  Windows API函数:主要是指已经被文档化的可调用的子例程

n  原生的系统服务:指操作系统中未文档化的,可以在用户模式下调用的底层服务,如NtCreateProcess

n  内核支持函数(例程):值操作系统内部且只能被内核调用的子例程

n  Windows服务:由Windows服务管理器启动的进程(在注册表中,认为驱动定于为服务,但是书中并不这样引用)

n  DLL:一组可调用的子例程,合起来被链接成一个二进制文件,应用程序可以动态加载这些二进制文件。

1.2.3 进程、线程和作业

1.2.3.1 进程

程序是指一个静态的指令序列,而进程是程序的实例化,拥有各种资源。一个进程由以下元素组成:

n  私有的虚拟地址空间

n  程序定义的代码和数据,被映射到进程的虚拟地址空间中

n  一个已打开的句柄列表,这些句柄指向各种资源

n  称为访问令牌的安全环境,标示了改进程关联的用户,安全组和特权

n  进程ID,可以唯一识别一个进程

n  至少一个线程

每个进程都指向一个父进程或者创建者进程,但是如果父进程被关闭,进程就会指向一个不存在的父进程。

1.2.3.2 线程

线程是在进程中的实体,也是Windows执行此进程的调度实体,没有线程进程是不可能运行的。

线程的基本部件:

n  一组代表处理器状态的CPU寄存器中的内容

n  2个栈,一个用于线程在内核模式下执行,一个用于线程在用户模式下执行

n  线程局部存储区(TLS),线程私有存储区域,各个子系统,运行库,DLL都会用到这个区域

n  唯一表示线程的线程ID

n  线程自己的安全环境

易失的寄存器,栈,私有存储区域合起来称为线程的环境。

虽然线程有自己的环境,但是同一个进程内的线程共享该进程的虚拟地址空间及其他属于该进程的资源。

也就是说线程可以读写进程内其他线程的内存,但是不能跨进程的访问,除非另外一个进程把虚拟地址空间变成共享内存区。

1.2.3.3 虚拟地址描述符

虚拟地址描述符是一些数据结构,内存管理器使用这个数据结构来记录一个进程所使用的虚拟地址。

1.2.3.4 作业

作业是指,一组进程当一个整体来维护管理。

1.2.4 虚拟内存

Windows 实现了平面地址空间的虚拟内存系统,每个进程感觉自己独立拥有一个很大的私有地址空间。虚拟内存提供了内存逻辑视图,并不对应于内存物理布局。运行的时候,内存管理器借助硬件支持,讲虚拟地址翻译成真正的物理地址。

进程之间就隔离了,一个进程不会访问到另外一个进程的东西。

大多数系统拥有的物理内存比虚拟地址小,所以当内存不够的时候,内存管理器会把内存移动到磁盘,释放内存,让被的进程使用。

在32bit下,4GB的地址空间,其中2GB是内核地址空间,2GB是用户模式地址空间,在起用3gb参数,用户模式地址空间时3GB,内核模式地址空间时1GB

还有AWE地址窗口扩展,可以让32bit系统访问64GB的内存。缺点是程序员自己解决映射关系。

 

在64bit下地址空间可以达到8T,在Itanium系统上可以达到7T

 

1.2.5 内核模式和用户模式

为了避免用户程序读写关键操作系统数据,Windows使用了2中处理器访问模式:用户模式,内核模式。用户程序代码运行在用户模式,系统代码运行在内核模式。内核模式允许访问所有系统内存和cpu指令。用内核模式来保护操作系统稳定。

虽然Windows 进程都有自己的地址空间,但是内核模式的操作系统和驱动都是使用同一个虚拟地址空间。

系统空间中的页面只有在内核模式下可以访问,用户空间中的页面在用户模式下都可以访问。

内核模式下运行的代码可以访问所有系统空间中的内存。

因为进入内核模式就缺少保护,所以第三方设备驱动程序加载时要小心。

当用户模式调用系统服务的时候,会切换到内核模式下,当要将cpu控制权返回给用户的时候要先切换到用户模式。

1.2.6 终端服务及多个会话

1.2.7 对象和句柄

内核对象是某一个静态定义的对象类型的单个运行时的实例。对象类型包含了一个系统定义的数据类型,在该对象上可用的函数,及一组对象属性。如所有的进程是进程对象类型的一个实例。

对象和普通数据结构的区别是,对象的内部结构是被隐藏的。必须要调用对象服务才能读写对象内部数据。

对象技术的实现了操作系统4个系统任务:

n  提供了可供人读的名称

n  进程间共享资源和数据

n  保护资源,避免未授权访问

n  引用跟踪,如果不再使用可以释放掉。

1.2.8 安全性

Windows的核心安全功能包含:针对所有可共享对象的自主保护,安全审计,登录时密码认证,以及一个资源被一个进程释放后,其他进程看不到上一个进程留下的资源。

1.2.9 注册表

注册表是系统数据库,包含了引道和配置系统所有的信息。也反映了内存中易失的数据窗口,比如当前的硬件状态,性能计数器。

1.2.10 UNICODE

1.3 挖掘Windows内部机理

主要介绍了一些调试工具,和Windows SDK

 

 

 深入解析Windows操作系统笔记——CH2系统结构

2.系统结构

本章主要介绍系统的总体结构,关键部件之间的交互,以及运行在什么环境。

2.系统结构

2.1 需求和设计目标

2.2 操作系统模型

2.3 总体结构

2.3.1 可移植性

2.3.2 对称多处理

2.3.3 可伸缩性()

2.3.4 客户和服务器版本的区别

2.3.5 版本检查

2.4 关键的系统组件

2.4.1 环境子系统和子系统dll

2.4.1.1 Windows子系统

2.4.1.2 POSIX子系统

2.4.1.3 OS/2子系统

2.4.2 NTDLL.DLL

2.4.3 执行体

2.4.4 内核

2.4.5 内核对象

2.4.6 硬件支持

2.4.7 硬件抽象层(HAL)

2.4.8 设备驱动程序

2.4.9 系统进程

2.4.9.1 空闲进程

2.4.9.2 中断和DPC

2.4.9.3 system进程和系统进程

2.4.9.4 会话管理器

2.4.9.5 Winlogon,lsass和Userinit

2.4.9.6 服务控制器管理

2.5 总结

 

2.1 需求和设计目标

2.2 操作系统模型

在大多数用户操作系统中,应用程序与操作系统本身是隔离的:操作系统代码在内核模式下执行,可以访问系统数据和硬件,应用程序代码运行在用户模式下,只有有限的接口可以使用,对系统数据访问受限,无法直接访问硬件。

 

和unix一样,windows系统大部分代码和驱动程序都是共享相同的受保护的内核模式空间。意味着操作系统任何组件,都可以破坏其他组件的数据。

 

当然用户程序和操作系统所有组件是隔离的。应用程序无法直接访问系统中特权部分的数据和代码。

 

windows内核模式组件也体现了基本的面向对象设计原则。如他们不会直接进入另一个组件的数据结构来访问该组件维护的数据。相反是利用正式的接口来传递参数,访问和修改相应的数据接口。

 

但是严格上来说windows并不是一个面向对象系统。windows内部使用c语言,并不是面向对象系统,c语言对象实现,只是借用了面向对象语言的特性。

2.3 总体结构

本节介绍windows的设计目标和包装方式,如下图windows总体结构中的关键系统组件。

 

 

 

有4中用户模式进程:

1.固定的系统支持进程,如登陆进程,会话管理器进程。

2.服务进程,宿纳了windows服务,如进程管理器和假脱机服务。

3.用户应用程序,有6个类型:windows32位,windows64位,windows3.1 16位,ms-dos 16位,posix32位或者OS/2 32位。

4.环境子系统服务进程,实现了操作系统环境的部分支持。这里的环境是指操作系统展示给用户或者程序员的个性化部分。

在windows下,用户程序不能直接访问原始的windows服务,要通过一个或者多个子系统动态链接库。

 

windows内核组件包含:

1.windows执行体,包含基本的操作系统服务,如内存管理,进程和线程管理,安全性,I/O,网络,跨进程通信。

2.windows内核,是由一组底层的操作系统功能构成,如线程调度,终端和异常处理分发。以及处理器同步。提供了一组例程和基础对象。执行体的其他部分利用这些例程和对象实现更高层次的功能。

3.设备驱动程序,硬件设备驱动程序,也包含文件系统和网络驱动程序。其中硬件设备驱动程序将用户的I/O函数调用转化为特定的硬件设备请求。

4.硬件抽象层,指一层特殊代码,它把内核,设备驱动程序和windows执行体其他部分跟与平台相关的硬件差异隔离开来。

5.窗口和图形系统:实现了图形用户界面函数。

文件名

组件

Ntoskrnl.exe

执行体和内核

Ntkrnlpa.exe

执行体和内核,支持物理地址扩展,是的系统可寻址64GB物理内存

Hal.dll

硬件抽象层

Win32k.sys

Windows子系统的内核模式部分

Ntdll.dll

内部支持函数,以及执行体函数和系统服务分发存根(stub)

Kernerl32.dll,Advapi32.dll,User32.dll,Gdi32.dll

Windows的核心子系统DLL

2.3.1 可移植性

windows的一个设计目标是要能够运行在各种不同的硬件体系结构上。

windows有2中方式支持可移植性以支持多种硬件体系结构和平台:

1.windows有一个分层设计,系统底层部分与处理器体系结构相关,或与平台相关的,这些部分被隔离到独立的模块中,所以搞成不需要考虑体系结构的区别。有2个组件为系统提供了可移植性:内核和硬件抽象层。

2.windows的绝大多数代码是由c语言编写的,少部分是使用c++编写的,只有那些需要直接与系统硬件通信的部分或者对性能极端敏感的操作系统部分,才是用汇编语言编写的。

2.3.2 对称多处理

多任务是值多个执行线程之间共享同一个处理器的操作技术。

 

能够很好的在多处理器运行是windows 的设计目标。windows是一个对称多处理(SMP)操作系统。没有主处理器,操作系统和用户线程可以被调度到任何一个处理器上运行,而且所有的处理器共享唯一的内存空间。

 

对称处理和非对称处理不同,操作系统选一个处理器运行系统内核代码。而其他处理器运行用户代码。

 

 

 

xp和2003支持2中新的多处理器系统:超线程(hyperthreading),NUMA(非一致性的内存结构)。

超线程是intel一个技术,可以一个物理处理器上有多个逻辑处理器,是的一个逻辑cpu可以在其他逻辑cpu正在忙着的时候继续运行。

 

在非一致性内存结构NUMA系统中,处理器被组织成更小的单元,成为node,每个结点都有自己的处理器和内存,并同一个一个缓存一致(cache-cohernet)的互联总线连接到更大的系统上。NUMA系统上的windows仍然作为一个smp系统运行。所有的处理器可以访问所有内存。不过本地结点比其他节点速度要快,系统想要提高性能做法是,根据现场用到的内存所在的节点,讲现场调度到同一个处理器上。

 

在windows的最初设计上32位最多只支持32个cpu,64位支持64个cpu。并没有本质的因素来限制处理器个数。注册表LocensedProcessors可以限制处理器个数。

 

考虑到性能问题内核和HAL分为2个版本,单处理器和多处理器版本。

在系统磁盘上的文件名

在发布介质上单处理器版本的名称

在发布介质上多处理器版本的名称

Ntoskrnl.exe

Ntoskrnl.exe

Ntkrnlmp.exe

Ntkrnlpa.exe

Ntkrnlpa.exe in \windows\<arch>\Driver.cab

Ntkrpamp.exe in \windows\<arch>\Driver.cab

Hal.dll

取决于系统类型

取决于系统类型

以下只针对2000系统

 

 

Win32.sys

\I386\UNIPROC\Win32k.sys

\I386\Driver.cab中Win32.sys

Ntdll.dll

\I386\UNIPROC\Ntdll.dll

\I386\Ntdll.dll

Kernel32.dll

\I386\UNIPROC\Kernel32.dll

\I386\Kernel32.dll

2.3.3 可伸缩性()

多处理器系统下,管家你的问题是可伸缩性。windows有以下功能这些功能对windows作为一个多处理器起到关键性的作用:

1.能一个处理器上运行系统代码,也可以在多个处理器上运行。

2.在单个进程内执行多个线程,这些线程可以在不同的处理器上运行。

3.内核内部(如自旋锁,排队自旋锁以及压栈锁)以及设备驱动程序和服务器进程内部的细粒度同步,是的多个组件可以并行在多个处理器上运行。

4.如I/O完成端口之类的编程机制,是的可以实现高效的多线程进程,并且这样的程序再多处理系统上有很好的伸缩性。

2.3.4 客户和服务器版本的区别

客户版和服务器版主要区别是有:

支持的处理器个数不同。

支持的物理内存不同。

所支持的并发网络连接数不同。

2.3.5 版本检查

2.4 关键的系统组件

已经看过上面的简易的结构图,了解了高层的结构体系。

之后都会围绕这个图展开,第三章解释windows使用的主要控制机制(如中断,对象管理器)。第五章启动和关闭windows的过程。第四章介绍各个管理机制(注册表,服务进程,WMI)。剩余的章节更加详细的讨论各个关键区域内存结构和操作(进程,线程,内存管理,安全性,I/O,存储管理,高速缓存管理器,windos文件系统和网络)。

 

2.4.1 环境子系统和子系统dll

如上图,最初windows有3个子系统,os/2,posix,wondows。os/2最后一次发布和Windows2000。到了xp posix也不发布了。3个子系统中windows子系统比较特别,是必须启动的。

查看注册表HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems下面有子系统的信息。

 

其中Required值表示了启动要加载的子系统,如上图,值为Debug和Windows。Window值包含了windows子系统的文件规范,csrss.exe它代表了客户/服务器运行时的子系统。Debug为空。Optional值为Os2,Posix表示这2个子系统,被按需启动。Kmode表示windows子系统的内核环境下运行的部分为win32k.sys。

 

环境子系统角色是将windows基础系统服务暴露给应用程序。每个子系统都提供了对于windows原生服务不同部分的访问能力。也就是说建立在某个子系统上的应用程序可以做到的,是另一个建立在不同子系统的应用程序无法做到的。如posix的fork。

 

每个exe可以执行映像被绑定到一个子系统上,如VC++ link命令/SUBSYSTEM可以指定类型代码,可以使用Exetype工具查看此类型代码。

 

用户程序不能直接调用windows服务而是通过dll来调用如windows子系统dll(kernel32.dll,advapi32.dll,user32.dll,Gdi32.dll),POSIX子系统DLL(psxdll.dll)。当一个应用程序调用子系统可能会发生3中情况:

1.函数完全在该子系统dll中实现的,在用户模式下运行。

2.该函数要求调用windows执行一次或多次。

3.改函数要求在环境子系统中完成某个工作。

2.4.1.1 Windows子系统

Windows子系统有一下几个主要组件构成:

1.环境子系统进程(Csrss.exe),包含下列支持:

         a.控制台(文本)窗口

         b.创建或删除进程和线程

         c.对16为虚拟DOS机(VDM)进程的一部分支持。

d.其他一些函数,比如GetTempFile,DefineDosDevice,ExitWindowsEx,以及几个自然语言函数支持。

2.内核模式驱动程序(win32k.sys)包含:

a.窗口管理器,它控制窗口显示管理屏幕输出,采集来自键盘,鼠标,和其他设别的输入,同时也负责将用户的消息传递给应用程序。

         b.图形设备接口,他是专门正对图形输出设备的函数库。

3.子系统dll

子系统dll,如Kernel32.dll,Advapi32.dll,User32.dll,Gdi32.dll,将windows api文档化,对应到Ntoskrnl.exe和Win32k.sys大多数未文档化的系统服务调用。

4.图形设备驱动

         指硬件香瓜你的图形显示器驱动程序,答应及驱动程序和视频微端口驱动程序。

2.4.1.2 POSIX子系统

posix可以看成是一个基于unix的可移植的操作系统接口。值的是正对unix风格的操作系统接口的一组国际标准。posix鼓励厂商实现unix风格,编译在系统之间迁移。

需要使用posix子系统,要求使用platform sdk中使用posix的头文件和库文件。posix是按需启动的当第一次启动posix,psxss.exe要运行起来。posix的映像文件不是直接运行的。一个特殊的称为posix.exe的支持映像文件被启动起来,然后再创建一个子程序来运行posix应用程序。

2.4.1.3 OS/2子系统

和posix一样,有用性很有限,而且OS/2已经不在适用于windows了。

2.4.2 NTDLL.DLL

NTDLL.DLL是一个特殊的系统支持库,主要用于子系统DLL。包含两个类型函数:

1.系统服务分发存根(stubs),他们会调用Windows执行体系服务。

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

 

第一组函数是为windows执行体系服务提供接口,在用户模式下可以通过接口函数调用windows执行体的系统服务,如(NtCreatefile,NtSetEvent)

 

对于每个这样的函数,ntdll包含了一个同名入口,函数内部的代码包含了与处理器体系接口相关的模式切换指令,通过该指令可转换到内核模式下,从而调用系统服务分发器。分发器检查某些参数后,再调用真正的内核模式系统服务,其中包含ntoskrnl.exe内部实现代码。

 

NTDLL.DLL也包含了许多支持函数,比如映像文件加载器(以ldr开头的函数)、对管理器、Windows子系统进程通信函数(Csr开头的函数)、以及一般运行库(Rtl开头的函数)、也包含了异步调用(APC)分发器和异常分发器。

2.4.3 执行体

Windows执行体是Ntoskrnl.exe中的上层,内核是其下层。执行体包含以下几类函数:

1.可在用户模式下调用的导出函数。这些函数成为(系统服务)并通过ntdll导出。还有一些未文档化的如LPC、NtQueryInformationProcess

2.可通过DeviceIoControl函数调用设备驱动器函数。

3.只能在内核模式下导出的函数,并且这儿写函数在Windows DDK或者Windows IFS Kit已经文档化。

4.在内核模式下调用,未在Windows DDK或者IFS Kit中文档化的导出函数。

5.定义为全局符号,但是未被导出的函数。如以Iop或者Mi开头的函数(分别是内部IO管理器支持函数和内部内存管理器支持函数)。

6.未定义为全局符号,而是在一个模块内容的函数。

 

Windows执行体还包含以下组件:

1.配置管理器,复制系统注册表的实现和管理

2.进程和线程管理器,创建或者终止进程和线程。

3.安全应用监视器,强制在本地计算机上实行安全策略,它守护着系统资源执行对运行时对象的保护和审计。

4.I/O管理器:实现了设备无关的I/O,负责将这些操作分发到恰当的设备驱动程序做进一步处理。

5.即插即用(pnp)管理器:为了支持一个特定的设备,确定驱动,并加载这些驱动。

6.电源管理器:负责协调事件,并且向设备驱动程序产生电源管理I/O通知。如电源管理器设备为系统空间,通过将cpu置于睡眠来降低电源电耗。

7.WDM Windows管理规范例程:允许设备驱动发布有关性能和配置信息以及接受来自用户模式的WMI服务命令。

8.高速缓存管理器:提高了以文件为基础的I/O操作的性能,其做法是让最近引用过磁盘数据留在主内存中以便快速访问。(并且延迟了写操作,在将更新数据发送到磁盘前先在内存中停留一小段时间。)

9.内存管理器:实现了虚拟内存

10.逻辑预取器:加速系统和进程启动过程

 

另外Windows执行提还包含了4组主要支持函数:

1.对象管理器,创建,管理,删除Windows执行体对象和抽象数据类型,这些对象和数据类型代表了操作系统资源。

2.LPC设施,在同一台机器上用户进程和服务器进程之间传递消息。

3.公共运行库,字符串处理,算术操作等函数。

4.执行体支持例程:如系统内存分配,互锁的内存访问,以及2中特殊的同步对象:资源和快速互斥体。

2.4.4 内核

内核是由Ntoskrnl.exe中的一组函数以及对硬件体系结构的低层支持构成的。Ntoskrnl.exe中的这组函数提供了一些最基本的机制。内核代码使用C编写,并不容易在C中访问的任务,则保留使用汇编。大部分函数都已经文档化,以Ke开头。

2.4.5 内核对象

内核给高层做支持。内核实现了操作系统最基本的机制(调度,分发)。把各种策略决定留给了执行体。

内核外看来,执行体将线程和其他可共享资源都表示为对象。这些对象要求一些策略开销。这些开销在内核中不存在,内核实现了一组更简单的对象,称为内核对象,帮助内核控制好中心处理过程,并且支持执行体对象的创建工作。执行体层的绝大多数对象包装了一个或者多个内核对象,把他们的内核属性合并起来。

一组内核对象建立了有关控制各种操作系统功能叫做控制对象,另一组内核对象融合了同步的能力,改变或者影响线程调度叫分发器对象。分发器对象包含了内核线程,互斥体,事件,内核事件对,信号量,定时器,等待定时器。

执行体通过内核函数创建内核对象实例,维护对象实例。构建更加复杂的对象提供给用户。

2.4.6 硬件支持

内核的另一个主要功能是将执行体和设备驱动程序从windows所支持的各种硬件体系结构中抽象出来,或者隔离出来变化的差异。

在内核设计是竟可能的使用公共代码最大化。内核支持的可移植性接口,在不同的体系结构上是等同的。而且实现这组接口的大部分代码,在不同的结构体系上也是相同的。但是有些代码和体系结构有关,如上下文切换。

从高层看线程选择和上下文切换可以使用相同的算法(上一个线程的执行上下文被保存起来,新线程的环境被加载进来),但在不同的处理器上,实现还是有差异的。执行上下文是由处理器的寄存器来描述的,所以要保存和加载哪些数据还是有差别的。

2.4.7 硬件抽象层(HAL)

硬件抽象层是系统可移植的关键。HAL是一个可加载的,内模式模块提供了windows当前运行平台的低层接口。它隐藏了与硬件相关的细节,如I/O接口,中断控制器,以及多处理器通信机制等,体系结构或者机器相关的功能。

windows内部组件以及用户编写的设备驱动并不直接访问硬件,当它们需要获得与平台相关的信息时,它们可以通过调用HAL例程来保存可移植性。DDK中能找到很多有关HAL在驱动中的用法。

虽然windows带了几个HAL,但是安装时只能有一个被选中,并且copy到系统磁盘,其文件名为hal.dll。

2.4.8 设备驱动程序

设备驱动程序是可加载的内核模式(以.sys结尾),他们在I/O管理器和相应的硬件之间建立链接。驱动在内核模式下,位于以下3个环境之一:

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

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

3.作为一个中断的结果(因此不存在任何特定的进程或者线程执行环境中)

驱动也是调用HAL,因此驱动程序可以在windows支持的cpu体系结构上代码级移植,在同一个体系结构族内是二进制可移植的。

设备驱动有以下几类:

1.硬件设备驱动程序,通过HAL操作硬件,从而输出到设备或者网络,或者从设备或者网络中输入。硬件设备驱动也有很多类型如,总线驱动,人机界面驱动等。

2.文件系统驱动程序是指可以接受面向文件的I/O请求,并将这些请求转化成针对某一特定设备的I/O请求。

3.文件系统过滤器驱动程序:如截取了I/O请求并且执行某些增值处理之后再传递给下一层驱动(执行磁盘镜像,加密的驱动程序)。

4.网络重定向器和服务器指文件系统I/O请求传递给网络上的某一台机器。或者从网络上接收此类请求的文件系统驱动程序。

5.协议驱动程序,如TCP/IP,NetBEUI,IPX/SPX之类的网络协议

6.内核流式过滤器驱动程序:这样的驱动被串接起来,以便对流数据进行信号处理。

驱动程序是内核模式中添加代码的唯一方式。

2.4.9 系统进程

以下系统进程会出现在每个windows系统中(其中空闲进程,system进程并不是完整的过程,因为它们不是运行在用户模式的可执行文件):

1.空闲进程

2.system进程

3.会话管理器(smss.exe)

4.windows子系统(csrss.exe)

5.登陆进程(winlogon.exe)

6.服务控制管理器(services.exe)和它创建的子服务进程(如系统提供通用服务宿主进程svrhost.exe)

7.本地安全认证服务器(lsass.exe)

2.4.9.1 空闲进程

空闲进程是第一个进程,并没有在用户模式下的实际映像文件。

2.4.9.2 中断和DPC

标记为中断和DPC(分发过程调用)用于中断和延迟过程调用的时间,他们并不是进程,列在这里是因为他们都会消耗cpu,并没有计算在任何一个进程中,而是被算在系统空闲中。

2.4.9.3 system进程和系统进程

system进程是一种特殊线程的母体。这些特殊线程只能运行在内核模式哦。系统线程有普通线程所有属性和环境,但是只运行系统空间中加载的代码。系统线程没有地址空间,因此动态存储空间,都必须从系统中内存堆分配,比如换页或者非换页池。

系统线程是由PsCreateSystemThread来创建的,这个函数只能在内核环境下才能被调用。

内核会创建一个称为平衡集管理器的系统线程,每秒没唤醒一次,可能发出调度和内存管理相关事件。告诉缓存管理器也使用系统线程来实现“预读”和”延迟写”功能。

在排查问题是,知道系统线程映射到某个驱动程序中,甚至映射到包含改代码的子例程中,一定非常有用。

所以如果system进程中的线程正在运行,首先要确定哪些线程在运行。通过线程看哪个驱动开始的或者检查调用栈,得知在运行到哪里了。

2.4.9.4 会话管理器

会话管理器(smss.exe)是系统中第一个创建的用户模式进程,由负责完成执行体和内核初始化工作的内核模式系统线程最后创建实际的smss.exe进程。

在windows启动过程中,会话管理器负责许多比较重要的步骤,如打开页面文件,执行延迟文件改名,删除操作,创建环境变量。将子系统程序(csrss.exe)和winlogon.exe启动起来。winlogon进程依次启动其他系统进程。

smss.exe的主线程执行以上步骤后,一直在csrss.exe和winlogon的进程上等待。如果这2个进程中任何一个非正常终止了,则ssms.exe让系统崩溃,(崩溃代码:status_system_process_terminated或0xc000021a)因为windows依赖这2个进程才能运行。

 

smss等待加载子系统的请求,调式事件,以创建新的终端服务器会话的请求。

终端服务会话是由smss来完成的。当smss接到一个创建会话的请求时,先调用NtSetSystemInformation,请求建立内核模式数据结构。又调用内部的内存管理函数MmSessionCreate该函数建立起会话虚拟地址空间,改地址空间包含会话中的换页池以及由win32子系统的内核模式部分和其他的会话空间设备驱动程序所分配,属于某个会话的数据结构,然后会为该会话创建winlogon和csrss实例。

 

2.4.9.5 Winlogon,lsass和Userinit

winlogon登陆进程处理交互式用户的登陆和注销,当sas被按下(ctrl+atl+del),winlogon接到一个用户登陆请求。

登陆过程的身份识别和认证是在一个名为GINA(图形识别和认证)的可替换DLL中,windows的标准为GINA为Msgina.dll实现了默认的windows登陆界面。然而开发人员可以提供他们自己的GINA DLL来实现其他的身份识别和认证机制如:基于声波的方法。

一旦用户名和口令捕捉到了就可以送到本地安全认证服务器进程(lsass.exe)进行认证。lsass调用适当的认证包,以执行实际的验证操作,比如口令是否符合存储在活动目录或者sam中的口令信息。

在成功完成验证后,lsass调用安全引用监视器中的一个函数(如:NtCreateToken)创建一个访问令牌对象。对象包含当前用户的安全范围。winlogon利用此访问令牌来创建该用户会话中的初始进程默认为userinit.exe。

userinit执行该用户环境的一些初始化工作,然后再查找注册表winlogon下的shell并且创建一个进程来运行系统定义的外壳程序(默认explorer.exe)

然后userinit退出,这就是explorer.exe没有父进程的原因。winlogon在注销,登陆,sas winlogon是活动的。关于登陆过程各个步骤的完整秒死可以看第5章。有关安全认证可以查看第八章。

2.4.9.6 服务控制器管理

windows中的服务可以自一个服务器进程,也可以是一个驱动程序。这一指的是用户模式进程(如:unix的守护进程),这些进程可以在系统引导是自动启动起来,而无需交互式的登陆过程。也可以被配置为手动启动。

服务控制器是一个特殊的系统进程,用于启动,停止服务进程也复制服务进程之间的交互。服务有3个民粹:运行中进程名,注册表内名称以及管理器的显示名。

在服务进程和所运行的服务之间并不是一一对应的,因为有些服务和其他共享一个进程。注册表服务类型代码指明了共享还是独占进程。

许多windows组件使用系统服务实现如Spooler,event log,Task Scheduler和多个网络组件。

2.5 总结

本章主要概括的介绍了一遍windows体系结构,检查了一遍关键的组件,他们之间是如何联系起来的。

深入解析Windows操作系统笔记——CH3系统机制

3.系统机制

微软提供了一些基本组件让内核模式的组件使用:

1.陷阱分发,包括终端,延迟的过程调用(DPC),异步过程调用(APC),异常分发以及系统服务分发

2.执行体对象管理器

3.同步,包括自旋锁,内核分发器对象,以及等待是如何实现的。

4.系统辅助线程

5.其他的机制,比如Windows全局标记

6.本地过程调用

7.内核事件跟踪

8.Wow64

3.系统机制

3.1陷阱分发

3.1.1 中断分发

3.1.1.1 硬件中断

3.1.1.2 软中断请求级别(IRQL)

3.1.1.3 软中断

3.1.2 异常分发

3.1.3 系统服务分发

3.1.3.1 32位系统服务分发

3.1.3.2 64位系统服务分发

3.1.3.3 内核模式的系统服务分发

3.1.3.4 服务描述符表

3.2 对象管理器

3.2.1 执行体对象

3.2.2 对象结构

3.2.2.1 对象头和对象体

3.2.2.2 对象类型

3.2.2.3 对象方法

3.2.2.4 对象句柄和进程句柄表

3.2.2.5 对象安全性

3.2.2.6 对象保持力

3.2.2.7 资源记账

3.2.2.8 对象名称

3.2.2.9 会话名称空间

3.3 同步

3.3.1 高IRQL的同步

3.3.1.1 互锁操作

3.3.1.2 自旋锁

3.3.1.3 排队自旋锁

3.3.1.4 栈内排队自旋锁

3.3.1.5 执行体的互锁操作

3.3.2 低IRQL的同步

3.3.2.1 内核分发对象

3.3.2.2 快速互斥体和受限互斥体

3.3.2.3 执行体资源

3.3.2.4 压栈锁

3.4 系统辅助线程

3.5 windows全局标志

3.6 本地过程调用(LPC)

3.7 内核事件跟踪

3.8 Wow64

3.8.1 Wow64进程地址空间布局结构

3.8.2 系统调用

3.8.3 异常分发

3.8.4 用户回调

3.8.5 文件系统重定向

3.8.6 注册表重定向和反射

3.8.7 I/O请求

3.9总结

 

3.1陷阱分发

中断和异常是导致处理器转向正常控制流之外代码的两种系统条件。陷阱(trap)是指当异常或者中断发生时,处理器捕捉到一个执行线程,并将控制权转移到操作系统中某处固定地址处的机制。

在Windows中处理器将控制权转给一个陷阱处理器 (trap handle)。所谓陷阱处理器是指与某个特殊的中断或者一场相关的一个函数

 

 

 

内核对待中断和异常是有区别的,中断是异步事件,并且与当前正在运行的任务毫无关系。中断主要由I/O设备,处理器时钟,定时器产生。中断可以允许和禁止。异常是一个同步过程,它是一个特殊指令执行的结果。

异常可以在同样数据在一个程序里重现。异常的例子:内存访问违例,特定的调试器指令,以及除0错误。内核把系统服务调用异常(从技术上讲,他们是系统陷阱(trap))。

当一个硬件异常或者中断产生的时候,处理器在被中断的线程的内核栈中记录机器状态信息,当它可以回到控制流中该点处继续执行。如果该线程在用户模式下执行,那么windows就切换到该线程的内核模式栈。然后windows在被中断的线程的内核栈上创建一个陷阱帧(trap frame),并把线程的执行状态保存在陷阱帧里。在内核调试器中输入dtnt!_ktrap_frame就可以看到陷阱定义。

 

多数情况下内核安装了前端陷阱处理函数,在内核将控制权交给与改陷阱香瓜的其他函数之后或者之前,由这些前段陷阱来执行一些常规的陷阱任务。

如陷阱条件是一个设备中断,则内核硬件中断陷阱处理器将控制权转交给一个由设备驱动程序提供给改中断设备的中断服务例程(ISR)。

若陷阱条件是因为调用了一个系统服务引发,那么通用的系统服务陷阱处理器将控制前交给执行体中指定的系统服务。内核不会为不处理的陷阱安装陷阱处理器。陷阱处理器一般使用KeBugCheckEx,当内核检测到可能导致数据被破坏的行为时,改函数会停止计算机。

3.1.1 中断分发

硬件产生的中断往往是有I/O设置激发的。当设备需要服务就会以中断的方式通知处理器。中断驱动的设置可以一步的进行I/O处理。

系统可以产生软中断,如内核可能触发一个软中断,触发线程分发过程,同时也以异步的方式打断一个线程的执行。

内核安装了中断陷阱处理器来响应设备中断,中断陷阱处理器将控制权递给一个负责该中断的外部例程(ISR)或者传递给一个响应中断的内部内核例程。

下面介绍硬件如何向处理器通知中断,内核支持中断类型,设备驱动如何与内核交互,以及内核如何识别软中断。

3.1.1.1 硬件中断

在windows锁支持的平台上,外部I/O中断进入中断控制器的一个引脚,该控制器在cpu的引脚上中断cpu。中断控制器将IRQ(中断请求)翻译成中断号,利用该中断号作为中断分发表的索引。并将控制权传递给恰当的中断分发例程。

在引导时,windows填充IDT(中断分发表),其中包含了指向内核中负责处理每个中断和异常的指针。

windows将硬件IRQ映射到IDT上,同时它利用IDT来为异常配置陷阱处理器。虽然windows支持最多256个IDT项,但是支持的IRQ数据量由中断控制机设计决定。

 

3.1.1.2 软中断请求级别(IRQL)

虽然中断控制器已经实现一层中断优先级,但是windows仍然强迫使用它自己的中断优先级方案,称为中断请求级别(IRQL)。

X86,X64,IA64中断请求级别:

 

 

 

中断是按优先级别来处理的高优先会抢占低优先级中断的执行权,当一个高优先级中断发送,处理器会把中断线程上下文保存起来,并调用与中断相关的陷阱分发器,陷阱分发器提升IRQL,并调用中断服务例程,调用完成后降低IRQL,回到中断发送前,被中断线程运行。但是当有其他,低优先级中断时,当IRQL降低,低优先级中断出现。这样,内核会恢复到上述过程来处理中断。

 

线程优先是线程的属性,IRQL是中断源的属性。每个处理器的IRQL设置可以随系统代码的执行变化。

每个处理器的IRQL设置决定了该处理器可以接收哪些中断。当一个内核模式线程运行时,可以通过KeRaiseIrql和KeLowerIrql来提升和降低处理器IRQL或通过调用内核同步对象的函数间接提高或者降低IRQL。当处理器的IRQL高于中断源则被屏蔽,否则被中断打断。

访问。

因为访问PIC(中断控制器)比较慢所以引入了优化技术延迟IRQL以避免访问PIC。当IRQL被提升,HAL记下新的IRQL而不是去修改中断屏蔽值。当一个较低中断发生则HAL将中断屏蔽值设置为对于第一个中断正常的值。这样当IRQL被提升的时候没有更低优先级中断,则HAL需要修改PIC。

一个内核模式线程根据请求,来降低和升高处理器IRQL。当中断发生时,陷阱处理器(或处理器本身)将改处理器的IRQL提升到中断源的IRQL.这样会把等于或者低于它的所有中断都屏蔽。保证了不被低级中断截掉。被屏蔽的中断由其他处理器处理或者被保存下来知道IRQL下降。

因此系统组件包括内核和设备驱动,都试图让IRQL保持在被动级别。这样可以提高设备启动可以更加及时的响应硬件中断。

每个中断级别都有特定的目的,如内核发出一个处理器间的中断,以请求另外一个处理器执行一个动作。

将中断映射到IRQL:IRQL级别和中断控制器定义的中断请求并不相同,在hal中决定一个中断分配给那个IRQL。然后调用HAL函数HalGetSystemInterruptVector把中断映射到对应的IRQL。

预定义的IRQL:以下介绍一下预定义的IRQL

1.只有当内核在KeBugCheckEx中停止了系统并屏蔽所有中断的时候,内核才会使用高级别的IRQL。

2.电源失败,出现在NT文档中,但是从来没有使用过。

3.处理器间的中断,用于向另外一个处理器请求执行一个动作。

4.时钟,主要用于系统时钟,内核利用该黄总段级别来跟踪具体时刻,以及现场测量或者分配cpu时间。

5.性能剖析(Profile),当内核的性能剖析功能被打开的时候,内核性能剖析陷阱处理器会记录下中断发生时被执行的代码的地址。(性能剖析器Kernrate)

6.设备IRQL,用来对设别中断优先级分区

7.DPC/Dispath级别和APC级别是由内核和设备驱动程序产生的软中断。

8.被动级别,最低的IRQL优先级别,它不是一个中断级别,它是普通线程运行时设置的,允许所有中断发生。

对于DPC/Dispatch级别或者更高级别上的代码,一个重要的限制是它不能等待一个对象。另外一个限制DPC/Dispatch或者更高级别的IRQL只能访问非换出页内存。若2个限制都违反了系统会崩溃,代码为IRQL_NOT_LESS_OR_EQUAL。在驱动上违反限制是一种常见的错误。在驱动上违反限制上一种创建的错误。

中断对象,内核提供了一种可移植的机制使得设备驱动程序可以为它们的设备注册ISR。这是一个称为中断对象的内核控制对象。

中断对象包含了所有“供内核将一个设别的ISR与一个特定级别的中断关联起来的所有信息”,包含该ISR的地址,该设备中断时所在的IRQL级别,以及内核中该ISR关联的IDT项。

驻留在中断对象中的代码调用了实际的中断分发器,通用是内核的KiInterruptDispatch或者KiChainedDispatch例程,并将指向中断对象的指针传递给它。

下图显示了中断控制流:

 

 

 

将ISR与特定中断级别关联起来的称为连接一个中断对象,将ISR与IDT项断开关联称为断开一个中断对象。这些操作通过内核函数IoconnectInterrupt和IoDisconnectInterrupt完成。

3.1.1.3 软中断

虽然大多数中断都是硬件产生,但是windows内核也为各种各样的任务产生软中断。包括:

1.激发线程分发

2.非时间紧急中断处理

3.处理器定时到期

4.特定线程的环境中异步执行一个过程

5.支持异步I/O操作

分发或者延迟过程调用(DPC)中断,当一个线程不能继续执行的时候,比如因为线程已经终止了或者主动进入等待状态,内核就会直接调用分发器,从而立即导致一个环境切换。但是有时检测到线程已深入到许多层代码中,这时应该进行重新调度,这种情况下内核请求分发,但是将它推迟到完成了当前的行为之后再进行。

当啮合对共享的内核数据访问,会把IRQL拉到DPC/Dispatch级别当内核检查到需要分发的时候,请求一个DPC/Dispatch中断。所以只有当内核完成了当前的活动,把IRQL拉低,分发中断才能处理。

延迟事务也在这个IRQL上运行,DPC是完成一项系统任务,但是不是那么紧迫,这些函数被称为延迟的,是因为不会了立即执行。

DPC赋予操作系统一种能力,产生一个中断并且在内核模式下执行系统函数。内核利用dpc来处理定时到期,以及一个线程的时限到期以后重新调度处理器。为了给硬件中断提供及时的服务,windows视图把IRQL保持在低于设备IRQL之下。为了达到这个目的,让设备驱动程序ISR执行最少必要的工作来响应他们的设备,将异变的中断状态保存起来,并将数据传输非时间紧迫的中断处理推迟到 DPC/Dispatch IRQL级别上的DPC中在执行。

DPC是通过DPC对象来表示的,DPC对象是内核控制对象,对于用户模式不可见,对于设备驱动和内核代码是可见的。DPC对象包含最重要的信息是DPC中断将要调用哪个系统函数地址。正在等待的DPC被存放在队列中,每个处理器都有一个队列称为DPC队列。要想请求一个DPC,系统会初始化DPC对象,然后放入DPC队列中。

默认情况下内核把DPC对象放在发生该DPC请求的处理器的DPC队列末尾。在设备驱动程序只需指定一个DPC优先级别和指定特定CPU,就可以改变这种默认方式。指定在某个CPU上叫定向DPC。如果一个DPC的优先级为低级或者中级则放入队列尾,否则放入队列头部。

当处理器的IRQL从DPC/Dispatch或更高降到某个更低的级别时,内核处理DPC。在处理DPC是IRQL在DPC/Dispatch级别上,并且将DPC来出来运行直到队列为空。当队列为空内核才让IRQL降低到DPC/Dispatch以下。让正常的线程执行过程继续执行。

 

 

 

DPC优先级可以以另一种方式影响到系统行为。内核通常通过一个DPC/Dispatch级别的中断来激发队列“抽干”的动作。只有当一个DPC被定为在ISR所在的处理器上,且改DPC的优先级是高级或者中级时,内核才产生一个中断,若为低级只有当DPC请求到一个阀值或一段时间后,内核才会请求中断。

若DPC被定为在一个不同于其ISR运行的CPU上,并DPC为高级。内核立即用一个信号通知CPU,以便”抽干”它的DPC队列。若优先级为中级或者低级则DPC数据超过阀值,内核才会激发一个DPC/Dispatch中断。

DPC中断产生的规则:

DPC优先级别

DPC被定为在ISR的处理器上

DPC被定为在另一个处理器上

低级

DPC队列长度超过最大的DPC队列长度值,或者DPC请求率小于最小的DPC请求率。

DPC队列长度超过最大的DPC队列长度或者系统空闲

中级

总是激发

DPC队列长度超过最大的DPC队列长度或者系统空闲

高级

总是激发

总是激发

DPC主要为设备驱动提供的,但是内核也使用DPC,内核使用DPC来处理限时到期事件。在系统时钟每个”嘀嗒”点上,就发生一个时钟IRQL级别的中断。时钟中断处理器对系统时间进行更新,将一个记录了当前线程运行多长时间的计数器递减。当计数器减到0,线程到期,内核可能需要重新调度该处理器,这个任务在DPC/Dispatch IRQL上完成。

时钟中断处理器将一个DPC插入到队列中以便激发分发过程。然后结束他的工作并且降低IRQL,因为DPC中断级别较低,所以在时钟中断完成前出现尚未处理的设备中断,都在DPC中断之前被处理。

APC异步调用,异步过程调用提供了一种在特定用户线程环境中执行用户程序和系统代码的途径。APC经过排队以便在特定线程的环境中执行。

APC是由一个内核控制对象(APC对象)来描述的,正在等待执行的APC驻留在一个由内核管理的APC队列中。APC队列是特定线程相关的,即每个线程有它自己的APC队列,当内核请求要将APC排队时,它将一个APC排队,她将APC插入到将来执行此APC例程的那个线程的队列中。当内核请求APC级别中断,当该线程最终开始执行的时候,会执行此APC。

有2种APC:内核和用户模式。内核模式的APC并不要求从目标获取许可就可以运行在改线程的环境中,而用户模式必须先获取许可。内核模式的APC有普通和特别2种,将IRQL提升到APC级别或调用KeEnterGuardRegion,就可以静止这两种类型的内核模式APC。

执行体使用内核模式的APC来完成那些必须要在特定线程的地址空间(执行环境)中才能完成的操作系统任务。它可以利用特殊的内核模式APC来指示某个线程停止执行一个可中断的系统服务。

用户模式APC(ReadFileEX,WriteFileEx和QueueUserApc),如ReadfileEx,WritefileEx允许调用者指定一个完成例程,当I/O完成是例程就会被调用。I/O完成机制是通过I/O的线程插入一个APC来实现的。内核APC运行在APC级别上,用户模式APC运行在被动级别上。

APC交付会导致等跌队列重新排序,如APC用来把等待资源的线程挂起,那么该线程就会进入等待访问这个资源队列的末尾。

3.1.2 异常分发

中断可以在任何时候发生,异常则是直接由当前正在运行的程序产生。windows引入了一种称为结构化异常处理的设施,应用程序可以在异常发生时获得控制,然后应用程序可以修正条件,并返回到异常发生处,将栈展开(使引发异常的子例程执行过程中止),或想系统报告,改异常不可识别,因为系统应该继续搜索一个有可能处理此异常的异常处理器。

x86上所有异常都在预定义的中断号,这些中断号对应IDT项。每个项指向了某个特定异常的陷阱处理器。

所有异常,除了简单的通过陷阱处理器,可以解决的之外,其他都由异常分发器的内核模块服务。异常分发器就是找到一个异常处理器,处理要处理的异常。

异常处理对用户来说都是透明的,有些异常也允许原封不动的回到用户模式。如内存访问违例,算法溢出,操作系统不对他们处理。环境子系统可以建立起基于帧的异常处理器来处理异常。

基于帧是将一个异常处理与一个特定的过程激活动作关联起来。当一个过程被调用,代表该过程的帧被压到栈中。一个栈帧可以关联多个异常处理器,每个异常处理器保护源程序中一块特定代码。当发生一个异常时,内核查找与当前帧关联在一起的某个异常处理器。如果没有找到内核继续查找与上一个栈帧关联在一起的某个处理。如果最终还是没有找到异常处理器,内核会调用自己默认的异常处理器。

异常发生,CPU硬件将控制权递交给内核陷阱处理器,内核陷阱处理器创建一个陷阱帧。正由于陷阱帧处理完异常后,系统可以从停止的地方恢复。

如果在内核模式下的异常,异常分发器调用一个例程来找到一个基于帧的异常处理器。由它来处理异常。

在用户模式下,windows子系统有一个调试器端口和异常端口,通过它们来接收windows进程中用户模式异常的通知。内核在它默认的异常处理器中用了这些端口。

 

 

 

调试器端口是最常见的异常来源,因此异常分发器采取动作,

1.查看引发该异常的进程是否有一个相关的调试器进程。若存在异常分发器发送一个调试器对象信息到调试对象相关的进程。

2.若该进程没有附载的调试器进程或调试器并没有处理该异常,那么异常分发器切换到用户模式下。将陷阱帧按照Context数据结构的格式拷贝到用户栈中,并调用一个例程来找到一个基于帧的异常处理器。

3.如果没有找到或者虽然找到了但是它不处理该异常,则异常分发器切换到内核模式下,并且再次调用调试器,以便让用户做更多的调试。

4.若调试器不在运行,并没有找到基于帧的处理器,那么内核向与该线程的进程关联在一起的异常端口发送一个信息。该异常端口如果存在的话,则一定是由控制该线程的环境子系统注册的。环境子系统监听该端口,在恰当的时机把一个异常转化为一个与环境相关的信号或异常。客户/服务器运行时子系统(CSRSS)简单的弹出一个消息框来通知用户发生了错误,并且终止进程。

5.当POSIX从内核收到一个消息,指定的一个线程产生了异常,当内核在处理异常过程走得比较深了,而子系统并没有处理该异常,那么内核执行一个默认的异常处理器,它只是简单的将引发该异常的线程所在的进程终止掉。

未处理的异常

所有windows线程都有一个异常处理器来处理未被处理的异常。该异常处理器是在windows内部的进程启动函数或线程启动函数中声明。如:

 

 

 

如果一个线程的异常没有被处理,则windows的未处理异常过滤器将会被调用。这个函数目的是,当一个异常未被处理时,可以提供一种系统统一的行为和方法。

3.1.3 系统服务分发

内核陷阱处理器分发中断,异常和系统服务调用

3.1.3.1 32位系统服务分发

在x86 Pentium II处理器以上,使用windows使用sysenter执行触发一个陷阱,这个是intel特别为快速系统服务定义的。为了支持这一指令,windows在引导时刻把内核的服务分发器的地址保存与该指令相关的寄存器中。

执行该指令会导致变化到内核模式下,并且执行系统服务分发器,为了返回到用户模式,系统服务分发器通常执行sysexit执行(当处理器单步标记被打开,系统分发器改而使用iretd指令。)

在K6和更高的32位AMD处理器上,windows使用syscall类似于sysenter,系统嗲用号也在EAX上,而调用者参数则保存在栈中。在完成了分发之后,内核执行sysret指令。

3.1.3.2 64位系统服务分发

在64位体系结构上,windows使用syscall指令进行系统分发(和AMD处理器上syscall类似),系统调用号存在EAX寄存器,前4个参数存放在寄存器汇总其他参数存放在栈中。

在IA64上使用EPC指令,前8个系统调用参数通过寄存器来传递,其他参数通过栈传递。

3.1.3.3 内核模式的系统服务分发

内核利用传递进来的参数找到系统分发表中的服务信息(类似IDT表)。

 

 

 

系统服务分发器KiSystemService将调用的参数从用户模式栈中复制到内核模式,然后执行服务。如果换地给一个系统服务的参数指向了用户空间中的缓冲区,那么在内核模式代码复制到缓冲区或从缓冲区读前先要查明缓冲区是否可以访问。

每个线程都有一个指针指向它的系统服务表,windows有2个系统服务表,最多可以支持4个。系统服务分发器确定哪个表包含了所有请求的服务,它将32位系统服务号中的其中2个位解释成一个索引表。系统服务号低12位被用在该表索引所指定的表中进行的索引。

 

3.1.3.4 服务描述符表

一个主要的默认数组表(KeDescriptorTable)定义了Ntosrknl.exe中实现的核心执行体系统服务。另一个默认数组表(KeserviceDeseriptorTableShadow)包含了在windows子系统的内核模式部分win32.sys中实现的windows user和GDI服务。

当windows线程第一次调用一个windows user和GDI服务,该线程的系统服务表的地址被指向一个包含windows user和GDI服务的表格。KeAddSystemSericeTable可以让win32.sys加入到系统服务表中。

针对windows执行体服务的系统服务分发指令位于NTdll.dll中。子系统调用Ntdll.dll来实现。windows user和GDI函数,在这些函数中,系统分发指令是直接在user32.dll和GDI.dll中实现,没有涉及ntdll.dll。

 

3.2 对象管理器

windows实现了一个对象模型,以便为执行体的实现各种内部服务提供了一致的,安全的访问路径。执行体内部复制创建,删除,保护和跟踪对象的组件(windows对象管理器)。

对象管理器把原本可能散落在整个系统各处的资源控制操作集中在一起。对象管理器的设计意图是为了实现本章稍后列出的一些功能。

1.提供一种公共,同一个的机制来使用系统资源

2.将对象保护隔离到操作系统统一的区域中,从而可以做到c2安全等级

3.提供一种来记录进程使用对象数量的机制,从而可以对系统资源的使用上加限制。

4.简历一套对象命名方案,可以很方便的融合现有对象。

5.支持各种操作系统环境的需要

6.简历统一的规则来维护对象的保持力。

windows内部有两种类型的对象:执行体对象和内核对象。

执行体对象是指执行体的各种组件所实现的对象(如,进程管理器,内存管理器,I/O子系统)内核对象是指windows内核实现的一组更为基本的对象。这些对象在执行体内部被创建和使用。

 

3.2.1 执行体对象

每个windows环境子系统总是把操作系统的不同面貌呈现给它的应用程序。执行体对象和对象服务是环境子系统用于构建其自己版本的对象和其他资源基础。

执行体对象往往由在用户应用程序中一般的环境子系统或由操作系统的组件作为他们常规操作的一部分而创建。如为了创建一个文件,windows应用程序调用windows的createfile函数,该函数在windows子系统DLL kernel32.dll中现实中实现的,在经过了一些验证和初始化工作以后,createfile会调用原生的windows服务ntcreatefile来创建一个执行体文件对象。

windows子系统使用执行体对象来导出它自己对象集合,其中许多对象直接对应于执行体对象。

暴露给windows api的执行体对象:

对象类型

所代表的含义

符号链接(Symbolic link)

间接的引用一个对象名字的机制

进程(Process)

虚拟地址空间,以及为了执行一组线程对象而必需的控制信息

线程(Thread)

进程内部的一个可执行实体

作业(Job)

指一组进程,通过作业机制,可以像单个实体那样来管理他们

内存区(section)

共享内存的一个区域(在windows中也称为文件映射对象)

文件(File)

一个已打开的文件或者I/O设备的实例

访问令牌(Access token)

一个进程或者线程的安全轮廓(安全ID,用户权限等)

事件(Event)

一个具有持久状态(有信号,或者无信号的)的对象,可被用于同步或者通知。

信号量(Semaphore)

信号量是一个计数器,它提供了资源门控能力,对该信号量所保护的资源只允许某个最大数目的线程来访问它。

互斥体(Mutex)

用于顺序访问一个资源的一种同步机制

定时器(Timer)

这是一种当固定长时间过去时,通知一个线程的机制

IO完成(IoCompletion)

使线程能够将”I/O操作完成通知”进出队列的一种方法,在windows中称为IO完成端口。

键(Key)

这是一种引用注册表中数据的机制。

窗口站(WindowStation)

该对象包含了一个剪贴板,一组全局原子和一组桌面对象

桌面(Desktop)

这是一个被包含在窗口站内部的对象。桌面对象有一个逻辑显示器表面,其中包含了窗口,菜单和钩子。

3.2.2 对象结构

每个对象都有一个对象头和对象体,对象管理器控制了对象头,而执行体组件则控制了由它们创建的对象类型的对象体。每个对象头指向一个进程列表,类表中每个进程都打开了此对象。对象类型指向了称为类型对象的特殊对象。

 

 

 

3.2.2.1 对象头和对象体

对象管理器使用对象头中保存的数据来管理这些对象,而无须关系它们的类型。

标准对象头的属性:

属性

用途

对象名称

使一个对象对于其他的进程也是可见的,以便于共享

对象目录

提供了一个层次结构来存储对象名称

安全描述符

决定谁可以使用该对象,以及允许它们如何使用它(注:对于没有名称的对象来说,安全描述符是空[null])

配额花费

列出了当一个进程打开一个指向该对象的句柄时,针对该进程收取的资源花费额

已打开句柄的计数

记录了“打开一个句柄来指向该对象”的次数

已打开句柄的列表

指向一个进程列表,其中每个进程都打开了指向该对象的句柄。

对象类型

指向一个类型对象,该对象包含了正对这个类型的对象都是公共属性

引用计数

记录了一个内核模式组件引用该对象地址的次数

除了对象头以外,每个对象也有一个对象体,并且其格式和内容只有这种对象类型才有,同一类型的所有对象共享同样的对象体格式。一个执行体组件通过创建一个对象类型,并且为它提供一些服务,就可以控制和维护所有这个类型的对象体中的数据。

对象管理器提供了少量通过服务,通过这些服务可以对一个对象头中保存的属性进行操作,通过服务可以用再任何类型的对象上。

虽然通用的对象服务都支持所有对象类型,但是每个对象都有它自己的创建,打开和查询服务。

3.2.2.2 对象类型

对象头中包含的数据对于所有对象都是公共的,但是每个对象实例可以取不同的值。

为了节省内存,对象管理器只在创建一个新的对象类型时才会存储静态的,特定于对象类型的属性。

进程对象和进程类型对象:

 

 

 

类型对象的属性

属性

用途

类型名称

此种类型的对象名称(“process”,”event”,”port”)

池类型

指明了这种类型的对象是从换页的还是非换页的内存中分配

默认的配额花费

默认从进程配额中扣除的换页内存池值和非换页内存池值

访问类型

当一个线程打开某个指向该类型的对象时可以请求的访问类型(“读”,”写”,”终止”,”挂起”)

通用访问权限的映射关系

在4种通用的访问权限和属于该类型的访问权限之间的映射关系。

同步

指明了一个线程是否可以等待这种的对象

方法

在一个对象的生命周期的特定点上,对象管理器自动调用的一个或者多个例程。

一个对象能否支持同步,取决于该对象是否包含了一个内嵌的分发器对象,在“低IRQL的同步”中有介绍。

3.2.2.3 对象方法

上面的表中,最后一个属性就是方法。方法是由一组内部例程构成的,这些例程类似构造和解析函数(在创建和销毁时被使用)。

当一个执行体组件创建了一个新的对象类型时,它可以像对象管理器注册一个或多个方法,对象管理器在此种类型的对象生命周期中,某些明确定义的点上调用这些方法。

对象方法:

方法

何时调用

Open

当一个对象句柄被打开

Close

当一个对象句柄被关闭

Delete

在对象管理器中删除一个对象之前

Query name

当一个线程在一个从属名字空间中查询一个对象的名称时

Parse

当对象管理器在一个从属名字空间中搜索一个对象名称时

Security

当一个进程读写(如文件)在其从属名字空间中的保护属性时。

3.2.2.3.1 Open函数

对象管理器创建一个指向对象的句柄时会调用open方法,在对象被创建或者打开时候运行。只有一个对象类型(windowstation)定义了open方法。这样win32.sys能够与服务于桌面相关内存池的进程共享内存。

3.2.2.3.2 Close函数

close方法的例子是IO中,对象管理器关闭一个句柄使用close方法。close方法先检查看正在关闭该文件句柄进程是否有任务用于该文件并且未完成的锁,如果有则除去锁。

3.2.2.3.3 Delete函数

对象管理器在内存中删除临时对象以前,调用delete方法。内存管理器为内存区对象类型注册了delete方法,它会释放该内存区使用的物理页面。并在删除内存区对象前验证一下内存管理器为该内存区所分配的任何内部数据结构已被删除了。

3.2.2.3.4 Parse函数(类似于Query name)

若发现对象存在于对象管理器名字空间外,允许对象管理器把查找一个对象的控制权交给一个从属的对象管理器。若在搜索路径上碰到一个关联了parse的对象,会暂停搜索。对象管理器调用parse方法。将正在搜索的对象名称的剩余部分传给parse方法。除了对象管理器方法外在windows中还有注册表名字空间和文件系统名字空间。例如打开一个名为\Device\Floppy0\docs\resume.doc的文件句柄,对象管理器遍历它的名称树,直到到达Floppy0。调用parse,把\docs\resume.doc传入。I/O管理器的parse例程接受名称,并且传给文件系统,文件系统找到文件并打开。

3.2.2.3.5 Security方法

Security也是I/O系统使用方法,类似parse。一旦一个线程视图查询或改变那些用于保护一个文件的安全信息时,该方法就会被调用。安全信息是存储在文件对象中,而不是内存,因此必须调用I/O系统才能找到安全信息,并将它们读出来或进行修改。

3.2.2.4 对象句柄和进程句柄表

当进程根据名称来创建或者打开一个对象时,它会接受到一个句柄,通过句柄来访问一个对象,要比使用名称访问快得多。因为对象管理器可以跳过名称查找过程,直接找到目标对象。进程也可以在其创建时刻通过继承句柄的方式获得句柄或从另一个进程接收一个复制的句柄。

所有的用户模式进程在其线程使用一个对象以前,必须先拥有一个指向该对象的句柄。句柄被用做指向系统资源的间接指针,这样可以让应用程序不与系统数据结构直接交互。

对象句柄还是提供了额外的一些好处:第一,不同句柄没有什么区别可以使用统一的接口来引用。第二,对象管理器有独立的权利来创建句柄,查找句柄。也就是对象管理器可以仔细地审查每个可能会影响对象的用户模式动作。

对象句柄是索引与进程相关的句柄表中的项相关。执行体进程(EPROCESS)块中一个域指向句柄表。句柄表实现方式是3层和虚拟地址到物理地址映射类似。

当进程被创建对象管理器分配了句柄表的最高层结构,其中包含了指向中间层表的指针;同时也创建了中间层,其中包含了第一个指向子句柄表的指针数组,还分配了最底层,其中包含了第一个子句柄表。

 

 

 

把低24位看成3个8位,分别索引到3层结构中的一层。在xp,2003在进程创建时,最底层句柄表被分配,其他的都会被按需分配。在windows 2000中一个子句柄表是255个可用表项。在xp,2003表项=(页大小/表项大小)-1。在windows xp,2003上句柄表项:

 

 

 

P:说明了调用者是否允许关闭句柄。I:该进程创建的子进程是否在它们句柄表中有一份该句柄的拷贝。A:关闭该对象时是否应该产生一个升级信息(对象管理器内部使用该标记)。

系统组件和设备驱动程序通常需要打开一些不应该让用户访问的对象。可以通过内核句柄表来表示。内核句柄表只有在内核模式下可以飞昂文,可以在任何进程环境下。

对象管理器看到一个句柄的高位被设置时,就会将它识别为内核句柄表中的句柄。也就是说内核句柄表中的句柄的引用值大于0x80000000。在windwos 2000中内核句柄表是一张独立的句柄表,但是在xp和2003中,内核句柄表也被用做system进程的句柄表。

3.2.2.5 对象安全性

当一个进程打开一个句柄,对象管理器调用安全引用监视器,监视该对象描述符是否允许该进程所请求的访问类型,若允许,引用监视器返回一组准许的访问权限,同时对象管理器放入它创建的对象句柄中。

当下次进程要使用句柄时可以快速的检查这一组句柄中的准许访问权限。

3.2.2.6 对象保持力

对象保持力,分为暂时和永久的。暂时是当需要的时候使用,不需要的时候释放。永久是一直保持知道被显式释放。

对象管理器通过两个阶段来实现对象保持力。第一阶段称为名称保持力,第二个阶段,不再有用时,停止保留对象本身(也就是删除)。

名称保持力:当一个进程打开一个对象的句柄。会在该对象头信息中的已打开句柄计数器+1。当用完关闭句柄,对象管理器已打开句柄-1,。当计数器为0,对象管理器从全局名字空间中删除该对象名称。

不再有用时删除:对象专门提供一个引用计数来记录。

已打开句柄计数器:当进程打开一个对象句柄+1,关闭句柄-1

引用计数:提供一个对象指针+1,用完了-1

当已打开计数器为0,引用计数大于0.表示对象还在使用。当引用计数为0,对象管理器会从内存中将它删除。

 

进程A,进程B和内核结构引用了一个对象,因此handlecount=2,referencecount=3。

3.2.2.7 资源记账

windows对象管理器提供了一个中心设施来实现资源记账。每个对象头都包含了配额花费。

windows每个进程都指向一个配额的数据结构,配额为0表示不限制。

3.2.2.8 对象名称

对象名称可以满足1.区分对象之间的方法。2.找到并获得特定对象的方法。3.允许程序间共享。

只有2种情况会使用名称进行查找,1.创建一个命名对象时,会通过名称查找,验证全局名称空间中不存在。2.当打开一个句柄,句柄指向一个命名对象时,对象管理器查找该名称并返回一个对象句柄。

对象的名称存储位置取决于对象类型

目录

所存储对象名称的类型

\GLOBAL??

Ms-dos设备名(\DosDevices是指向此目录的符号链接)

\BaseNameObjects

互斥体,时间,信号量,可等待的定时器和内存区对象

\Callback

回调对象

\Device

设备对象

\Driver

驱动程序对象

\FileSystem

文件系统驱动程序对象和文件系统识别器对象

\KnowDlls

已知DLL(在启动时候由系统映射的DLL)的内存区名称和路径

\Nls

已映射的国家语言支持表的内存区名称

\ObjectTyoes

对象类型名称

\RPC Control

远程过程调用(RPC)所使用的端口对象

\Security

与安全子系统相关的对象的名称

\Windows

Windows子系统的端口和窗口站

对象的名称相对于一台计算机而言是全局的,但是他们的跨越网络是不可见的。但是对象管理器解析名称的方法使得有可能访问其他机器上的命名对象。

对象目录是对象管理器支持这种层次型命名结构手段。对象目录解析对象名称为指向对象的指针。对象管理器利用指针来构建对应的句柄,将这些对象句柄返回给用户模式的调用者。

符号链接,在某些文件系统中,通过符号链接,用户可以创建一个文件名或一个目录名,当被使用的时候,实际上被操作系统转译成另外一个不同的文件或文件名。使用符号链接,是一种让用户间接的共享一个文件或目录的内容。

符号链接对象,完成的功能类似于对象名称的功能一样。当对象名称中有符号链接,对象管理器遍历它的对象名称空间,找到该符号链接对象,并找到一个取代该符号链接名的字符串。

3.2.2.9 会话名称空间

一个登陆到控制台会话上,用户可以访问全局名称空间,另外会话可以获得该名称空间的私有名称空间实例。\DosDevices,\Windows,\BaseNamedObjects属于会话局部的名称空间,都会被放在私有名称空间中。将名称空间中相同部分复制,来初始化名称空间。

对象管理器在\session\X下创建私有版本,对象管理器以透明的方法,将对象的名称从\BaseNameObjects重定向到\session\2\BaseNamedObjects。

windows子系统DLL将windows应用程序传过来的位于\DosDevice中对象引用加上\??前缀(c:\windows变成\??\c:\windows)。依赖于EPROCESS中DeviceMap。DeviceMap结构中DosDevicesDirectory域所指的对象目录管理器代表了进程的局部DosDevices。

在windows 2003和xp上,系统没有将全局对象拷贝到局部DosDevices目录中,当看到\??会通过DeviceMap中DosDevicesDirectory找到该进程局部\DosDevices目录。若在局部中没有,并且DeviceMap有效,则会在GlobalDosDevicesDirectory查找对象。

当会话中的应用程序要与其他会话的实例同步在任何对象名称前加入\Global\ApplicationInitialized被重定向到\BasedNameObjects\ApplicationInitized而不是\Sessions\2\BaseNamedObjects\ApplicationInitialized。

在2003和xp中应用程序只要在\DosDevices中没有这样的对象就会访问全局,不需要使用\Global。

3.3 同步

当一个资源不允许共享访问,或共享访问导致不可预测的后果则需要互斥。若一段代码访问了一个不可共享的资源,则这样的代码区成为临界区。

3.3.1 高IRQL的同步

在内核执行的各个阶段,内核必须保证,在临界区内部同一时刻只有一个处理器在执行。

在中断发生,内核可能在更新一个全局数据结构,而中断的处理例程可能也要修改此数据结构。在单处理器,可以用以下方式来避免。如,当线程修改全局数据结构时,禁止所有中断。windows的作法是执行临界区时,把IRQL拉高会用到该数据结构的最高IRQL。

3.3.1.1 互锁操作

这个最简单的同步方式,依赖于支持多处理器硬件,操作一个整型值来进行比较。

3.3.1.2 自旋锁

自旋锁是内核用来实现多处理器互斥的机制

 

在windows中,所有内核模式自旋锁都有一个与之关联的IRQL。当运行自旋锁就会拉高IRQL。

3.3.1.3 排队自旋锁

工作方式:当一个处理器要获得已被其他处理器持有的队列自旋锁锁时,把自己的标示符放入一个与该自旋锁关联的一个队列中。

当自旋锁被释放,将锁交给队列中第一个标示符对于的cpu。

在同时处理器等待一个较忙的自旋锁不是检查自旋锁本身而是每个处理器的标志。在队列中位于它之前的处理器会对它设置,表明轮到这个等待的处理器了。

排队的自旋锁的自然结果是,他们在每个处理器标识符上旋转,而不是全局自旋锁上旋转有2个效果:

1.多处理器总线不会因为处理器之间的同步招致繁重的流量。

2.排队加强了先进先出的顺序,处理器之间性能更加一致。

在windows中定义了很多全局队列自旋锁,并且在每个处理器的“处理器控制区域(PCR)”包含了一组数组(保存了指向这些全局队列的自旋锁指针)。当调用KeAcquireQueueSpinLock的时候将一个PCR的索引传进去,可以获得对应的全局自旋锁。

3.3.1.4 栈内排队自旋锁

除了使用全局定义的静态排队自旋锁,xp和2003还提供了KeAcquireInstackQueuedSpinlock和KeReleaseInstackQueuedSpinlock。来支持动态分配的排队自旋锁。

3.3.1.5 执行体的互锁操作

内核提供了很多简单的建立在自旋锁基础上的同步函数。

3.3.2 低IRQL的同步

自旋锁使用有严格的限制:

1.对于受保护的资源,必须快速访问,不要与其他代码有复杂的交互关系

2.临界区代码的内存页不能换出去,不能引用可被换页的数据,不能调用外部过程,不能中断或异常。

在自旋锁不适合是可用:内核分发器对象,快速互斥体和受限互斥体,压栈锁,执行体资源。

 

是否暴露给设备驱动程式使用

禁止常规的内核模式APC

禁止特殊的内核模式APC

支持递归获取操作

支持共享的和独占的获取操作

内核分发器互斥体

内核分发器信号量

快速互斥体

受限互斥体

压栈锁

执行体资源

3.3.2.1 内核分发对象

内核以内核对象的形式,向执行体提供了额外的同步机制,这些内核对象合起来统称为分发器对象。每个支持同步的用户可见对象都封装了至少一个内核分发器对象。

 

3.3.2.1.1 等待分发器对象

一个用户模式的线程等待一个事件对象的句柄。

内核将该线程的调度状态从就绪状态改变成等待状态,然后将该线程加入到正在等待该事件的线程列表中。

另外一个线程设置了该事件,内核沿着该事件的等待线程队列向前搜索。若有一个线程等待条件满足将线程状态从等待改为就绪。若是一个可变优先级的线程,则内核可能也要提升它的执行优先级。

因为一个新线程已经变成就绪执行状态,所以进行重新调度。如果它找到一个正在运行的线程。其优先级低于就绪线程的优先级。那么会抢占此低优先级的线程,并且发出一个软中断,以便激发一个环境切换,切换到高优先级的线程中。

如果没有处理器可以抢占的话,则分发器将该就绪线程放到分发器就绪队列中,以后再被调度。

3.3.2.1.2 如果Signals一个对象

3.3.2.1.3 数据结构

typedef struct _DISPATCHER_HEADER {

UCHAR Type;

UCHAR Absolute;

UCHAR Size;

UCHAR Inserted;

LONG SignalState;

LIST_ENTRY WaitListHead;

} DISPATCHER_HEADER;

typedef struct _KWAIT_BLOCK {

LIST_ENTRY WaitListEntry;

struct _KTHREAD *RESTRICTED_POINTER Thread;

PVOID Object;

struct _KWAIT_BLOCK *RESTRICTED_POINTER NextWaitBlock;

USHORT WaitKey;

USHORT WaitType;

} KWAIT_BLOCK, *PKWAIT_BLOCK, *RESTRICTED_POINTER PRKWAIT_BLOCK;

每个分发器对象都有一个等待列表,列表中一项代表一个等待该对象的线程。所以当线程向一个分发器对象发出信号,内核可以很快的确定谁在等待对象。

等待快结构WaitListEntry指向被等待对象,Thread指向等待线程,NextWaitBlock指向下一个等待块。

 

 

 

3.3.2.2 快速互斥体和受限互斥体

快速互斥体也成为执行体互斥体,比互斥体对象提供了更好的性能。尽管他们也是建立在分发器事件对象基础上的,但是如果对于快速互斥体没有竞争的话,他们无须等待事件对象。

受限互斥体,本质上它与快速互斥体是相同的。使用了KGATE同步对象,通过调用KeEnterGuardedRegion来进制所有内核模式APC的事务,主要用户内存管理器。

3.3.2.3 执行体资源

执行体资源是一种支持共享和独占访问的同步机制,要求APC事务禁止,如果一个线程正在等待获得一个共享访问权,则它应该等待一个与该资源相关联的信号量,如果一个线程正在等待获得一个资源的独占访问权,则应该等待一个事件。

当一个独占持有者通过给信号量发型号来释放一个资源唤醒共享访问者。

当一个线程在等待独占访问一个资源,而该资源正在被其他线程拥有,该线程等待一个同步事件对象。

3.3.2.4 压栈锁

压栈锁是建立在KGATE同步对象基础之上,相比快速互斥体好处是可以按照共享的方式独占的模式来获得。

有两种类型压栈锁:普通压栈锁和能感知缓存的压栈锁。

普通压栈锁:当一个线程想要获得一个普通的压栈锁,若尚未被使用则压栈锁代码标记为已被占用。若已被占有(共享,独占),线程在自己栈上分配一个等待块,将初始化等待块中的事件对象,等待块加入到与压栈锁相关联的等待列表中。向该等待着的等待块中的事件发出信号。

能感知缓存的压栈锁简历在基本压栈锁之上。为每个处理器分配一个压栈锁,然后将这些压栈锁和处理器关联起来。当一个线程希望以共享方式获得压栈锁时,简单的获得对应于当前处理器的那个压栈锁以独占方式获得独占锁时,以独占模式获得每个处理器的压栈锁。

压栈锁的使用范围包括对象管理器和内存管理器,对象管理器中,可以保护全局对象管理器结构和对象安全描述符,在内存管理器,他们可以保护awe数据结构。

3.4 系统辅助线程

windows在system进程中创建了几个线程,这些线程称为系统辅助线程它们代表其他线程来完成一些工作。如DPC级别的IRQL不能运行更低IRQL级别才能执行的函数,必须将这样的处理过程传递给一个低于DPC级别的IRQL的执行线程上。

设备驱动程序或执行体组件通过ExQueueWorkItem和IoQueueWorkItem把工作放到一个队列分发器对象上,系统复制线程在该对象上寻找工作。工作包含一个例程以及一个参数,当辅助线程处理该工作,会把参数传递给例程。

系统辅助线程有以下三类:

延迟型辅助线程:优先级12上,处理器非紧急工作项目,当它在等待工作项目时允许栈页面被换出到页面文件中。

紧急型辅助线程:优先级13,处理一些紧急工作项目,始终在内存上

超紧急型辅助线程:优先级15,总在内存中。

执行体函数ExpWorkerThreadBalanceManager确定是否创建新的紧急型辅助线程,新的线程被称为动态的辅助线程。创建时必须满足下列条件:

1.在紧急工作队列下有工作项目

2.不活动的紧急型辅助线程的数目必须少于系统处理器个数

3.动态辅助线程数据少于16个。

3.5 windows全局标志

3.6 本地过程调用(LPC)

LPC用于进程间通信,LPC为了以下3中方式通信而设计:

1.短于256字节的信息可以通过LPC发送。从发送进程拷贝到系统地址空间中,再从系统地址空间中拷贝到接收进程地址空间中。

2.若多于256字节则复制到共享内存区。

3.若超过了内存共享区,可以直接在地址空间中读取或写入。

LPC有多种端口:服务器连接端口,服务器通信端口,客户通信端口,未命名的通信端口

 

 

 

3.7 内核事件跟踪

一个公共的基础设施,向内核和ETW提供痕迹数据应用程序要使用到ETW,要属于以下3类:

1.控制器,启动或停止,也管理缓冲区

2.提供者,为它所能产生的事件类定义GUID,并注册到ETW以上,并接受控制器命令,启动,停止它所负责的事件类跟踪。

3.消费者,选择一个或多个会话,读取数据。

控制器启动内核记录器(ETW库)向WMI发送一个IO请求说明要开始跟踪哪些事件类。当WMI接受到已启动跟踪源接收到数据就会写入buffer,每隔一秒触发一次写入日志文件。

3.8 Wow64

64位windows上win32仿真,也就是可以在64位上执行32位,x86应用程序。以DLL形式来实现。

wow64.dll实现了文件系统重定向,以及注册表重定向和反射

wow64cpu.dll实现了cpu从32到64,从64到32之间的切换

wow64win.dll截取了win32k.sys导出GUI系统调用

 

 

 

3.8.1 Wow64进程地址空间布局结构

wow64进程可以是2G,也可以是4G虚拟空间。若没有设置大地址空间感知标志则最多保留2G,若开启大地之空间感知标志最多保留4G。

3.8.2 系统调用

Wow64.dll钩住从32位代码变值原生64为系统代码路径,也钩住64位原生系统需要调用至32位用户模式代码的所有代码路径。

启动应用程序,64位ntdll.dll映射到地址空间,初始化判断映像头为32位x86则加载wow64.dll映射32位ntdll.dll,建立ntdll内部启动环境切换到32位,执行32位加载器。

64位和32位间通过wow64在之间转换。

3.8.3 异常分发

通过wow64来转化异常分发。

3.8.4 用户回调

通过wow64来转化

3.8.5 文件系统重定向

为了降低应用程序移植代价,所有相关API将windows\system32替换为windows\syswow64。使用文件系统重定向来实现。

线程使用wow64enable,wow64FsRedirection函数进制文件系统重定向。

3.8.6 注册表重定向和反射

注册表也使用重定向在注册表上创建原生和wow642中,视图为了允许32和64位com组件互操作,在注册表中某些特定部分被更新,wow64也会将这些更新映射到迎来一个视图。

3.8.7 I/O请求

IO除了读写硬盘还可以用于程序通信,驱动程序通IoIs32bitProcess来检测是否从一个wow64进程发出的。

3.9总结

主要介绍执行体建立起来的基本系统机制。

posted @ 2019-09-04 15:50  CharyGao  阅读(6680)  评论(0编辑  收藏  举报