从零探索现代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
驱动开发环境配置
- 安装VS2019和WDK
- 如果VS没有KernelModDriver模板,找到VS目录中的WDK.vsix安装
- 基本驱动代码结构:
#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;
}
调试环境配置
传统串口调试方法
- 虚拟机添加串口设备
- 在虚拟机中运行
msconfig启用串口调试 - Windbg配置串口管道连接
VirtualKD调试方法
- 下载VirtualKD项目
- 运行
vmmon64.exe - 注意:使用VirtualKD后无法同时使用串口调试
调试信息显示配置
- 修改注册表:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\ 添加Debug Print Filter项,设置Default值为0x8或0xf - 使用DbgView工具查看
DbgPrintEx输出
调试指令
如果出现输出过多导致卡顿:
kd> ed nt!Kd_SXS_Mask 0
kd> ed nt!Kd_FUSION_Mask 0
驱动加载工具
- 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
设备与符号链接创建
// 创建设备
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);
栈溢出漏洞基础
漏洞原理
当驱动程序处理用户输入时,如果未正确验证输入数据的长度,可能导致栈缓冲区溢出,覆盖返回地址等关键数据。
利用步骤
- 识别存在漏洞的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/