0. 前置环境
更基础
- WIndows10 Vmware虚拟机
- Visual Studio 2019,有WDK
- Windbg Preview(我用单纯是觉得更好看)
- 最重要的HEVD项目:https://github.com/hacksysteam/HackSysExtremeVulnerableDriver
我直接下载的3.00 Release版
我使用的Windows版本是
I. 编程环境
如果你想快速搭建一个驱动开发环境可以参考B站上的一些资料,如:配置驱动开发环境
如果按照步骤vs没有KernelModDriver
这一模板,找到vs目录的WDK.vsix
双击即可
一段驱动的主要代码,在main.cpp
中编写
#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;
}
规定了驱动加载和卸载的两个函数,并在加载和卸载时打印调试信息
II. 调试环境
传统串口调试
添加串口
在虚拟机中Win+R召唤msconfig
,打开允许串口调试
然后在(记得用管理员打开)windbg中按照图中配置即可,其他选项不变
一直找不到管道的话可以点Break再等会儿就有了
VirtualKD法调试
项目链接:http://virtualkd.sysprogs.org/
安装后,vmmon64.exe
就行了
但是用了就不能用串口调试了
打开信息显示+DbgView
在\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\
注册表中添加Debug Print Filter
,并设置一个Default
的DWORD
值,你可以将其设置为0x8
或者允许更多调试信息的0xf
虽然说windbg确实能打印出DbgPrint
,但是HEVD使用的是DbgPrintEx
,接受不到,安装了DbgView后,他会把调试信息打印出了并且windbg也能收到
调试指令
如果出现了以下情况且虚拟机卡顿,可以使用这两个指令关闭输出
1: kd> ed nt!Kd_SXS_Mask 0
1: kd> ed nt!Kd_FUSION_Mask 0
关闭这两个函数的输出,莫名其妙变卡的话再用一下
III. 驱动加载
使用KmdManager.exe,毛子的黑科技,使用时需要管理员启动
也可以使用osLoader啥的
这里用KmdKit的KmdManager演示,运行HEVD
那么就算搭建成功
A. 编程基础
关于内核模式驱动程序:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/kernel/
内核中交互是通过IRP
请求进行交互的
IRP 结构是表示 I/O 请求数据包的部分不透明结构。 IRP 结构的未记录成员是保留的,仅由 I/O 管理器使用,在某些情况下,由文件系统驱动程序 (FSD) 使用。
MSDN:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/ddi/wdm/ns-wdm-_irp
使用到的内存堆栈为I/O Stack
I/O 管理器为分层驱动程序链中的每个驱动程序提供其设置的每个 IRP 的 I/O 堆栈位置。 每个 I/O 堆栈位置都包含 一个IO_STACK_LOCATION结构。
MSDN:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/kernel/i-o-stack-locations
IRP通过iostack发送给设备,对应的是应用层的“消息”。设备可以存在(硬盘等),也可以不存在(QQ Protect驱动等)。
根据上图,应用层通过和设备对象(FDO)进行交互,设备(PDO)再和设备对象交互,实现交互。同时FDO向PDO的交互不是必要的。
HAL 硬件抽象层
HAL通常是一个独立的动态链接库,windows自身携带多种HAL,但是在系统安装的时候只会选择一种,名为
hal.dll
。涉及中断控制器、单处理器/多处理器硬件断点。
B. 代码
创建设备
MSDN:https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/device-tree
使用DeviceTree,可以找到:https://web.archive.org/web/20200519214156/http://www.osronline.com/OsrDown.cfm/devicetree_v230.zip
UNICODE_STRING DeviceName = { 0 }; //设备名
PDEVICE_OBJECT pDevice = NULL; //设备对象
RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
Status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevice);
if (!NT_SUCCESS(Status)) {
DbgPrint("Create Device Failed: %x\n", Status);
return Status;
}
创建符号链接
符号链接就是类似与Z:\
之前的前缀
使用WinObj
可以看到,我用的再第一章中下载的KdmKit
中的SymLinks
(太老了,建议换一个)
UNICODE_STRING SymLink = { 0 };
RtlInitUnicodeString(&SymLink, SYM_NAME);
Status = IoCreateSymbolicLink(&SymLink, &DeviceName);
if (!NT_SUCCESS(Status)) {
DbgPrint("Create Symbol Link Failed: %x\n", Status);
IoDeleteDevice(pDevice);
return Status;
}
关联功能的交互
I. 创建“句柄”
具体调试方法也在上面的文档中
每个内核模式驱动程序都必须实现名为 DriverEntry 的函数,该函数在加载驱动程序之后会立即得到调用。 DriverEntry 函数使用指向驱动程序实现的一些其他函数的指针来填充 DRIVER_OBJECT 结构的某些成员。 例如,DriverEntry 函数使用指向驱动程序的 Unload 函数的指针来填充 DRIVER_OBJECT 结构的 Unload 成员
DriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreate;
根据上面的文档,创建的函数和主函数Entry
差不多,这里用的是设备对象,不是驱动对象
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;
}
II. 关闭“句柄”
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;
}
NTSTATUS
MyClean(PDEVICE_OBJECT pdevice, PIRP pIrp) {
NTSTATUS RET = STATUS_SUCCESS;
DbgPrint("My Device Has Clean\n");
pIrp->IoStatus.Status = RET;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return RET;
}
成功加载
III. 在Ring3进行交互
#include <iostream>
#include <windows.h>
int main()
{
HANDLE hDevice = NULL;
hDevice = CreateFileW(L"\\\\.\\My1DeviceLinker", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice== INVALID_HANDLE_VALUE) {
std::cout << "Error Create File\n";
system("pause");
return 0;
}
std::cout << "Success open\n";
system("pause");
CloseHandle(hDevice);
std::cout << "Success close\n";
system("pause");
return 0;
}
C. 二阶段
MSDN:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/ddi/wdm/nf-wdm-iocreatedevice
关于创建设备的原型
NTSTATUS IoCreateDevice(
[in] PDRIVER_OBJECT DriverObject,
[in] ULONG DeviceExtensionSize,
[in, optional] PUNICODE_STRING DeviceName,
[in] DEVICE_TYPE DeviceType,
[in] ULONG DeviceCharacteristics,
[in] BOOLEAN Exclusive,
[out] PDEVICE_OBJECT *DeviceObject
);
[in] DeviceExtensionSize
指定要为 设备对象的设备扩展 分配的驱动程序确定的字节数。 设备扩展的内部结构是驱动程序定义的。
- 维护设备状态信息。
- 为驱动程序使用的任何内核定义对象或其他系统资源(如旋转锁)提供存储。
- 保存驱动程序必须在系统空间中驻留的任何数据,以执行其 I/O 操作。
那么这就是一段描述要传输的数据的空间的大小的值。
从驱动中读取
驱动MyRead
函数和DriverEntry
NTSTATUS
MyRead(PDEVICE_OBJECT pdevice, PIRP pIrp) {
UNREFERENCED_PARAMETER(pdevice);
NTSTATUS RET = STATUS_SUCCESS;
DbgPrint("My Device Has Read\n");
PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
ULONG ReadSize = pStack->Parameters.Read.Length;
PCHAR Buffer = pIrp->AssociatedIrp.SystemBuffer;
DbgPrint("Ring3 Want Read %x\n", ReadSize);
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;
}
R3
CHAR Test[0x40] = { 0 };
DWORD lpRead = 0;
ReadFile(hDevice, Test, 30, &lpRead, NULL);
printf("%p -%s--%d\n", Test, Test,lpRead);
-
30
是要读取的字节 -
lpRead
是真实读取的字节
每向下传递一层需要一个设备栈(可以试着从自己设计这样一个模式的角度想想)
SystemBuffer
和pIrp->MdlAddress
是同一块物理地址的两个不同虚拟地址(不同的映射)。
需要设置读写方式
pDevice->Flags |= DO_BUFFERED_IO;
//设备创建成功,绑定符号链接
向驱动中写入
R3
WriteFile(hDevice, "This is From Ring3.", strlen("This is From Ring3."), &lpRead, NULL);
驱动
NTSTATUS
MyWrite(PDEVICE_OBJECT pdevice, PIRP pIrp) {
UNREFERENCED_PARAMETER(pdevice);
NTSTATUS RET = STATUS_SUCCESS;
DbgPrint("My Device Has Wrtitten\n");
PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
ULONG ReadSize = pStack->Parameters.Write.Length;
PCHAR Buffer = pIrp->AssociatedIrp.SystemBuffer;
DbgPrint("Ring3 Write Read %x\n", ReadSize);
RtlZeroMemory(pdevice->DeviceExtension, 200);
RtlCopyMemory(pdevice->DeviceExtension, Buffer, ReadSize);
DbgPrint("--%p-%s\n", Buffer, (PCHAR)pdevice->DeviceExtension);
pIrp->IoStatus.Status = RET;
pIrp->IoStatus.Information = strlen("Message From Driver");
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return RET;
}
[IOCTL]自定义控制IO
IRP_MJ_DEVICE_CONTROL
,定义IOCTL操作,很多内核的交互大多都是依靠此方式
这里程序接收一个数字返回值x2
驱动
定义操作标识
MSDN:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/ddi/d4drvif/nf-d4drvif-ctl_code
#define IOCTL_MUL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x9888, METHOD_BUFFERED, FILE_ANY_ACCESS)
- 0x9888:标识符号
编写函数
NTSTATUS
MyControl(PDEVICE_OBJECT pdevice, PIRP pIrp) {
UNREFERENCED_PARAMETER(pdevice);
NTSTATUS RET = STATUS_SUCCESS;
DbgPrint("My Device Has IOCTL\n");
PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
ULONG ioCode = pStack->Parameters.DeviceIoControl.IoControlCode; //功能码
ULONG inLen = pStack->Parameters.DeviceIoControl.InputBufferLength; //出入长度
//ULONG outLen = pStack->Parameters.DeviceIoControl.OutputBufferLength; //输出长度
ULONG ioInfo = 0;
switch (ioCode)
{
case IOCTL_MUL:
{
DWORDLONG inData = *(PDWORDLONG)pIrp->AssociatedIrp.SystemBuffer; //取出传输的数据
DbgPrint("Kernel Recive: %d, Len: %lld\n", inData, inLen);
inData *= 2;
DbgPrint("Kernel Data %d\n", inData);
*(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;
}
一些关键函数
DriverEntry
NTSTATUS
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
NTSTATUS Status = STATUS_SUCCESS;
DbgPrint("Driver Running -> %wZ\n", &DriverObject->DriverName);
DriverObject->DriverUnload = DriverUnload;
//创建设备
UNICODE_STRING DeviceName = { 0 }; //设备名
PDEVICE_OBJECT pDevice = NULL; //设备对象
RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
Status = IoCreateDevice(DriverObject, 0x200, &DeviceName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevice);
if (!NT_SUCCESS(Status)) {
DbgPrint("Create Device Failed: %x\n", Status);
return Status;
}
pDevice->Flags |= DO_BUFFERED_IO;
//设备创建成功,绑定符号链接
UNICODE_STRING SymLink = { 0 };
RtlInitUnicodeString(&SymLink, SYM_NAME);
Status = IoCreateSymbolicLink(&SymLink, &DeviceName);
if (!NT_SUCCESS(Status)) {
DbgPrint("Create Symbol Link Failed: %x\n", Status);
IoDeleteDevice(pDevice);
return Status;
}
DbgPrint("Device & Symbolic Link Created\n");
DriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = MyClose;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MyClean;
DriverObject->MajorFunction[IRP_MJ_READ] = MyRead;
DriverObject->MajorFunction[IRP_MJ_WRITE] = MyWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyControl;
DbgPrint("Function Settal Done\n");
return Status;
}
DriverUnload
VOID
DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
DbgPrint("Driver Stopping -> %wZ\n", &DriverObject->DriverName);
DbgPrint("Device Stopping\n");
if (DriverObject->DeviceObject) {
IoDeleteDevice(DriverObject->DeviceObject);
UNICODE_STRING symname = { 0 };
RtlInitUnicodeString(&symname, SYM_NAME);
IoDeleteSymbolicLink(&symname);
}
}
用于在Ring3交互的项目的主函数
// R3Control.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <windows.h>
#define IOCTL_MUL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x9888, METHOD_BUFFERED, FILE_ANY_ACCESS)
int main()
{
HANDLE hDevice = NULL;
hDevice = CreateFileW(L"\\\\.\\My1DeviceLinker", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice== INVALID_HANDLE_VALUE) {
std::cout << "Error Create File\n";
return 0;
}
std::cout << "Success open\n";
system("pause");
std::cout << "now IOCTL\n";
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);
CloseHandle(hDevice);
std::cout << "Success close\n";
return 0;
}
参考
https://www.bilibili.com/video/BV1QJ411A7kR
https://space.bilibili.com/1992190180/
https://learn.microsoft.com/zh-cn/windows-hardware/drivers/kernel/