从零探索现代windows内核栈溢出-以HEVD练习为例(上)
字数 1355 2025-08-23 18:31:17

Windows内核驱动开发与栈溢出漏洞利用基础教程

环境搭建

前置环境要求

  • Windows10系统
  • Vmware虚拟机
  • Visual Studio 2019 + WDK (Windows Driver Kit)
  • Windbg Preview (调试工具)
  • HEVD项目 (HackSys Extreme Vulnerable Driver) v3.00

驱动开发环境配置

  1. 安装VS2019和WDK
  2. 如果VS没有KernelModDriver模板,找到VS目录中的WDK.vsix安装
  3. 基本驱动代码结构:
#include <ntifs.h>
#include "win10.h"
#include "x64.h"

VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
    UNREFERENCED_PARAMETER(DriverObject);
    DbgPrint("Driver Stopping -> %wZ\n", &DriverObject->DriverName);
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    UNREFERENCED_PARAMETER(RegistryPath);
    DbgPrint("Driver Running -> %wZ\n", &DriverObject->DriverName);
    DriverObject->DriverUnload = DriverUnload;
    return STATUS_SUCCESS;
}

调试环境配置

传统串口调试方法

  1. 虚拟机添加串口设备
  2. 在虚拟机中运行msconfig启用串口调试
  3. Windbg配置串口管道连接

VirtualKD调试方法

  1. 下载VirtualKD项目
  2. 运行vmmon64.exe
  3. 注意:使用VirtualKD后无法同时使用串口调试

调试信息显示配置

  1. 修改注册表:
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\
    添加Debug Print Filter项,设置Default值为0x8或0xf
    
  2. 使用DbgView工具查看DbgPrintEx输出

调试指令

如果出现输出过多导致卡顿:

kd> ed nt!Kd_SXS_Mask 0
kd> ed nt!Kd_FUSION_Mask 0

驱动加载工具

  • KmdManager.exe (需管理员权限)
  • osLoader等工具

内核驱动编程基础

内核驱动核心概念

  1. IRP (I/O Request Packet): 内核中进程间通信的基本结构
    • 文档:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/ddi/wdm/ns-wdm-_irp
  2. I/O Stack: I/O管理器为每个驱动程序提供的堆栈位置
    • 包含IO_STACK_LOCATION结构
    • 文档:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/kernel/i-o-stack-locations
  3. HAL (硬件抽象层): 处理硬件相关操作的独立DLL

设备与符号链接创建

// 创建设备
UNICODE_STRING DeviceName = {0};
PDEVICE_OBJECT pDevice = NULL;
RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
Status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevice);

// 创建符号链接
UNICODE_STRING SymLink = {0};
RtlInitUnicodeString(&SymLink, SYM_NAME);
Status = IoCreateSymbolicLink(&SymLink, &DeviceName);

驱动功能实现

1. 创建设备句柄

DriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreate;

NTSTATUS MyCreate(PDEVICE_OBJECT pdevice, PIRP pIrp) {
    NTSTATUS RET = STATUS_SUCCESS;
    DbgPrint("My Device Has Opened\n");
    pIrp->IoStatus.Status = RET;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return RET;
}

2. 关闭设备句柄

DriverObject->MajorFunction[IRP_MJ_CLOSE] = MyClose;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MyClean;

NTSTATUS MyClose(PDEVICE_OBJECT pdevice, PIRP pIrp) {
    NTSTATUS RET = STATUS_SUCCESS;
    DbgPrint("My Device Has Closed\n");
    pIrp->IoStatus.Status = RET;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return RET;
}

3. Ring3与Ring0交互

用户层代码:

#include <windows.h>

int main() {
    HANDLE hDevice = CreateFileW(L"\\\\.\\My1DeviceLinker", 
        GENERIC_READ | GENERIC_WRITE, 
        0, NULL, OPEN_EXISTING, 
        FILE_ATTRIBUTE_NORMAL, NULL);
    
    if(hDevice == INVALID_HANDLE_VALUE) {
        printf("Error Create File\n");
        return 0;
    }
    
    printf("Success open\n");
    CloseHandle(hDevice);
    printf("Success close\n");
    return 0;
}

数据传输实现

1. 从驱动读取数据

驱动层:

DriverObject->MajorFunction[IRP_MJ_READ] = MyRead;

NTSTATUS MyRead(PDEVICE_OBJECT pdevice, PIRP pIrp) {
    NTSTATUS RET = STATUS_SUCCESS;
    PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
    ULONG ReadSize = pStack->Parameters.Read.Length;
    PCHAR Buffer = pIrp->AssociatedIrp.SystemBuffer;
    
    RtlCopyMemory(Buffer, "Message From Driver", strlen("Message From Driver"));
    
    pIrp->IoStatus.Status = RET;
    pIrp->IoStatus.Information = strlen("Message From Driver");
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return RET;
}

用户层:

CHAR Test[0x40] = {0};
DWORD lpRead = 0;
ReadFile(hDevice, Test, 30, &lpRead, NULL);
printf("%p -%s--%d\n", Test, Test, lpRead);

2. 向驱动写入数据

驱动层:

DriverObject->MajorFunction[IRP_MJ_WRITE] = MyWrite;

