Windows内核

前言
学习一下Windows内核的一种安全机制:Windows 8 中引入的Early Launch Anti-Malware(ELAM)模块。该机制旨在防止内核地址空间中执行未经授权的代码,对于Rootkit来说,就是使Rootkit更加难以破坏系统。

ELAM模块
ELAM模块是一种用于Windows系统的检测机制,它允许第三方安全软件(如杀软)注册一个内核模式驱动程序,该驱动程序保证再启动过程的早期执行,即再其它第三方驱动程序加载之前。因此,当攻击者试图将恶意组件加载到Windows内核地址空间时,安全软件可以检查并阻止恶意驱动程序加载,因为ELAM驱动程序以及处于激活状态。

工作原理
驱动加载过程
在Windows系统引导过程中讲过,Windows 系统的启动分为多个阶段,简化下:

固件初始化
引导加载程序
Windows 启动管理器
Windows 内核加载
内核初始化
会话管理器初始化
用户登录
在 Windows 内核加载阶段,控制权会移交给Windows加载器Winload.exe,它会加载必要的内核文件如Ntoskrnl.exe,在此时会加载注册表中配置为 引导启动类型(Boot Start Type)的驱动程序,包括ELAM模块。

在内核加载后,但又未开始初始化驱动程序时,Windows会首先加载ELAM驱动程序。ELAM驱动程序被定义为Boot Start 驱动程序,优先级最高。

驱动程序在注册表中的位置:

HKLM\SYSTEM\CurrentControlSet\Services[ELAMDriverName]
在这里插入图片描述
start值为0x0,表明它是一个引导启动驱动程序,但这里我电脑是0x3,说明它是手动启动的状态。type为0x1,表明这是一个内核驱动程序。

当ELAM驱动程序被加载到内存后,初始化例程(DriverEntry)开始执行。

API回调例程
ELAM驱动程序通过特定的API注册回调例程,内核使用这些例程来评估系统注册表配置单元和引导驱动程序中的数据。这些回调检测恶意数据和模块,并防止它们被Windows加载和初始化。

Windows内核通过实现以下API例程注册和注销这些回调函数:

CmRegisterCallbackEx和CmUnRegisterCallback 注册和注销监视注册表数据回调.
ELAM 驱动可以通过注册这些回调来拦截对关键注册表数据(如 HKLM\SYSTEM 或特定安全策略)的访问,分析和阻止恶意修改。

CmRegisterCallbackEx函数原型:

NTSTATUS CmRegisterCallbackEx(
PCALLBACK_FUNCTION CallbackFunction, 指向回调函数的指针。
PCUNICODE_STRING Altitude, 回调的优先级,通常为字符串表示,如 "320000"
PVOID DriverObject, 驱动的 DRIVER_OBJECT 指针。
PVOID Context, 回调的上下文信息。
PLARGE_INTEGER Cookie 用于标识注册的回调实例,稍后通过 CmUnRegisterCallback 取消注册。
);
CmUnRegisterCallback函数原型:

NTSTATUS CmUnRegisterCallback(
LARGE_INTEGER Cookie 对应于 CmRegisterCallbackEx 的返回值,用于取消回调
);
IoRegisterBootDriverCallback和IoUnRegisterBootDriverCallback注册和注销启动驱动程序的回调。
ELAM 驱动程序使用这些回调函数,决定后续引导驱动程序是否允许加载。

IoRegisterBootDriverCallback函数原型:

NTSTATUS IoRegisterBootDriverCallback(
PIO_BOOT_DRIVER_CALLBACK CallbackFunction, 指向回调函数的指针。
PVOID Context 回调的上下文信息。
);
IoUnRegisterBootDriverCallback函数原型:

NTSTATUS IoUnRegisterBootDriverCallback(
PIO_BOOT_DRIVER_CALLBACK CallbackFunction 对应于 IoRegisterBootDriverCallback 的注册回调。
);
这些例程使用标准的 EX_CALLBACK_FUNCTION ,原型如下:

typedef VOID (*EX_CALLBACK_FUNCTION)(
PVOID CallbackContext,
PVOID Argument1,
PVOID Argument2
);
CallbackContext:上下文信息,由注册回调时提供。
Argument1 和 Argument2:回调的特定参数,因 API 而异。
一旦驱动程序执行了上述回调例程之一来注册回调,则参数CallbackContext会从ELAM驱动程序接收上下文。上下文是指向内存缓冲区的指针,该缓冲区包含ELAM驱动程序特定的参数,任何回调例程都可以访问该参数。此上下文还是一个用于存储ELAM驱动程序当前状态的指针。

