-
-
[原创]分析Windows Section Object机制
-
发表于: 6天前 333
-
先导内容:
这里写得较为详细,也有些混乱,主要供以后我遇到相关问题时查阅使用。
Section Object,它是一个系统级的、独立于任何进程的对象,代表了一段可以被多个进程共享的物理存储资源。这个资源可以是内存(由页文件pagefile.sys支持),也可以是磁盘上的文件(如.exe、.dll或数据文件)。通过节对象,多个进程可以映射同一份数据到各自的地址空间,实现内存共享或文件的内存映射。
我们可以使用Process Explorer来查看某个进程的节对象句柄:

这是三个对于理解Section Object工作原理与应用的重要概念:
Address就是该section object映射的进程地址空间对应的VAD节点。
Segment是真正描述section object数据的对象。
引自潘爱民老师的《Windows内核原理与实现》:

用户态 (Win32 API):
内核态 (内核API):
下面我们通过一个示例程序来更详细地了解section object的工作机制:

首先我们调用CreateFileMappingW创建了一个Section Object。这里拿到的HANDLE的值是0xE8。那么我们来看看其对应的Section内核对象。首先拿到进程的基本信息:

拿到Cid,就可以使用!handle命令取得句柄对应的内核对象信息了:

可以看到HandleCount为1,说明有一个进程引用了这个Section Object,自然就是我们的示例程序了:

下面分析这个Section Object对象的结构。
于是我们拿到了Section Object地址:ffff808652d39d30。我们查看其结构:

在输出中,可以看到这个节对象被挂载到了一颗AVL树上,Windows系统使用一颗AVL树来管理所有Section Object。
由于我们在CreatFileMappingW中传递了INVALID_HANDLE_VALUE,使用了系统分页文件作为物理存储,那么其StartingVpn和EndingVpn就都为0。
还可以看到该Section Object大小为4KB,我们在CreatFileMappingW中填写的是1024,但是最小也是以页大小为单位创建Section Object。还可以看到页保护属性字段正是我们在CreateFileMappingW中填写的PAGE_READWRITE:

_SECTION中的u1联合体中是CONTROL_AREA,当然也有是FileObject的情况(以磁盘文件作为物理存储时):

我们查看CONTROL_AREA:

_CONTROL_AREA 是连接 _SECTION(逻辑对象)与 _SEGMENT(物理/页文件存储)的桥梁。它不关心映射到哪个进程的哪个地址,只关心“这块内存的物理存储状态”和“当前有多少人在用”。
注意到 +0x008 处同时显示了 ListHead 和 AweContext:
这是 _CONTROL_AREA 中的一个联合体(Union)。
这个结构不关心有多少人在看这块内存(MappedViews),也不关心是谁在看。它只负责一件事:管理这块虚拟内存实际占用的物理存储资源(物理内存页或分页文件槽位)。
对于NumberOfCommittedPages = 0 与 PrototypePte:
我们的进程是映射了这个Section Object对应的物理存储到虚拟地址空间的,从VAD中可以看到:

我们查看这个VAD节点的详细信息:

可以看到其中_MMVAD中和Section Object相关的字段,和我们通过_SECTION结构获取的对应字段,值是一致的。
具体如何通过__MMVAD解析得到Section Object这里就演示了。
我们开始调试这个程序。
)
反汇编CreateFileMappingW,它只是把用户态参数格式转换为内核态参数格式,然后调用NtCreateSection:
从这里准备进入内核态。
这里进行了0x4A号系统调用,我们查找SSDT表基址为:

根据索引号计算在SSDT表中的偏移,右移四位后加上SSDT基址,得到0x4A对应的函数地址:

偏移为0x087a0003,右移4位加上SSDT基址得到:

那么我们开始分析nt!NtCreateSection函数:

注意 a10 = 1(倒数第二个参数):这个标志表示"来自NtCreateSection系统调用"还是"来自其他入口"——它影响 MiCaptureSectionCreateExtendedParameters 中的权限检查和 MiCreateSectionCommon 中是否获取进程令牌。
接下来分析MiCreateSectionCommon:
首席是AllocationAttributes 合法性检查(大量位掩码,非法即返回 STATUS_INVALID_PARAMETER_6 = 0xC00000F4)
这些是 SEC_* 标志的有效组合校验(例如 SEC_RESERVE 和 SEC_COMMIT 冲突等)。
然后是用户/内核模式敏感的指针捕获:
这是 Nt 函数的标志性行为:由于参数来自用户态,必须在异常捕获下安全读取指针。
接着捕获扩展参数 + 获取令牌/会话
这里解释了为什么需要 a10=1 标志:该标志决定是否引用进程主令牌——文件映射需要以进程令牌做后续的文件访问权限检查。
然后调用 MiCreateSection + 重试循环
重试循环处理文件映射创建时遇到的瞬态条件(如文件正在被锁定/删除中)。
最后成功后处理 + 句柄创建(NtCreateSection 的收尾):
CcZeroEndOfLastPage 值得注意:它把映射文件最后一页末尾的未使用字节清零,防止映射视图泄漏文件中未初始化的数据(信息泄露防护)。
在NtCreateSectionEx中,调用了MiCreateSection,这个函数的分派逻辑如下:
我们的示例代码中创建的文件映射对应的物理存储是页交换文件,所以调用的是MiCreatePagingFileMap。注意到MiCreateSection中调用了MiLogSectionObjectEvent,这里会触发ETW,我们可以使用ETW检测Section Object的创建。
注意,这是从用户态创建Section Object的调用链分析。如果使用了MmCreateSection这个内核态API,它会调用MmCreateSectionEx,然后也会调用MiCreatePagingFileMap或者MiCreateImageOrDataSection。
对于MmCreateSection,潘爱民老师的《Windows内核原理与实现》中做了详细分析。不过要注意的是,现代Windows内核中MmCreateSection相关实现与WRK中给出的源代码略有不同,应该要
那么对于创建section object的分析就到这里,如果继续再深入分析的话,我的水平还不够,这里浅尝辄止。
下面分析MapViewOfFile,也就是映射这一步的流程。
首先调用了MapViewOfFile:

也是对参数进行一些准备后,调用了NtMapViewOfSection:

由此准备进入内核,可以看到系统调用号是0x28。
总体调用链:
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。