NTSTATUS MyWrite(PDEVICE_OBJECT pdevice, PIRP pIrp) {
    NTSTATUS RET = STATUS_SUCCESS;
    PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
    ULONG ReadSize = pStack->Parameters.Write.Length;
    PCHAR Buffer = pIrp->AssociatedIrp.SystemBuffer;
    
    RtlZeroMemory(pdevice->DeviceExtension, 200);
    RtlCopyMemory(pdevice->DeviceExtension, Buffer, ReadSize);
    
    pIrp->IoStatus.Status = RET;
    pIrp->IoStatus.Information = strlen("Message From Driver");
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return RET;
}

用户层:

WriteFile(hDevice, "This is From Ring3.", 
    strlen("This is From Ring3."), &lpRead, NULL);

IOCTL控制实现

1. 定义IOCTL操作

#define IOCTL_MUL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x9888, METHOD_BUFFERED, FILE_ANY_ACCESS)

2. 实现IOCTL处理函数

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyControl;

NTSTATUS MyControl(PDEVICE_OBJECT pdevice, PIRP pIrp) {
    NTSTATUS RET = STATUS_SUCCESS;
    PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
    ULONG ioCode = pStack->Parameters.DeviceIoControl.IoControlCode;
    ULONG inLen = pStack->Parameters.DeviceIoControl.InputBufferLength;
    ULONG ioInfo = 0;
    
    switch(ioCode) {
        case IOCTL_MUL: {
            DWORDLONG inData = *(PDWORDLONG)pIrp->AssociatedIrp.SystemBuffer;
            inData *= 2;
            *(PDWORDLONG)pIrp->AssociatedIrp.SystemBuffer = inData;
            ioInfo = 4;
            break;
        }
        default:
            RET = STATUS_UNSUCCESSFUL;
            ioInfo = 0;
            break;
    }
    
    pIrp->IoStatus.Status = RET;
    pIrp->IoStatus.Information = ioInfo;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return RET;
}

3. 用户层调用

#define IOCTL_MUL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x9888, METHOD_BUFFERED, FILE_ANY_ACCESS)

DWORDLONG a = 64;
DWORDLONG b = 0;
DWORD info = 0;
DeviceIoControl(hDevice, IOCTL_MUL, &a, sizeof(DWORDLONG), 
    &b, sizeof(DWORDLONG), &info, NULL);
printf("value a: %lld, b: %lld\nreal info %d\n", a, b, info);

栈溢出漏洞基础

漏洞原理

当驱动程序处理用户输入时,如果未正确验证输入数据的长度,可能导致栈缓冲区溢出,覆盖返回地址等关键数据。

利用步骤

  1. 识别存在漏洞的IOCTL处理函数
  2. 构造超长输入数据触发溢出
  3. 精确控制溢出数据覆盖返回地址
  4. 跳转到攻击者控制的代码区域

防护措施

  1. 严格验证输入数据长度
  2. 使用安全字符串函数(如RtlStringCbCopy
  3. 启用栈保护机制(如/GS编译选项)

参考资源

  • Microsoft文档:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/kernel/
  • HEVD项目:https://github.com/hacksysteam/HackSysExtremeVulnerableDriver
  • VirtualKD项目:http://virtualkd.sysprogs.org/
Windows内核驱动开发与栈溢出漏洞利用基础教程 环境搭建 前置环境要求 Windows10系统 Vmware虚拟机 Visual Studio 2019 + WDK (Windows Driver Kit) Windbg Preview (调试工具) HEVD项目 (HackSys Extreme Vulnerable Driver) v3.00 驱动开发环境配置 安装VS2019和WDK 如果VS没有KernelModDriver模板,找到VS目录中的WDK.vsix安装 基本驱动代码结构: 调试环境配置 传统串口调试方法 虚拟机添加串口设备 在虚拟机中运行 msconfig 启用串口调试 Windbg配置串口管道连接 VirtualKD调试方法 下载VirtualKD项目 运行 vmmon64.exe 注意:使用VirtualKD后无法同时使用串口调试 调试信息显示配置 修改注册表: 使用DbgView工具查看 DbgPrintEx 输出 调试指令 如果出现输出过多导致卡顿: 驱动加载工具 KmdManager.exe (需管理员权限) osLoader等工具 内核驱动编程基础 内核驱动核心概念 IRP (I/O Request Packet) : 内核中进程间通信的基本结构 文档:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/ddi/wdm/ns-wdm-_ irp I/O Stack : I/O管理器为每个驱动程序提供的堆栈位置 包含 IO_STACK_LOCATION 结构 文档:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/kernel/i-o-stack-locations HAL (硬件抽象层) : 处理硬件相关操作的独立DLL 设备与符号链接创建 驱动功能实现 1. 创建设备句柄 2. 关闭设备句柄 3. Ring3与Ring0交互 用户层代码: 数据传输实现 1. 从驱动读取数据 驱动层: 用户层: 2. 向驱动写入数据 驱动层: 用户层: IOCTL控制实现 1. 定义IOCTL操作 2. 实现IOCTL处理函数 3. 用户层调用 栈溢出漏洞基础 漏洞原理 当驱动程序处理用户输入时,如果未正确验证输入数据的长度,可能导致栈缓冲区溢出,覆盖返回地址等关键数据。 利用步骤 识别存在漏洞的IOCTL处理函数 构造超长输入数据触发溢出 精确控制溢出数据覆盖返回地址 跳转到攻击者控制的代码区域 防护措施 严格验证输入数据长度 使用安全字符串函数(如 RtlStringCbCopy ) 启用栈保护机制(如/GS编译选项) 参考资源 Microsoft文档:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/kernel/ HEVD项目:https://github.com/hacksysteam/HackSysExtremeVulnerableDriver VirtualKD项目:http://virtualkd.sysprogs.org/