回调类型
上面原型中的Argument1也是指回调类型,对于引导启动驱动程序,它可以是下列类型之一:

BdCbStatusUpdate向ELAM驱动程序提供有关驱动程序依赖项或引导驱动程序的加载的状态更新。
当 Argument1 为 BdCbStatusUpdate 时,Argument2 通常是指向 BOOT_DRIVER_STATUS_UPDATE 数据结构的指针。示例结构:

typedef struct _BOOT_DRIVER_STATUS_UPDATE {
UNICODE_STRING DriverPath; // 当前驱动的路径
NTSTATUS Status; // 加载状态,例如 STATUS_SUCCESS 或错误代码
} BOOT_DRIVER_STATUS_UPDATE, *PBOOT_DRIVER_STATUS_UPDATE;
BdCbInitializeImage提供引导驱动程序的元数据,允许 ELAM 驱动对其进行分类。
LAM 驱动基于驱动程序的签名、路径或自定义策略分类驱动:

BootDriverGood: 信任并加载。
BootDriverBad: 阻止加载。
BootDriverUnknown: 未知状态,可根据策略处理。
当 Argument1 为 BdCbInitializeImage 时,Argument2 通常是指向 BOOT_DRIVER_INFO 数据结构的指针。示例结构:
typedef struct _BOOT_DRIVER_INFO {
UNICODE_STRING FilePath; // 驱动程序的文件路径
PVOID ImageBase; // 驱动程序在内存中的基地址
ULONG ImageSize; // 驱动程序映像的大小
ULONG Classification; // 当前分类状态(由回调函数更新)
} BOOT_DRIVER_INFO, *PBOOT_DRIVER_INFO;
引导驱动程序的分类
Argument2参数表示操作系统对引导驱动程序的分类信息,如上所说的,分为三类:非恶意(信任并加载)、恶意(阻止加载)以及未知。

然而,ELAM驱动程序只能基于以下有限的数据对驱动程序映像进行分类:

映像名称
映像注册为引导驱动程序的注册表位置
映像文件的证书发布者和所有者
映像的散列值和对应的算法名称
证书指纹和指纹算法的名称
ELAM驱动程序密钥获取到映像的基质,就无法访问硬件驱动器上的二进制映像文件,因为存储设备驱动程序栈尚未初始化(系统尚未完全启动)。它只能基于映像的散列值和证书来决定要加载的驱动程序,而不能观察映像本身。因此,在这个阶段对驱动程序的保护不是很有效。

ELAM执行策略
Windows根据该注册表项中指定的ELAM策略值HKLM\System\CurrentControlSet\Control\EarlyLaunch\DriverLoadPolicy来决定是否加载已知的恶意驱动或未知驱动。

下面是允许加载的驱动程序类型和对应的ELMA策略值。

策略值名称 策略值 描述
PNP_INITIALIZE_DRIVERS_DEFAUL 0x00 仅加载已知为非恶意的驱动程序
PNP_INITIALIZE_UNKNOWN_DRIVERS 0x01 仅加载已知未非恶意和未知的驱动程序
PNP_INITIALIZE_BAD_CRITICAL_DRIVERS 0x03 加载已知未非恶意、未知的和已知为恶意的关键驱动程序(这是默认设置)
PNP_INITIALIZE_BAD_DRIVERS 0x07 加载所有驱动程序
可以看到,默认的ELAM策略PNP_INITIALIZE_BAD_CRITICAL_DRIVERS允许加载恶意的关键驱动程序。这意味着,如果一个关键驱动程序被ELAM归类为已知的恶意驱动程序,系统也会加载它。这种策略背后的基本原理是,关键系统驱动程序是操作系统的重要组成部分,因此任何初始化失败都会导致操作系统无法启动。也就是说,除非成功加载并初始化了所有关键请重新,否则系统不会启动。因此,此ELMA策略会损害某些安全性,以提高可用性和可维护性。

但是,这个策略不会加载已知为恶意的非关键驱动程序,或者那些非必要的驱动程序。这就是PNP_INITIALIZE_BAD_CRITICAL_DRIVERS和PNP_INITIALIZE_BAD_DRIVERS策略之间的主要区别:后者允许加载所有驱动程序,包括已知为恶意的非关键驱动程序。

BOOTKIT绕过
ELAM可以有效的抵御Rootkit的威胁,但在低于Bootkit方面却没有什么用(它并非为Bootkit设计的)。

