selph
selph
发布于 2021-11-26 / 771 阅读
0
0

Windows 内核编程 ch1--Windows 内部概览

本系列是本人对于帕维尔·约西福维奇的这本《Windows内核编程》进行学习的学习笔记,主要记录整理本人对于Windows内核编程学习到的新知识以及对已有知识的整理,内容可能不完全跟书上一致,会有些许自己添加的内容进行补充。

1.1 进程

进程是个对象,用于容纳和管理运行实例,使用唯一标识符 pid 来标识

进程包含:可执行程序、私有虚拟空间、主令牌、私有句柄表、线程

1.2 虚拟内存

进程里的内存地址都是相对地址,相对于进程的地址,通过页机制映射,将虚拟地址映射到物理内存中或页面文件里

如果要访问的内存在物理内存中,就直接访问,如果在文件里,就会抛出缺页异常,然后从文件读取到物理内存中,然后返回

页面大小取决于 CPU,x86/x64 Windows 系统一个小页 4KB,一个大页 2MB(大页在物理内存里是连续的,且始终是非分页类型,只能设置读/写保护)

Windows 通过地址转换缓冲区 TLB 来加快地址转换速度


虚拟内存中的每个页都有三种状态:

  • 空闲,页面未分配,任何访问都会引起访问违例(access violation)异常
  • 已提交,页面已分配,不违反页面保护属性可以成功访问,已提交的页面通常映射到物理内存或文件中了
  • 保留,页面未提交,但保留了地址范围供以后可能发生的提交操作使用(比如:分配内存时利用保留内存维护连续的虚拟地址空间)

系统内存地址在每个进程里都是一样的,32 位系统位于高 2G,64 位系统位于高 128T 的位置

系统空间里有系统内核,硬件抽象层 HAL 和内核驱动,用户模式的程序不能访问到系统空间去

如果一个内核驱动产生了内存泄漏,驱动卸载之后依然得不到释放

如果一个用户程序产生了内存泄露,内核会负责关闭和释放超出进程生命周期的泄露内存

1.3 线程

线程是执行代码的实体,也是一个对象,使用进程提供的资源来完成任务

线程重要的信息有:当前访问模式、执行上下文、一个栈或两个栈、TLS 数组、基本优先级和当前优先级、处理器亲和性 affinity

线程的运行状态常见的有三种:运行、就绪、等待


线程有一个内核栈(32 位下 12KB,64 位下 24KB),有一个用户空间栈(默认可以扩大到 1MB)

当线程处于运行和就绪状态时,内核栈一直驻留在物理内存中,用户栈则有可能会被页面换出

用户栈的处理:起始只提交一小块内存,大概一页这么大,然后剩下空间作为保留内存,以保证栈的虚拟空间是连续的,在需要更多栈空间的时候栈能够增大(按需增长)

系统用 PAGE_GUARD 标记已提交的下一页,当线程需要更多栈空间会把内容写入到警戒页面,然后产生一个异常被内存管理器处理,移除当前页面警戒状态并提交,然后设置下一页为警戒保护

用户栈大小默认是 PE 头指定的提交大小和保留大小,使用 CreateThread 和 CreatRemoteThread 函数可以指定提交大小保留大小,未公开的函数 NtCreateThreadEx 允许同时指定两个值

1.4 系统服务

系统 API 的调用最后通过 ntdll.dll 的特殊 cpu 指令进入内核模式,跳转到系统服务分发器的例程,然后根据 eax 的值作为 SSDT 的索引跳转至响应的系统服务进行执行,执行完成之后会返回用户模式

1.5 系统总体架构

Windows 子系统运行的映像文件是 csrss.exe,帮助内核对 Windows 系统中运行的进程进行管理,一旦杀掉,系统就崩溃

后来 Windows10 1607 版本引入的 WSL 子系统,用了一个新的进程类型:微(pico)进程和微提供者(pico provider),微进程是空地址空间的进程,供 WSL 进程使用,WSL 进程运行时的每个 Linux 系统调用都会被微提供者截取转换成等价于 Windows 的系统调用

1.6 句柄和对象

Windows 系统内核有很多种可用的对象,用户程序通过句柄来间接访问对象,内核程序则可直接通过对象指针进行访问,也可将句柄转换成对象指针进行访问

句柄是指向一个表格的入口索引,表格在进程的基础上维护,逻辑上指向一个系统内核对象,句柄的值从 4 开始,是 4 的倍数

内核模式使用对象的时候,通过 ObReferenceObjectByHandle 函数从有效的句柄种获得指针,该函数会使对象的引用计数 +1,当用完之后需要使用 ObDerefenceObject 函数将对象的引用计数-1,如果忘了,会造成资源泄露,只有重启才能解决

当对象的引用计数=0 时,对象管理器会销毁对象


对象有些是有名称的,比如互斥体,事件等,有些是无名字的,比如进程,线程等用 id 标识的,文件名并非对象名

用户模式使用 CreateXXX 创建或打开一个对象,这里提供的对象名并非最终的对象名,名称前面会被自动添加上一些内容,比如 \Sessions\x\BaseNamedObjects\ 这里会标识是 Session0 还是 1 ,使用 Global\ 作为前缀来创建对象可以创建会话共享的对象

通过 winObj 和 Process Explorer 两个程序可以方便查看对象信息


访问已经存在的对象,在 Process Explorer 程序中,句柄视图下查看访问掩码,这个掩码表示,对该对象可进程什么操作:

image.png

双击句柄可查看对象相关信息:注意,这里的引用指的不是对象正在使用的引用计数

查看对象引用计数的正确方法是使用内核调试器的 !trueref 命令

image.png


评论