windows内核研究(驱动开发-0环与3环的通信)

驱动开发


0环与3环的通信

设备对象

在之前开发窗口程序时,消息都是被封装成一个结构体(MSG),在内核开发时,消息被封装成另外一个结构体:IRP(I/O Request Package)

在窗口程序中,能够接收消息的只能是窗口对象,在内核中,能够接收IRP消息的只能是设备对象

创建设备对象

c 复制代码
// 创建设备名称
UNICODE_STRING deviceName;
RtlInitUnicodeString(&deviceName, L"\\Device\\MyDevice");

/*
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			// 指向接收指向新创建的 DEVICE_OBJECT 结构的指针的变量的指针
);
*/

// 创建设备对象
NTSTATUS ntIoCreate = IoCreateDevice(driverObject, 0, &deviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObject);

数据的交互方式

c 复制代码
// 设置数据的交互方式
driverObject->Flags |= DO_BUFFERED_IO;
  • 缓存区方式读写(DO_BUFFERED_IO):操作系统将应用程序提供缓冲区的数据复制到内核模式下的地址中
  • 直接方式读写(DO_DIRECT_IO):操作系统会将用户模式下的缓冲区锁住,然后操作系统将这段缓冲区在内核模式地址再次映射一遍,这样,用户模式的缓冲区和内核模式的缓冲区指向的是同一区域的物理内存(缺点就是要单独占用物理页面)
  • 其他方式读写(在调用IoCreateDevice创建设备后对drivceObjcet->Flags既不设置DO_BUFFERED_IO也不设置DO_DIRECT_IO此时就是其它方式):在使用其他方式读写设备时,派遣函数直接读写应用程序提供的缓冲地址。在驱动程序中,直接操作应用程序的缓冲区是很危险的,只有驱动程序与应用程序运行在相同线程上下文的情况下,才能使用这种方式

IRP的类型

当应用层通过CreateFile,ReadFile,WriteFile,CloseHandle等函数打开,从设备读取数据,向设备写入数据,关闭设备的时候,会使操作系统系统产出不同的IRP_CREATE,IPR_MJ_READ,IRP_MJ_WRITE等IRP

其它类型

IRP类型 来源
IRP_MJ_DEVICE_CONTROL DeviceControl函数会产生些IRP
IRP_MJ_POWER 在操作系统处理电源消息时,产生此IRP
IRP_MJ_SHUTDOWN 关闭系统前会产生些IRP

IRP_MJ_DEVICE_CONTROL才是我们Ring3与驱动交互的常规方式

测试代码

3环应用程序代码

cpp 复制代码
#include<iostream>
#include<windows.h>

#define IN_BUFFER_MAXLENGIT			0x10			// 输入缓存最大长度
#define OUT_BUFFER_MAXLENGTH		0x10			// 输出缓存最大长度
// 参数说明:1,设备类型,2,操作码,3,以什么方式进行访问,4,访问权限
#define OPER1 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) 
#define OPER2 CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define SYMBOLICLINK_NAME "\\\\.\\MyTestDriver"

HANDLE g_hDriver;