ELAM只能监视合法加载的驱动程序,但是大多数Bootkit会使用未注册的操作系统功能来加载内核模式驱动程序。这意味着尽管有ELAM,Bootkit也可以绕过安全性强制措施并将其代码注入内核地址空间。此外,Bootkit的恶意代码可以在初始化操作系统内核和加载任何内核模式驱动程序(包括ELAM)之前允许。这意味着它可以避开ELAM保护。

在这里插入图片描述
在所有的操作系统子系统(I/O子系统、对象管理器、即插即用管理器等)被初始化之后,以及执行ELAM之前,大多数Bootkit都会在内核初始化期间加载它们的内核模式代码。

简单的ELAM驱动
#include <ntddk.h>

// 上下文结构体
typedef struct _ELAM_CONTEXT {
BOOLEAN AllowUnknownDrivers; // 策略:是否允许未知驱动
} ELAM_CONTEXT, *PELAM_CONTEXT;

// 回调函数原型
VOID ElamBootDriverCallback(PVOID CallbackContext, PVOID Argument1, PVOID Argument2);

// 上下文全局变量
ELAM_CONTEXT g_ElamContext = { TRUE }; // 默认允许未知驱动

// DriverEntry - 驱动入口点
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
NTSTATUS status;

// 注册 ELAM 回调函数
status = IoRegisterBootDriverCallback(ElamBootDriverCallback, &g_ElamContext);
if (!NT_SUCCESS(status)) {
    KdPrint(("Failed to register ELAM callback. Status: 0x%x\n", status));
    return status;
}

KdPrint(("ELAM driver loaded successfully.\n"));

// 设置驱动卸载函数
DriverObject->DriverUnload = [](PDRIVER_OBJECT DriverObject) {
    // 注销回调
    IoUnRegisterBootDriverCallback(ElamBootDriverCallback);
    KdPrint(("ELAM driver unloaded.\n"));
};

return STATUS_SUCCESS;

}

// ELAM 回调函数
VOID ElamBootDriverCallback(PVOID CallbackContext, PVOID Argument1, PVOID Argument2) {
PELAM_CONTEXT pContext = (PELAM_CONTEXT)CallbackContext;

// 检查回调类型
if ((ULONG_PTR)Argument1 == BdCbInitializeImage) {
    // 获取驱动信息
    PBOOT_DRIVER_INFO BootDriverInfo = (PBOOT_DRIVER_INFO)Argument2;

    // 检查驱动路径
    if (wcsstr(BootDriverInfo->FilePath.Buffer, L"TrustedDriver.sys")) {
        BootDriverInfo->Classification = BootDriverGood; // 可信驱动
        KdPrint(("Driver %wZ classified as Good.\n", &BootDriverInfo->FilePath));
    } else if (pContext->AllowUnknownDrivers) {
        BootDriverInfo->Classification = BootDriverUnknown; // 未知驱动
        KdPrint(("Driver %wZ classified as Unknown.\n", &BootDriverInfo->FilePath));
    } else {
        BootDriverInfo->Classification = BootDriverBad; // 阻止加载
        KdPrint(("Driver %wZ classified as Bad.\n", &BootDriverInfo->FilePath));
    }
}

}

