内核呓语系列1 —— 系统结构

2013-12-17 Nie.Meining Coding

学习内核有一段时间了,做了不少底层开发、驱动开发相关的东西,因此打算写一个系列,把一些内核相关的东西梳理梳理。

首先声明,这个系列不是系统地介绍内核原理,主要是整理一些底层开发中涉及到的东西,相当于一个开发笔记汇总,想到什么写什么,也许同样做底层开发的朋友们看到后能产生共鸣。我争取把这个系列坚持记录下去,水平有限出错难免,还望路过的大牛们多加指点。

废话不说了,进入正题。

作为内核相关的文章,还是从系统结构讲起吧。Windows和Linux内核一样,基本结构分3层,自底向上分别是硬件设备(即HAL层,提供硬件抽象)、系统环境(提供系统服务)、应用环境(使用系统服务):

nhyy1_1.png

其中HAL层跟硬件结合紧密,因此使用大量汇编语言,针对不同硬件环境提供不同的代码实现,所以在驱动开发时,应该尽量使用HAL层提供的宏来实现硬件I/O(如READ_PORT_BUFFER_UCHAR/WRITE_PORT_BUFFER_UCHAR),避免直接使用in/out等硬件依赖性强的汇编指令。其实这也是为什么操作系统必须要安装而不能拷贝的原因之一(安装时会根据硬件选择拷贝相应的HAL层文件)。说句题外话,有时候 ghost 版操作系统不稳定也跟 hal.dll 不配套有关。

对于Windows,系统环境层其实又分微内核执行体两层,不过这两层都在ntoskrnl.exe内核驱动中实现(根据系统是否开启了PAE、是否有多核处理器等因素,内核驱动会不一样。本文以ntoskrnl.exe为例)。微内核和执行体的分层主要体现在设计上,微内核负责线程调度、中断处理、对象同步等工作,执行体则直接向上层提供服务(SSDT/ShadowSSDT)。这种分层设计也是为什么EPROCESS/ETHREAD会内嵌KPROCESS/KTHREAD的原因:

nhyy1_2.pngnhyy1_3.png

Exxx由执行体管理,Kxxx由微内核管理,但有些数据执行体和微内核都需要查询,因此Exxx和Kxxx里也会有重复的东西,例如EPROCESS.ThreadListHead和KPROCESS.ThreadListHead(这种冗余的设计,对Linux来讲通常是不能接受的)

其实除了应用环境在Ring3,内核层执行体HAL都在Ring0。对咱们码农来讲,广义上只要是Ring0统统叫内核。

最后再讲一个Windows和Linux内核结构中的巨大区别。众所周知,Linux的图形化支持并不是必须的,其用户通常能从长串的命令敲击中获得异样的快感,但Windows把图形支持作为Windows子系统的一部分,放到了内核中,这也是Windows的UI响应比Linux快的原因。Linux采用命令行终端作为shell,而Windows中的shell就是著名的Explorer.exe:

nhyy1_4.png

在用户登录后由userinit.exe进程读取该值启动shell程序(默认为Explorer.exe)。因此也有很多邪恶的东西会尝试改变这个值……

另外还有一个有趣的注册表键:

nhyy1_5.png

该键的默认值是1,这就是为什么shell程序(即explorer.exe)在意外终止后通常会自动重启自己(重启是被Winlogon.exe启动的,userinit.exe早已退出了)。需要说明的是,如果进程退出码是1的话,不会被自动重启。这就是任务管理器杀死explorer.exe不会导致它自动重启的原因。任务管理器通过TerminateProcess(hProc, 1)的方式杀进程:

nhyy1_6.png

图形支持只是windows子系统中的一部分。Windows最初试图支持posix、os/2和windows三个子系统(posix、os/2子系统实质上也是由windows子系统实现),不过后来到Windows XP的时候似乎只剩下Windows子系统了。可执行程序的PE结构中专门有个Subsystem域说明该程序运行的子系统类别。用LordPE程序抓个图,可以看到Subsystem域对GUI、CUI也做了区分:

nhyy1_7.png

第一篇先写到这里吧。其实系统结构是个很庞大的话题,其中还有很多有趣的东西和实现的细节,留到以后讲各个组件的时候再细讲吧。下一章打算展开讲讲Windows子系统。

发表评论:

Powered by emlog