BOOL open(const CHAR* pLinkName) {
	TCHAR szBuffer[10] = { 0 };
	g_hDriver = CreateFileA(pLinkName,GENERIC_READ|GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
	DWORD errorCode = GetLastError();
	swprintf(szBuffer,sizeof(szBuffer),L"%d:\n",errorCode);
	if (g_hDriver != INVALID_HANDLE_VALUE)
		return TRUE;
	else 
		return FALSE;
}

/*
BOOL DeviceIoControl(
	[in]                HANDLE       hDevice,			设备句柄
	[in]                DWORD        dwIoControlCode,	控制代码
	[in, optional]      LPVOID       lpInBuffer,		向0环传递的缓冲区的地址
	[in]                DWORD        nInBufferSize,		缓冲区的大小
	[out, optional]     LPVOID       lpOutBuffer,		向3还传递的缓冲区的地址
	[in]                DWORD        nOutBufferSize,	缓冲区的大小
	[out, optional]     LPDWORD      lpBytesReturned,	实际返回的长度(字节数)
	[in, out, optional] LPOVERLAPPED lpOverlapped		
);
*/


BOOL ioControl(DWORD dwIoCode,PVOID InBuffer,DWORD InBuffLen,PVOID OutBuff,DWORD OutBuffLen) { 
	DWORD dw;
	DeviceIoControl(g_hDriver,dwIoCode,InBuffer,InBuffLen,OutBuff,OutBuffLen,&dw,NULL);
	return TRUE;
}

int main() {

	DWORD dwInBuffer = 0x11112222;
	TCHAR szOutBuffer[OUT_BUFFER_MAXLENGTH] = { 0 };

	// 通过符号链接 打开设备
	if (open(SYMBOLICLINK_NAME)) {
        printf("open device success\n");
	}
	else {
        printf("open device failed\n");
	}
	ioControl(OPER2, &dwInBuffer, IN_BUFFER_MAXLENGIT, szOutBuffer, OUT_BUFFER_MAXLENGTH);
	CloseHandle(g_hDriver);
	system("pause");
	return 0;
} 

0环驱动程序

c 复制代码
#include <ntddk.h>


#define DEVICE_NAME L"\\Device\\MyDevice"
// Ring3用CreateDevice打开设备时用 \\\\.\\MyTestDriver
#define SYMBOLICLINK_NAME L"\\??\\MyTestDriver"


#define OPER1 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define OPER2 CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)

// IRP_MJ_CREATE 处理函数
NTSTATUS IrpCreateProc(PDEVICE_OBJECT pDevObj,PIRP pIrp) {

	DbgPrint("设备创建了...");

	// 设置返回状态
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	return STATUS_SUCCESS;
}

// IRP_MJ_CLOSE 处理函数
NTSTATUS IrpCloseProc(PDEVICE_OBJECT pDevObj, PIRP pIrp) {
	DbgPrint("设备关闭了...");

	// 设置返回状态
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	return STATUS_SUCCESS;
}




// IRP_MJ_DEVICE_CONTROL 处理函数
NTSTATUS IrpDeviceControlProc(PDEVICE_OBJECT pDevObj, PIRP pIrp) {
	NTSTATUS stauts = STATUS_INVALID_DEVICE_REQUEST;

	PIO_STACK_LOCATION pIrpStack;
	
	ULONG uIoControlCode;
	PVOID pIoBuffer;
	ULONG uInLength;
    ULONG uOutLength;
	ULONG uRead;
	ULONG uWrite;

	// 设置临时变量的值
	uRead = 0;
	uWrite = 0x12345678;

	//获取IRP数据
	pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
	//获取控制码
    uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
	// 获取缓冲区地址(输入和输出的缓冲区都是一个)
	pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
	// Ring3 发送数据的长度
	uInLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
	// Ring0 发送数据的长度
	uOutLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;

	pIrp->IoStatus.Information = 0;

	switch (uIoControlCode) {
		case OPER1:
		{
			DbgPrint("调用IRP_MJ_DEVICE_CONTROL ---> 1");
			// 设置返回状态
			pIrp->IoStatus.Information = 1;
			stauts = STATUS_SUCCESS;
			break;
		}
		case OPER2:
		{
			DbgPrint("调用IRP_MJ_DEVICE_CONTROL ---> 2 接收字节数:%d\n", uInLength);
			DbgPrint("调用IRP_MJ_DEVICE_CONTROL ---> 2 输出字节数:%d\n", uOutLength);
			// 读缓存
			memcpy(&uRead,pIoBuffer,4);
			DbgPrint("调用IRP_MJ_DEVICE_CONTROL ---> 2 地址:%llx",uRead);
			// 写缓存
			memcpy(pIoBuffer,&uWrite,4);

			// 设置返回状态
			pIrp->IoStatus.Information = 2;
			stauts = STATUS_SUCCESS;
			break;
            
		}
	
	}

		// 设置返回状态
	pIrp->IoStatus.Status = stauts;
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	return STATUS_SUCCESS;

}


// 卸载函数
VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
	DbgPrint("驱动被卸载了\n");
}




NTSTATUS DriverEntry(PDRIVER_OBJECT driverObject, PUNICODE_STRING reg_path) {

	NTSTATUS ntIoCreate = 0;

	PDEVICE_OBJECT pDeviceObject = NULL;
	UNICODE_STRING deviceName;
	UNICODE_STRING symbolicLinkName;

	// 创建设备名称
	RtlInitUnicodeString(&deviceName, L"\\Device\\MyDevice");

	// 创建设备对象
	ntIoCreate = IoCreateDevice(driverObject, 0, &deviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObject);
	if (ntIoCreate != STATUS_SUCCESS) {
		DbgPrint("设备对象创建失败!\n");
		return ntIoCreate;
	}

	// 设置数据的交互方式
	driverObject->Flags |= DO_BUFFERED_IO;
	
	// 创建符号链接名称(给Ring3访问)
	// Ring3用CreateFile打开设备时用 \\\\.\\MyTestDriver
	RtlInitUnicodeString(&symbolicLinkName, L"\\??\\MyTestDriver");

	// 创建符号链接
	ntIoCreate = IoCreateSymbolicLink(&symbolicLinkName, &deviceName);
	if(ntIoCreate != STATUS_SUCCESS) {
		DbgPrint("符号链接创建失败!\n");	
		IoDeleteDevice(pDeviceObject);
		return ntIoCreate;
	}

	// 设置派遣函数和卸载函数
	driverObject->MajorFunction[IRP_MJ_CREATE] = IrpCreateProc;
	driverObject->MajorFunction[IRP_MJ_CLOSE] = IrpCloseProc;
	driverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDeviceControlProc;
	driverObject->DriverUnload = DriverUnload;

	return STATUS_SUCCESS;
}

演示效果

相关推荐
cxr8288 天前
SPARC方法论在Claude Code基于规则驱动开发中的应用
人工智能·驱动开发·claude·智能体
sukalot9 天前
window显示驱动开发—显示适配器的子设备
驱动开发
Evan_ZGYF丶9 天前
【RK3576】【Android14】如何在Android14下单独编译kernel-6.1?
linux·驱动开发·android14·rk3576
sukalot10 天前
window显示驱动开发—视频呈现网络简介
驱动开发
sukalot10 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(二)
驱动开发
zwhSunday10 天前
Linux驱动开发(1)概念、环境与代码框架
linux·运维·驱动开发
sukalot11 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(三)
驱动开发
sukalot11 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(一)
驱动开发
cxr82812 天前
基于Claude Code的 规范驱动开发(SDD)指南
人工智能·hive·驱动开发·敏捷流程·智能体
zwhSunday13 天前
Linux驱动开发(2)进一步理解驱动
linux·驱动开发