<think>我们正在讨论Windows内核结构和工作原理。根据用户的问题和提供的引用,我们需要组织一个关于Windows内核的详细回答。用户提供的引用[1]、[3]、[4]都与Windows内核相关,而引用[2]是关于Linux的,所以我们可以忽略引用[2]。 根据引用[1],Windows内核分为三层:HAL层、内核层(微内核)和执行体层。其中,HAL层负责硬件抽象,内核层提供操作系统原语(如线程、进程、调度、中断处理等),执行体层提供可供应用程序和驱动程序调用的功能。执行体层和内核层都在同一个模块ntoskrnl.exe中。 引用[3]说明了用户模式代码如何通过系统DLL(特别是ntdll.dll)调用内核服务。ntdll.dll中的存根函数(以Nt为前缀)是用户模式和内核模式之间的桥梁。 引用[4]主要讨论了内存管理,包括分页机制、页面文件、工作集管理和内存保护机制等。 因此,我们可以将回答分为以下几个部分: 1. Windows内核整体结构(三层架构) 2. 用户模式与内核模式的交互(系统服务调用) 3. 内存管理(分页、工作集、保护机制等) 另外,我们还需要生成相关问题。 注意:按照要求,所有行内数学表达式用$...$,独立公式用$$...$$,并且用中文回答。同时,在引用处添加引用标识。 结构安排: - 首先概述Windows内核的总体结构 - 然后详细说明每一层的功能 - 接着介绍系统服务调用机制 - 再介绍内存管理 - 最后生成相关问题 由于引用[4]内容较多,我们重点提取与内存管理相关的部分进行介绍。 下面开始组织回答。</think>### Windows 内核结构和工作原理详解 Windows 内核采用分层设计,核心模块 **`ntoskrnl.exe`** 包含硬件抽象层(HAL)、内核层和执行体层,协同实现进程管理、内存调度和硬件交互。以下是关键结构和工作机制: --- #### 一、内核分层架构 1. **硬件抽象层(HAL)** - 隔离硬件差异,为上层提供统一接口(如中断控制器、I/O端口访问)[^1]。 - 示例:不同主板的ACPI电源管理通过HAL标准化调用。 2. **内核层(Micro-Kernel)** - 核心操作系统原语: - 线程/进程调度(基于优先级抢占式) - 中断和异常处理(如缺页异常) - 同步机制(自旋锁、信号量)[^1]。 3. **执行体层(Executive)** - 提供系统服务接口: - **对象管理器**:统一管理内核对象(进程、文件、注册表键) - **内存管理器**:虚拟内存分页、工作集调整(见第四部分) - **I/O管理器**:设备驱动通信框架[^1]。 > 执行体与内核层编译为单一模块 **`ntoskrnl.exe`**,确保高效协作[^1]。 --- #### 二、用户模式与内核模式交互 1. **系统服务调用流程**: ```mermaid graph LR A[应用程序] --> B[Win32 API] B --> C[ntdll.dll存根函数] C -- NtCreateProcess等 --> D[内核模式系统服务] ``` - **`ntdll.dll`** 桥接用户态与内核态,提供前缀为 **`Nt*`** 的存根函数(如 `NtCreateProcess`)[^3]。 - 调用时触发 **`syscall`** 指令,切换至内核模式执行对应服务。 2. **关键支持模块**: - 映像加载器(`Ldr*`) - 调试支持(`Dbg*`) - 运行时函数(`Rtl*`)[^3]。 --- #### 三、内存管理机制 1. **虚拟内存分页** - **页错误处理流程**: ```mermaid graph TB A[进程访问虚拟地址] --> B{MMU触发页错误?} B -->|是| C[内存管理器检查有效性] C --> D[从磁盘pagefile.sys加载页] D --> E[更新页表并恢复执行] ``` - 页面文件 **`pagefile.sys`** 默认大小为物理内存的1.5-3倍[^4]。 2. **工作集管理** | 工作集类型 | 描述 | |------------------|--------------------------| | 进程工作集 | 进程私有物理页集合 | | 系统工作集 | 内核/驱动使用的页 | | 会话工作集 | 会话空间(如用户登录)页 | - **平衡集管理器** 基于 **LRU算法** 定期修剪工作集,根据内存压力动态扩展/收缩[^4]。 3. **内存保护机制** - **页面属性控制**: - `PAGE_READONLY`(只读) - `PAGE_EXECUTE`(可执行) - `PAGE_GUARD`(防护页,触发访问异常)[^4]。 - **地址空间随机化(ASLR)**:随机化模块加载基址、堆栈/堆基址,防范漏洞攻击[^4]。 --- #### 四、同步与中断处理 1. **中断优先级(IRQL)** - 分层处理机制(0~31级),高优先级中断可抢占低优先级任务。 - 示例:时钟中断 > 设备I/O中断 > 线程调度中断。 2. **同步对象** - 互斥体(Mutex)、事件(Event)、信号量(Semaphore)由执行体层统一管理。 - 避免优先级反转:通过 **优先级继承协议** 调整等待线程优先级[^1]。 --- ### 总结:Windows内核核心特性 | 模块 | 功能 | 关键组件 | |-----------------|----------------------------------------------------------------------|------------------------------| | HAL层 | 硬件差异抽象 | 标准硬件接口 | | 内核层 | 线程调度/中断处理/同步 | 微内核原语 | | 执行体层 | 系统服务/对象管理/内存管理 | 对象管理器、内存管理器 | | 用户-内核桥接 | 系统服务调用 | ntdll.dll(Nt*函数) | | 内存管理 | 虚拟分页/工作集优化/ASLR | 平衡集管理器、pagefile.sys | > **提示**:内核模块 `ntoskrnl.exe` 的完整性直接影响系统稳定性,恶意驱动可能通过修改其代码实现Rootkit攻击。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值