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;
}

演示效果

相关推荐
Evan_ZGYF丶2 小时前
【RK3576】Android 14 驱动开发实战指南
驱动开发·android14·rk3576
一个平凡而乐于分享的小比特4 小时前
MS1112驱动开发(iio框架)
驱动开发·ms1112·iio
小嵌同学5 小时前
Linux 内存管理(2):了解内存回收机制
linux·运维·arm开发·驱动开发·嵌入式
S,D1 天前
锁步核,为什么叫锁步核?
驱动开发·单片机·嵌入式硬件·mcu·iso26262·锁步核·车规mcu
帅次2 天前
系统分析师-计算机系统-指令系统&多处理机系统
驱动开发·硬件架构·硬件工程·射频工程·基带工程·材料工程·精益工程
S,D2 天前
MBIST - Memory BIST会对memory进行清零吗?
驱动开发·mcu·memory·功能安全·iso26262·mbist·bist
S,D3 天前
MCU进入低功耗模式前的引脚处理原则和方法 --> 以最小化低功耗电流
驱动开发·stm32·单片机·嵌入式硬件·mcu·standby·低功耗引脚处理
Code季风5 天前
测试驱动开发(TDD)实战:在 Spring 框架实现中践行 “红 - 绿 - 重构“ 循环
java·驱动开发·后端·spring·设计模式·springboot·tdd
闻道且行之6 天前
驱动开发(3)|rk356x驱动GPIO基础应用之点亮led灯
驱动开发