
本书主要讲述 Windows 内核的设计过程,从最底层的细节使用源码一步一步分析,结 合 Bochs 和 WinDbg 调试器进行验证。本书提供全部源代码和能直接编译的项目工程,集理论、架构、编码、运行和调试于一体进行讲述,从多种角度呈现内核构架的基本流程。本书主要包括了 Windows 内核加载器(ntldr)的分析,Windows 内核调试组件的设计,实现结构化异常处理的支持,并对内存管理和对象管理进行了精心讲解,同时对基于 IRP 请求包的 I/O 系统进行了论述,并且介绍了如何设计文件系统,最后简单讲解了进程和线程的一些基本知识。
推荐序1 关于操作系统内核设计的书一般内容都庞大复杂,细致精微,读起来相当费劲。比起一般的技术书籍来说,阅读的过程痛苦程度高了很多。其实最要命的是将这些章节一一读完弄懂之后,我们稍有的成就感转瞬即逝,另一个问题接踵而来:我搞明白了这些东西,究竟能做什么用?要知道满大街的程序员,会被拉去开发操作系统的,一百个里边也许都没有一个。 操作系统是一种基础设施,在信息化的世界里,它就像电力和自来水一样。现在有不少呼声,我们要开发自己的操作系统。然而每次真正有适合 PC 端的操作系统开发出来,立刻会被网上一大批人一边倒地鄙视:“抄袭”、“拿 Linux 改个名就说是自己的”、“骗科研经费”等等说法不绝于耳。其实并非这些人真的体验过新的操作系统,也不是他们真的了解这些开发过程的容易或艰辛。而是每个人心里都很清楚,无论新的操作系统做得有多好,它都不会像 Windows 这样在千万个用户的电脑上流行起来。所以他们的鄙视之言永远都不会得到事实的反驳,他们可以放心大胆地在网络言论上通过“喷”来轻松地显示自己的睿智和存在感。 这是因为操作系统并非一个孤立的东西。就像自来水厂绝非一处水源加一个处理工厂。自来水厂的核心竞争力反而不是水源,而是那千万根连通到客户家中的水管。我很容易就能新找一处水源,建设一个自来水厂。但是谁家愿意为我把自己家的水管敲掉换成我的?在漫长的时间里,我们已经习惯了使用 Windows 上的软件。就算现在有一款更好的操作系统,但它无法运行我需要的软件,我也会毫不犹豫地拒绝它。假设这款操作系统能完美地兼容 Windows 的软件,又会面临另一个问题:既然开发者都知道只需要给 Windows 开发软件,就能兼容另一个操作系统,那么开发者就永远只会给 Windows 开发软件了。本来因为软件数量不足而兼容 Windows 软件,然后又因此而导致软件数量更少了。这成为一个恶性循环!Windows 只要略微升级,改变特性,这款新的操作系统也得随之升级,否则新版的软件又无法支持。命运完全悬于人手。 所以操作系统能形成垄断,并非因为技术有多高明。说什么 Windows 有几千万行代码所以永远也不能超过那完全是扯淡。难道说越臃肿的东西就越是强大?Windows 的几千万行代码的规模反而有可能是历史的不断积累,垃圾代码无法清除(为了兼容旧版软件和扯不清的部门利益)而导致的。从头开发一个新的操作系统反而轻松很多。问题是操作系统天然属于占坑型的软件。一旦占住了这个坑,除非占坑者主动放弃,否则垄断很难被攻破。这不是一个技术问题。PC 上的 Windows 如此,移动平台上的 Android 与 iOS 也如此。 那为什么我们还要学习它? 因为我们可以使用它,但不能任它忽悠我们,即便它像自来水一样便宜(目前 Windows依然贵得离谱,这是当代奇葩之一),或者干脆就是免费的(比如 Android)。笔者倾向于操作系统会越来越便宜,最终变成免费或者接近于免费的,经济上应该不会成为一个问题。但我们必须了解它,熟知它的问题。就像我们得了解我们每天喝的自来水,里边有漂白粉,甚至抗生素! 只有真正明白学了做什么用,才有可能有学习的动力,才有可能真正学好。如今要开发自主版权的操作系统的呼声,很大一部分来自安全的需要。没有人愿意让自己安全的命脉攥在别人的手里。其实我反而觉得,想要真正把握自己安全的命脉,与其等待自己开发的操作系统去夺了已经被占的坑,不如充分地了解现在占坑的操作系统是怎么运作的。一个操作系统不管多么封闭多么神秘,它总归是一堆二进制代码。它的执行过程是可以被分析被监控的。至于有人说缺少源代码,事实上国家是可以要求微软提供源代码的。问题倒在于分析源代码的人:如果你不懂操作系统,那么纵然三千万行源代码摆在你面前,里边堂而皇之地写上一百个漏洞,你又能找出来一个吗? 学习这些前辈们的作品,读者不但能了解前辈们的英明睿智,还能发现他们的局限。希望读者不要做一个单纯的膜拜者和盲从者。 如果你觉得 Windows 的 64 位版驱动要验签名了,内核有 PatchGuard 了,所以更安全了;如果你觉得你用的是“水果”,不越狱所以固若金汤;如果你觉得 Android 不 root 就是安全的,我只能说你还处于蒙昧无知的状态。如果你感觉到了哪里可能会有漏洞,而你可以一一填补,说明你混沌初开了。如果你像某些大公司的系统设计员一样,忽然冒出一个前无古人的想法,认为这样可以一劳永逸地解决安全问题,说明你开始了独立思考,可以着手安全问题了!如果你终于回到现实,甘愿像被缚的普罗米修斯一样,今夜痊愈,明日又开始新一轮的开膛破肚,永无休止,那你终于走上了正途! 想要开始这个奇妙的旅程,就打开本书,认真了解 Windows 内核设计的每个细节吧。我谨以此文,祝贺陈老师,祝贺《Windows 内核设计思想》一书出版。 畅销书《天书夜读——从汇编语言到 Windows 内核编程》 《寒江独钓——Windows 内核安全编程》作者 谭文 2015 年 1 月 13 日 推荐序2 认识陈树宝先生到现在大概也有 5 年时间了,他是个技术深厚、勤奋踏实的开发牛人,常常承担项目主要的开发职责。刚接触他时就知道他在研究 Windows 内核方面下了很大的工夫,从系统启动阶段到系统各种管理机制都有研究,例如内存管理、对象管理、进程管理、I/O 系统、文件系统等,我甚是佩服他在这方面的功底造诣。由于自己对 Windows 内核了解不多,遇到这方面的疑问,偶尔也会请教于他,总能得到一些收获。 相比于历来都是扎堆热门的 Linux 内核研究,以及雨后春般蓬勃发展起来的 Andriod系统类研究书籍,国内对 Windows 内核进行系统地全面深入研究的书籍实在太少了。据我浅薄的见闻所知,大概也只有毛德操先生和潘爱民先生的著作属于这类书籍,当然还有翻译自国外经典的《Windows Internals》(中文版为《深入解析 Windows 操作系统》)一书。实际上,国内研究 Windows 内核的人非常多,但常常限于坊间研究,能愿意公布出来分享的不多。或者是限于本职工作范围内所使用到的,或者是一些零零散散的资料。 当我知道陈树宝先生打算写《Windows 内核设计思想》一书,我感到非常高兴。一来能 将他这几年积累的知识呈现出来,我觉得这对国内想进入或者想了解这个领域的读者来说 是幸运的,至少可以少走一些他以前走过的弯路;二来通过书籍能够实现自己一定的价值,并且为这个领域贡献出一份力量。我们都知道,沉醉于研究对于那些务实的技术人来说是孤独的,需要有毅力恒心。而花费时间将自己的研究心血分享出来更是难得的。在起笔此书时,我也曾经问过陈树宝先生打算写哪方面的内容,也有一些疑虑,毕竟要写好这类“大气”的书是不容易的,不仅涉及知识面广,而且也需要这方面能力的自信。所幸的是,由于知识的积累,陈树宝先生轻车熟路,手到擒来,竟似不费功夫,让我消除了这个疑虑。 我很高兴为陈树宝先生的《Windows 内核设计思想》一书作序推荐。我了解到的信息, 也正如陈树宝先生在《Windows 内核设计思想》一书前言所说的,这本书是以介绍源码(这个源码有不少是自己整理和修改过并经过验证的),结合相应的调试来展现出 Windows 内核的设计思路,通过调试代码能让读者更好地理解整个内核框架。我细看了此书的目录,第 1 章介绍了搭建开发和调试环境,第 2 章主要介绍 OS 的引导阶段,而第 3 章更是直捣黄龙,进一步深入地介绍 Windows 的调试机制,可见调试在这本书里是非常重要的。作者非常注重实践,毫无保留地呈现核心的内容。在第 6 章开始介绍了热门的,也是必不可少的内存管理机制。我们知道,内存管理对一个 OS 来说太重要了,Windows 设计了一套特别的内存管理手段,这部分必然是非常值得一看的。第 7 章的对象管理更是 Windows 独特的地方,对象管理充斥于 Windows 的每个地方,这个机制非常重要。I/O 系统、文件系统、进程管理在本书中都能看到。总而言之,这是一本非常棒的好书。 作者在根据自己心得总结整理出来的代码基础上,结合系统机理,通过调试验证。我 认为这是本书的特别之处,它有别于其他过于理论描述的书籍,也有别于其他直接介绍源 代码的书籍,是一本值得期待的书。 畅销书《x86/64 体系探索及编程》《处理器虚拟化技术》作者 邓志 2015 年 1 月 11 日 序言 与 Linux 作为一套自由开发的类 UNIX 操作系统不同,Windows 内核代码长期以来为微 软所控制,始终保持云遮雾罩的神秘感。尽管在 2006 年微软针对教学和科研的目的,开放了 Windows 内核的部分源码 WRK(Windows Research Kernel),但是由于知识产权的问题,并不能摆在台面上来讲,有关这方面的原因,在毛德操老师所著的《Windows 内核情景分析——采用开源代码 ReactOS》中讲过。为此,笔者在本书中为求系统架构、程序流程设计的完整性,同时保证代码的高质量,经历了数次重构,其中第一次设计使用的就是 WRK代码,因为这是最正统的 Windows 代码,只是代码不能大幅度引用,造成了完整性的缺失,而本书要求能完整编译的开源代码,如果不这样,会影响读者的理解,于是放弃。笔者随后想到了 ReactOS 开源项目,但是 ReactOS 源码偏多,于是在第二次设计中使用的是 WRK 和ReactOS 代码的结合,即使用 ReactOS 代码去填充 WRK 的不完整部分,这种方法虽然保证了代码的完整性,但是由于代码的风格和设计不同,使得代码整体变得杂乱,因此这种方案也不可取。最终笔者抛弃了 WRK,采用完整的 ReactOS 代码(经过部分修改),虽然与正统的 WRK 代码不同,但是原理是一样的,而且表达很简洁,完全保留了 Windows 内核设计的中心思想,更有利于读者的学习。为了照顾部分读者学习 WRK,笔者在 MBR 处提供了切换回 WRK 的系统编译选项,有关 WRK 的更多知识,请参考潘爱民老师所著《Windows内核原理与实现》。 无论使用的是什么样的代码方式,本书都保证了理论上的正确性,大多数的理论都经 过了笔者的调试验证,而笔者也将在本书中向读者一步步展示自己对这些理论的理解和验 证过程,集理论、架构、编码、运行和调试于一体进行讲解,多角度全方位体现一个操作系统内核的完整设计过程。 Windows 内核的源码是相当多的,单就一本书是不可能完全讲解完的,笔者也自认为 还没有足够的能力去全部讲解。但重要的不是量多,而是质量上乘,精炼,笔者本着确保 读者能在 Windows 内核设计上形成一条清晰主线的目的,精选了内核设计上最重要的代码 片段供读者学习。笔者认为,只要读者认真理解了这些代码片段,想要再深入学习别的部 分就基本不成问题了,无论你是做 Win32 应用程序编程的,还是做内核驱动编程的,都已 经有足够的思路去设计你的代码,写自己的东西,而且一旦出现问题,也有足够的能力去 找到解决问题的方法。 在本书代码的设计中,笔者本人也按照 Windows 内核原理写过不少代码,但最终还是 决定不收纳在本书中。原因是本书的用意是讲解 Windows 设计的核心原理,应该力求让读 者掌握 Windows 设计最本质的东西,而不被其他繁杂的细节所影响。当读者认真读完这本 书之后,相信也会有像笔者一样的冲动,想要根据自己的想法在功能不变的基础上去设计 自己的代码片段,自由构架操作系统。 如果读者在学习过程中遇到问题,可以登录网站 book.yetingyu.com 获得更多的知识。 特别致谢 郑少华,华南师范大学计算机学院研究生,参与了本书部分章节的编写,处理了全部 书稿的文字错误,并进行了代码的整理、编译、链接和调试验证。 谭添升,微软公司程序员,在本书第 6 章的设计和编写过程中,给予了不少建议并修 改了不少问题。 邓志,《x86/64 体系探索及编程》和《处理器虚拟化技术》的作者,修正了本书第 2 章 中的不少问题并为本书作序推荐。 谭文,《天书夜读——从汇编语言到 Windows 内核编程》和《寒江独钓——Windows 内 核安全编程》的作者,为本书作序推荐。 李冰,本书的策划编辑,对于笔者的写作给予了充分认可和支持,她富有主见,决策 果断,参与了本书的命名。 白涛,本书的技术编辑,处理问题非常细致,认真修改了书中的多处错误,并且把本 书流程整理得有条不紊。 黄爱萍,电子工业出版社博文视点编辑,在本书写作过程中非常耐心地解答笔者提出 的问题,并给予鼓励。 陈树宝 2015 年 1 月 14 日 前言 本书最大的特色在于不从纯理论角度进行介绍,而是以源码与调试相结合来阐释。在 ReactOS 源码的基础上,剔除掉一些不那么重要,且影响大家整体把握 Windows 内核设 计的部分,修改和整理出一个新的源码版本,通过对这个源码版本用调试工具 Bochs 和 WinDbg 进行调试,再结合重点模块的论述,以及关键函数完整详细的分析,使读者能对整 个 Windows 内核框架透彻理解,并能根据自己的想法自由设计操作系统。 以下是书中介绍的读者可能比较感兴趣的知识点: 1. 物理内存如何收集? 2. 实模式和保护模式如何互相切换? 3. 系统中断如何安装? 4. 调试系统如何设计? 5. 分页机制如何开启? 6. 内核模块和启动型驱动如何加载? 7. 结构化异常如何为编程语言提供支持? 8. 如何为数据结构与算法学习提供支持? 9. 物理内存和虚拟内存如何进行管理? 10. 内核对象如何设计? 11. I/O 管理器如何为驱动程序提供支持? 12. 磁盘类驱动和微端口驱动如何读/写数据? 13. 文件系统如何设计?缓存怎样提供支持? 14. 原型 PTE 是什么?(section object)如何实现进程间共享? 15. NTDLL.DLL 如何映射到进程地址空间? 16. 第一个用户进程和线程怎样创建?线程如何切换? 为了使读者能从整体上把握本书讲述的内容,我们先做总体概述,提供一些视图供大 家理解,这些视图跟部分章节相关。 内核启动基本流程 本书主要探索的是 Windows 内核的设计部分,分析内核构架和代码细节。本书编写的 理论基础源于 ReactOS 和 WRK,为了保证本书编写的理论正确性,每一部分内容都经过了 调试验证。学习流程从 MBR(master boot record,即主引导记录)到内核创建的第一个用户 进程(smss.exe),图 1大体展示了内核初始化的各个阶段。 图 1 1. 计算机启动时控制权交给 MBR,MBR 负责把 NTLDR 加载到物理内存 0x20000。控 制权交给 NTLDR 前半部分 SU 模块(startup.com)。 2. SU 收集物理内存、开启 A20、开启保护模式、解析 NTLDR 文件的后半部分 Loader 模块(osloader.exe)。解析 PE 文件格式重定位到物理内存 0x00400000。控制权交给 Loader 模块。 3. Loader 模块初始化调试系统,初始化内存管理器,开启分页机制,加载内核模块和 启动型驱动程序到内核地址空间。控制权交给 ntoskrnl。 4. KiSystemStartup 进行内核初始化阶段 0:初始化调试系统,初始化 Idle 进程和线程, 初始化内核执行体,初始化 HAL 系统,初始化内存系统,初始化对象系统,初始化进程系 统(创建 System 进程和线程)。最后这条主线程转到 KiIdleLoop 成为真正的空闲线程。 5. KiIdleLoop 切换 Idle 线程到 System 线程执行,进行内核初始化阶段 1:初始化 HAL 系统,初始化对象系统,初始化注册表系统,初始化 I/O 系统,初始化进程系统,创建第一 个用户进程(smss.exe)。最后这条主线程成为零页面线程。 由于内核涉及的内容非常多,因此并不是每一部分都详细讲述,但是经过本书的学习, 再去理解那些未讲述的部分,会相对容易得多。 调试运行 WRK 图 2是使用 ntldr 加载运行的 WRK v1.2 系统界面,物理内存为 256MB,硬盘为 16MB, 调试器为 WinDbg,通信端口为 COM 1。我们使用的虚拟机是 VMware Workstation。 图 2 图 3所示是在内核初始化阶段 1,函数 IoInitSystem 初始化启动型驱动程序后的界面。 图 3 系统加载的文件是从“Windows Server 2003 Enterprise Edition Server Pack 1”操作系统 里复制过来的。可使用本书编写的简单文件系统 ntfs.sys 替换原版 ntfs.sys。 调试运行 ReactOS 图 4是笔者自己编译的 ReactOS 的调试运行系统界面,物理内存为 256MB,硬盘为 16MB,调试器为 WinDbg,通信端口为 COM 1。通过修改文件 boot.asm 里面的代码,ntldr 加载的就是 ReactOS 而不是 WRK 了。我们使用的虚拟机是 Bochs。 图 4 图 5所示是在内核初始化阶段 1,函数 IoInitSystem 初始化启动型驱动后显示的界面。 图 5 这些驱动程序全部以源码方式编译,可跟踪调试。FASTFAT 是我们自已实现的简单文 件系统驱动(重新构架)。磁盘类驱动 DISK 和微端口驱动 UNIATA 实现了对磁盘数据的 读/写支持。通过 I/O 管理器,弄清楚一个文件如何解析,如何从分区读到内存。 使用原版 ReactOS 系统环境调试 一直跟随ReactOS成长的读者,可以按照下面的操作替换内核模块。从http://sourceforge. net/projects/reactos/files/ReactOS/0.3.15/ 下载 ReactOS-0.3.15-REL-iso.zip,解压安装 ReactOS 0.3.15 系统,注意虚拟磁盘类型选择 IDE(默认是 SCSI)。系统安装完成后,虽然 ReactOS 默 认提供了调试器 RosDBG.exe 可供调试,但是它并不友好,也无法进行源码调试,非常不利 于学习。现在我们寻求一种替代方案——WinDbg。我们已经编译好了内核模块 ntoskrnl.exe, 这个模块自带了 kd 调试组件,现在我们把 ntoskrnl.exe 替换原版 ReactOS 中的 ntoskrnl.exe。 编辑虚拟机参数:在主界面中单击“Edit virtual machine settings”,弹出如图 6所示窗口。 图 6 单击设备“Hard Disk (IDE)”,在右边的“Utilities”下拉菜单中选择“Map”,在弹出的 窗口中取消勾选“Open file in read-only mode (recommended)”,单击“OK”按钮确认。这 样我们就使用映射的方式打开了 ReactOS 系统盘。找到目录Z:ReactOSsystem32,把原文 件 ntoskrnl.exe 改名为 ntoskrnl2.exe,方便以后我们需要时再改回去。复制我们的 ntoskrnl 项目生成的文件 ntoskrnl.exe(在ntosDebug 目录下)到Z:ReactOSsystem32。复制完成后关 闭窗口,再在“Utilities”下拉菜单中选择“Disconnect”,如果弹出提示框,单击“Force Disconnect”。这样就替换了 ntoskrnl.exe。如果需要可以通过同样的方式替换生成的启动型 驱动程序 acpi.sys、pci.sys 和 disk.sys。说到这里,我们简单提一点,在第 2 章中,我们讲述 到 LDFS 文件格式,我们也可以使用 NTFS 文件格式,映射 ntos.vmdk 到虚拟盘符后,格式成 NTFS 分区就行。但是这样一来,我们要修改不少代码,MBR 代码需要重写,因为需要 解析 NTFS 格式来找到 NTLDR。 替换完内核模块后,还需要开启 com_1 端口用于 WinDbg 连接通信。启动系统后,在 屏幕提示选项中按上下键选择“ReactOS (Debug)”回车,如图 7所示。 图 7 等待一小会,WinDbg 和 ReactOS 就连接上了。通过这种方式,我们在学习时可以进行 扩展,对不同的模块,可以分块编译后替换,尝试运行,看和原版中的有什么差别,也可 以按自已的设计修改部分代码,再编译、链接、运行。这样学习才能对系统的整体构架比 较熟悉,理解也会比较深刻。图 8所示是在内核初始化阶段 1,函数 IoInitSystem 初始化启 动型驱动后显示的界面。 图 8 这里我们能观察到启动型(SERVICE_BOOT_START)驱动的加载和系统初始化阶段型 (SERVICE_SYSTEM_START)驱动的加载。 阅读须知 在本书的某些章节中,有些函数相对较长,比如第 7 章讲述的查找对象名函数 (ObpLookupObjectName)就占用了十几页,书中我们保留了全部的细节,是为了保持函数 的完整性,而且一旦读者熟悉了这个函数,整个对象管理器的核心就一目了然了。 在将章节内容整理成书时,难免出现一些错误,有些章节虽然经过了多次修改,也仍 然有不如意的地方,读者在阅读时要结合源码调试去理解,以免出现理解偏差。 本书配套资源下载地址:http://www.broadview.com.cn/25314。 参考资料 本书成书过程中主要参考了 Windows Internals, 6th Edition(Mark Russinovich、David A. Solomon和Alexlonescu合著,中文版为《深入解析Windows操作系统(第6版)》)和Intel64 and IA-32 Architectures Software Developer’s Manual。此外,还参考了 ReactOS 官方网站(http: //www.reactos.org)、微软网站(http://www.microsoft.com)、维基百科(http://en.wikipedia.org) 及http://www.debuginfo.com/articles/debuginfomatch.html上的一些资料。
目录 推荐序1 iii 推荐序2 v 序言 vii 前言 ix 第1章 搭建开发和调试环境 1 1.1 编译环境 1 1.2 使用 Bochs 运行 ntos.img 2 1.3 使用 VMware 运行 ntos.vmdk 3 1.4 模拟调试运行 6 1.5 编程思想(等差数列求和) 8 1.6 函数调用约定 14 1.7 模块扩展约定 15 1.8 本章总结 15 第2章 Windows 内核加载器(SU) 16 2.1 主引导记录(MBR) 18 2.2 系统分区(Partition1) 25 2.3 SU 模块(startup.com) 28 2.3.1 原版 Windows Server 2003 系统 NTLDR startup.com 基本分析 28 2.3.2 根据原理重新构建新的 startup.com 30 2.4 检测物理内存 32 2.5 开启 A20 地址线 40 2.6 重新定位 GDT 和 IDT 42 2.7 保护模式 43 2.7.1 段描述符(Segment Descriptor) 43 2.7.2 特权级(privilege level) 48 2.7.3 段描述符表(segment descriptor table) 50 2.7.4 开启保护模式 51 2.8 加载 Loader 模块 53 2.9 转移控制权 59 2.10 导出函数 59 2.10.1 读写扇区 62 2.10.2 获取物理内存块 63 2.10.3 检测硬件 64 2.11 本章总结 64 第3章 Windows 内核调试设计 66 3.1 初始化调试系统 67 3.2 初始化模拟调试 74 3.3 初始化内核调试 76 3.3.1 中断和异常向量表 76 3.3.2 注册异常处理例程 79 3.3.3 基于栈框架的异常处理程序 82 3.3.4 除零错误(#DE) 85 3.3.5 单步异常(#DB) 90 3.3.6 断点异常(#BP) 91 3.3.7 一般保护性错误(#GP) 91 3.3.8 页面错误(#PF) 92 3.3.9 调试器服务(debugger service) 93 3.4 分发异常 94 3.5 内核调试分发 102 3.5.1 打印字符串 106 3.5.2 加载/卸载符号 109 3.5.3 报告异常 115 3.6 内核调试引擎核心 118 3.6.1 获取系统版本 125 3.6.2 获取/设置机器信息 126 3.6.3 读/写虚拟内存 126 3.6.4 设置/恢复断点 130 3.7 调试通信协议 136 3.7.1 串行端口 136 3.7.2 COM 寄存器 138 3.7.3 波特率 139 3.7.4 端口初始化 140 3.7.5 通信协议 142 3.7.6 读端口 142 3.7.7 写端口 146 3.7.8 调试包 147 3.7.9 接收调试包(KdReceivePacket) 150 3.7.10 发送调试包(KdSendPacket) 157 3.8 本章总结 160 第4章 Windows 内核加载器(Loader) 161 4.1 NtProcessStartup 162 4.2 PcMachInit 165 4.3 初始化内存管理器 166 4.3.1 内存描述符表 167 4.3.2 页面查找表 175 4.3.3 初始化堆 182 4.4 分页机制 195 4.4.1 页帧号(PFN) 195 4.4.2 页目录和页表 197 4.4.3 开启分页机制 198 4.4.4 虚拟地址转译物理地址 208 4.4.5 修改页目录和页表位置 210 4.5 Loader 读/写支持 211 4.5.1 ARC 接口标准 211 4.5.2 虚拟磁盘驱动(RamDisk) 219 4.5.3 磁盘驱动(Hwdisk) 220 4.5.4 文件系统驱动(Ldfs) 226 4.6 加载器参数块 232 4.7 加载和启动内核 233 4.7.1 收集硬件信息 238 4.7.2 本地语言支持(NLS) 239 4.7.3 加载模块 240 4.7.4 内存描述符链表 244 4.7.5 处理器控制域(PCR)和任务状态段(TSS) 246 4.8 本章总结 251 第5章 结构化异常处理 252 5.1 异常触发 254 5.2 分发异常(RtlDispatchException) 257 5.3 异常处理(_except_handler3) 262 5.4 全局展开(__global_unwind2) 266 5.5 局部展开(__local_unwind2) 272 5.6 冒泡排序(BubbleSort) 274 5.7 本章总结 275 第6章 内存管理 276 6.1 物理内存管理概述 276 6.2 虚拟地址空间布局 277 6.3 初始化内存系统(MmArmInitSystem) 278 6.4 初始化机器相关(MiInitMachineDependent) 296 6.5 初始化非分页池(MiInitializeNonPagedPool) 305 6.6 初始化系统 PTE(MiInitializeSystemPtes) 309 6.7 映射页帧数据库(MiMapPfnDatabase) 310 6.8 初始化颜色表(MiInitializeColorTables) 313 6.9 初始化页帧号数据库(MiInitializePfnDatabase) 315 6.9.1 从页面建立页帧号数据库(MiBuildPfnDatabaseFromPages) 319 6.9.2 从加载块物理内存链表建立页帧号数据库(MiBuildPfnDatabaseFromLoad- erBlock) 323 6.10 初始化池(InitializePool) 330 6.11 分配池页面(MiAllocatePoolPages) 335 6.11.1 非分页池分配 342 6.11.2 分页池分配 343 6.11.3 分页池页面错误处理 344 6.12 释放池页(MiFreePoolPages) 347 6.12.1 释放分页池 352 6.12.2 释放非分页池 353 6.13 建立分页池(MiBuildPagedPool) 354 6.14 分配池内存(ExAllocatePoolWithTag) 360 6.15 释放池内存(ExFreePoolWithTag) 371 6.16 初始化系统空间映射(MiInitializeSystemSpaceMap) 379 6.17 本章总结 381 第7章 对象管理 382 7.1 对象概述 382 7.1.1 对象整体结构 382 7.1.2 对象头(object header) 385 7.1.3 对象类型(object type) 386 7.1.4 对象类型例程模板(object type procedure template) 387 7.2 分配对象内存空间(ObpAllocateObject) 390 7.3 释放对象内存空间(ObpDeallocateObject) 397 7.4 初始化对象系统(ObInitSystem) 399 7.5 创建句柄表(ExCreateHandleTable) 406 7.6 创建对象类型(ObCreateObjectType) 410 7.7 创建目录对象(NtCreateDirectoryObject) 416 7.8 创建对象(ObCreateObject) 418 7.9 插入对象(ObInsertObject) 421 7.10 查找对象名(ObpLookupObjectName) 428 7.11 查找目录项(ObpLookupEntryDirectory) 445 7.12 创建无名句柄(ObpCreateUnnamedHandle) 448 7.13 分配句柄表项(ExpAllocateHandleTableEntry) 452 7.14 解析符号链接(ObpParseSymbolicLink) 465 7.15 Section 和 Segment 469 7.15.1 Prototype PTE 页面异常处理 478 7.15.2 Section 映射到系统进程 481 7.15.3 VAD 页面错误处理 482 7.16 本章总结 483 第8章 I/O 系统 485 8.1 初始化 I/O 系统(IoInitSystem) 486 8.2 创建驱动对象(IopCreateDriver) 497 8.3 创建设备对象(IoCreateDevice) 503 8.4 分配 I/O 请求包(IoAllocateIrp) 508 8.5 传递 I/O 请求包(IoCallDriver) 513 8.6 释放 I/O 请求包(IoFreeIrp) 514 8.7 解析设备(IopParseDevice) 515 8.8 磁盘读/写支持 525 8.9 本章总结 532 第9章 文件系统设计 533 9.1 文件系统初始化(DriverEntry) 533 9.2 挂载卷(NtfsMountVolume) 536 9.2.1 识别文件系统格式 540 9.2.2 文件控制块(FCB) 542 9.2.3 上下文控制块(CCB) 544 9.2.4 创建流文件对象(IoCreateStreamFileObject) 544 9.2.5 初始化缓存映射(CcInitializeCacheMap) 545 9.3 打开文件(NtfsOpenFile) 547 9.4 读文件(NtfsReadFile) 553 9.5 关闭文件(NtfsCloseFile) 555 9.6 发起卷挂载请求 561 9.7 加载 NTDLL 564 9.7.1 打开 NTDLL 564 9.7.2 检查 NTDLL 映像(MmCheckSystemImage) 565 9.7.3 创建 NTDLL Section 569 9.7.4 映射 NTDLL Section 到当前进程空间 570 9.7.5 获取用户进入点 571 9.8 本章总结 573 第 10 章 进程和线程 574 10.1 进程和线程初始化 575 10.1.1 初始化进程 576 10.1.2 初始化线程 578 10.1.3 初始化线程上下文 580 10.1.4 初始化进程系统(PspInitPhase0) 584 10.2 线程优先级(priority) 590 10.3 线程状态(thread state) 591 10.4 线程调度(thread dispatch) 593 10.5 分配进程虚拟内存(NtAllocateVirtualMemory) 601 10.6 创建用户进程(smss.exe) 608 10.7 本章总结 608 附录A PE格式 609