当前位置: 首页 > article >正文

应用层与驱动层通信DeviceIoControl

驱动层与应用层通信是通过DeviceIoControl,

符号定义

#define DEVICE_NAME  L"\\Device\\myDriver"             // Driver Name
#define SYMBOLIC_LINK_NAME  L"\\DosDevices\\myDriver"            // Symbolic Link Name
#define WIN32_LINK_NAME  "\\\\.\\myDriver"                    // Win32 Link Name//
// Device IO Control Codes
//
#define IOCTL_BASE          0x800
#define MY_CTL_CODE(i)        \CTL_CODE                  \(                         \FILE_DEVICE_UNKNOWN,  \IOCTL_BASE + i,       \METHOD_BUFFERED,      \FILE_ANY_ACCESS       \)// ===========内存数据结构, 不会用来做多端的数据传递======================
// ===========用户自己可以存些需要在内存中传递的数据======================
typedef struct _DEV_EXTENSION {INT32              iTest;WCHAR              wLastPath[512];
}DEV_EXTENSION, * PDEV_EXTENSION;

driverentry

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath
)
{NTSTATUS					status = STATUS_SUCCESS;UNICODE_STRING				uszDriverString;UNICODE_STRING				uszDeviceString;PDEVICE_OBJECT				pDeviceObject = NULL;PDEV_EXTENSION              pDevExt = NULL;HANDLE                      hThread = NULL;DbgPrint("MyDriver DriverEntry\n");// Point uszDriverString at the driver nameRtlInitUnicodeString(&uszDriverString, DEVICE_NAME);// Create and initialize device objectstatus = IoCreateDevice(DriverObject,sizeof(DEV_EXTENSION),&uszDriverString,FILE_DEVICE_UNKNOWN,0,FALSE,&pDeviceObject);if (!NT_SUCCESS(status)){DbgPrint("RegistryMonitor: ERROR IoCreateDevice - %08x\n", status);return status;}// 初始化设备扩展gpDeviceObject = pDeviceObject;pDevExt = (DEV_EXTENSION*)pDeviceObject->DeviceExtension;pDevExt->iTest = 0;wcscpy_s(pDevExt->wLastPath, sizeof(pDevExt->wLastPath)/sizeof(pDevExt->wLastPath[0]), L"");/* Point uszDeviceString at the device name */RtlInitUnicodeString(&uszDeviceString, SYMBOLIC_LINK_NAME);/* Create symbolic link to the user-visible name */status = IoCreateSymbolicLink(&uszDeviceString, &uszDriverString);if (!NT_SUCCESS(status)){DbgPrintEx("RegistryMonitor: ERROR IoCreateSymbolicLink - %08x\n", status);IoDeleteDevice(pDeviceObject);return status;}// Load structure to point to IRP handlersDriverObject->DriverUnload = UnloadDriver;DriverObject->MajorFunction[IRP_MJ_CREATE] = KDispatchCreateClose;DriverObject->MajorFunction[IRP_MJ_CLOSE] = KDispatchCreateClose;DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KDispatchIoctl;return STATUS_SUCCESS;
}

应用层要访问的符号链接是\\.\myDriver

首先驱动层要实现:

pDriverObject->DriverUnload = UnloadDriver;
pDriverObject->MajorFunction[IRP_MJ_CREATE] = KDispatchCreateClose;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = KDispatchCreateClose;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KDispatchIoctl;

Create和Close函数必须要实现,否则CreateFile的时候可能会报错

NTSTATUS KDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{DbgPrint("create or close happen\n");Irp->IoStatus.Status = STATUS_SUCCESS;Irp->IoStatus.Information = 0;IoCompleteRequest(Irp, IO_NO_INCREMENT);return STATUS_SUCCESS;
}

卸载例程


void UnloadDriver(IN PDRIVER_OBJECT DriverObject)
{UNICODE_STRING  uszDeviceString;NTSTATUS        ntStatus;RtlInitUnicodeString(&uszDeviceString, SYMBOLIC_LINK_NAME);IoDeleteSymbolicLink(&uszDeviceString);if (DriverObject->DeviceObject != NULL){IoDeleteDevice(DriverObject->DeviceObject);}DbgPrint("Unload Success \n");
}

其中MyDispatchDeviceControl用来与应用层通过DeviceIoControl通信

NTSTATUS KDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{UINT				dwDataWritten = 0;NTSTATUS			status = STATUS_INVALID_DEVICE_REQUEST;	 // STATUS_UNSUCCESSFULPIO_STACK_LOCATION	pIrpStack = IoGetCurrentIrpStackLocation(Irp);ULONG				uIoControlCode = 0;PVOID				pIoBuffer = NULL;ULONG				uInSize = 0;ULONG				uOutSize = 0;int* pReturn = NULL;PDEV_EXTENSION   	deviceExtension = (DEV_EXTENSION*)DeviceObject->DeviceExtension;NTSTATUS			ntStatus;ANSI_STRING			imagePath;						//进程路径CHAR				szImageName[300];					//进程镜像名HANDLE				pid = PsGetCurrentProcessId();ULONG				information = 0;// Get the IoCtrl CodeuIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;pIoBuffer = Irp->AssociatedIrp.SystemBuffer;uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;if (DeviceObject == gpDeviceObject){switch (uIoControlCode){case MY_CTL_CODE(1):{DbgPrint("buffer:%p,%d,%d\n", pIoBuffer, uInSize, uOutSize);wcscpy_s(gTargetUserSid, sizeof(gTargetUserSid) / sizeof(gTargetUserSid[0]), (WCHAR*)pIoBuffer);DbgPrint("get str:%ws, size:%d\n", gTargetUserSid, uInSize);wcscpy_s((WCHAR*)pIoBuffer, uOutSize, L"ok");information = 6;status = STATUS_SUCCESS;}break;default:{// Invalid code sentDbgPrint("[HookDeviceIocontrol] Unknown IOCTL: 0x%X (%04X,%04X)\r\n",uIoControlCode,DEVICE_TYPE_FROM_CTL_CODE(uIoControlCode),IoGetFunctionCodeFromCtlCode(uIoControlCode));status = STATUS_INVALID_PARAMETER;}}Irp->IoStatus.Information = information;// ***************注意一定要在自己要处理的ctlcode中修改status和information*******// ***************不然deviceIoControl会报错************************************Irp->IoStatus.Status = status;IoCompleteRequest(Irp, IO_NO_INCREMENT);return status;}else{Irp->IoStatus.Information = 0;// Complete the I/O RequestIrp->IoStatus.Status = STATUS_SUCCESS;IoCompleteRequest(Irp, IO_NO_INCREMENT);return status;}
}

然后应用层要打开驱动层的设备链接符号,来得到句柄,DeviceIoControl的通信依赖句柄

void TestDriver()
{BOOL bRet = TRUE;DWORD dwReturnSize = 0;WCHAR wzIn[MAX_PATH], wzOut[MAX_PATH];WCHAR symbolPath[MAX_PATH];HANDLE hSymbol = NULL;// 访问WIN32_LINK_NAMEwcscpy_s(symbolPath, sizeof(symbolPath) / sizeof(symbolPath[0]), L"\\\\.\\myDriver");wcscpy_s(wzIn, sizeof(wzIn)/sizeof(wzIn[0]), L"hello world_000");hSymbol = CreateFileW(symbolPath, GENERIC_WRITE | GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);if (INVALID_HANDLE_VALUE == hSymbol) {WriteLogEx(L"crete file:%s failed, error:%d\n", symbolPath, GetLastError());return;}DWORD dwInBufferSize = (wcslen(wzIn) + 1) * sizeof(WCHAR);bRet = DeviceIoControl(hSymbol, MY_CTL_CODE(1), wzIn, dwInBufferSize,wzOut, sizeof(wzOut), &dwReturnSize, NULL);if (!bRet) {WriteLogEx(L"DeviceIoControl:%s,size:%d failed, error:%d\n", symbolPath, dwInBufferSize, GetLastError());CloseHandle(hSymbol);return;}CloseHandle(hSymbol);WriteLogEx(L"done\n");
}

遇到的坑:

1.CreatFile失败

因为没有实现
DriverObject->MajorFunction[IRP_MJ_CREATE] = KDispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = KDispatchCreateClose;

2.deviceIocontrol返回失败,可是驱动层明明收到数据了

因为没有把返回值status调整为STATUS_SUCCESS,只是等于了一个初始化不会STATUS_SUCCESS的值

3.DeviceIoControl传到驱动层了,但是没有读到数据

一开始是读到了的,后来因为排查问题的时候以为是CtlCode的问题,于是随意调整了一下MY_CTL_CODE的第三个参数METHOD_BUFFERED,凑巧发现这个值影响到驱动层读取DeviceIoControl传递的数据,搜索一番发现微软官方是有定义的

Defining I/O Control Codes - Windows drivers | Microsoft Docsh

Method与内存传输的关系

1..METHOD_BUFFERED:缓冲区模式

inbuffer的内容被复制到IRP中的pIrp->AssociatedIrp.SystemBuffer复制的长度是DeviceIoControl中指定的nInBufferSize。

驱动返回数据时,也是向pIrp->AssociatedIrp.SystemBuffer中写入,操作系统会将数据复制到DeviceIoControl的outBuffer,复制的字节数是pIrp->IoStatus.Information, 这个数值由驱动指定。

派遣函数读取关键信息的代码如下:

	uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;pIoBuffer = Irp->AssociatedIrp.SystemBuffer;uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;

设置返回数据的代码如下

wcscpy_s((WCHAR*)pIoBuffer, uOutSize, L"ok");Irp->IoStatus.Information = 6;// Complete the I/O RequestIrp->IoStatus.Status = STATUS_SUCCESS;IoCompleteRequest(Irp, IO_NO_INCREMENT);return status;


2 METHOD_IN_DIRECT与METHOD_OUT_DIRECT  直接内存模式
与缓冲模式一样,DeviceIoControl中的inBuffer数据被复制到IRP中的pIrp->AssociatedIrp.SystemBuffer,复制的长度是DeviceIoControl指定的nInBufferSize。

直接内存模式中,操作系统会将DeviceIoControl指定的输出缓冲区锁定,然后在内核模式地址下重新映射一段地址。

派遣函数中IRP中的pIrp->MdlAddress记录DeviceIoControl指定的输出缓冲区。派遣函数应该使用MmGetSystemAddressForMdlSafe将这段内存映射到内核模式下的内存地址。

得到输入输出缓冲区的大小以及IOCTL的方式与缓冲区模式相同。

另外需要注意CTL_CODE设置的权限问题,若以只读方式打开设备,METHOD_IN_DIRECT的IOCTL操作会失败。

派遣函数中处理直接内存模式:

//显示输入缓冲区数据
 

UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
for (ULONG i=0;i<cbin;i++)
{DbgPrint("%X\n",InputBuffer[i]);
}
//pIrp->MdlAddress为DeviceIoControl输出缓冲区地址相同
DbgPrint("User Address:0X%08X\n",MmGetMdlVirtualAddress(pIrp->MdlAddress));

返回数据

UCHAR* OutputBuffer = (UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
//InputBuffer被映射到内核模式下的内存地址,必定在0X80000000-0XFFFFFFFF之间memset(OutputBuffer,0xAA,cbout);

3:METHOD_NEITHER :Neither模式
因为此模式直接访问用户模式地址,这是很危险的,所以此模式很少被用到。

使用用户模式地址必须保证调用DeviceIoControl 的线程与派遣函数运行在同一个线程上下文中。

派遣函数得到输入缓冲区的方式与前两种不同,此模式是通过IO堆栈的stack->Parameters.DeviceIoControl.Type3InputBuffer;得到输入缓冲区。

驱动通过pIrp->UserBuffer得到输出缓冲区。

得到输入输出缓冲区的长度与IOCTL的方式与前两种相同。

由于驱动程序的派遣函数不能保证传递进来的用户地址是合法地址,所以要对传入的用户模式地址进行可读写判断。这就需要ProbeForRead函数和ProbeForWrite函数与_try _execpt 结合使用。

下面是驱动派遣函数中Neither模式

//显示输入缓冲区数据

UCHAR* UserInputBuffer = (UCHAR*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
KdPrint(("UserInputBuffer:0X%0X\n",UserInputBuffer));//得到用户模式地址
PVOID UserOutputBuffer = pIrp->UserBuffer;
KdPrint(("UserOutputBuffer:0X%0X\n",UserOutputBuffer));__try
{KdPrint(("Enter __try block\n"));//判断指针是否可读ProbeForRead(UserInputBuffer,cbin,4);//显示输入缓冲区内容for (ULONG i=0;i<cbin;i++){KdPrint(("%X\n",UserInputBuffer[i]));}//判断指针是否可写ProbeForWrite(UserOutputBuffer,cbout,4);//操作输出缓冲区memset(UserOutputBuffer,0xAA,cbout);//如果在上面引发异常,所以以后语句不会被执行!pIrp->IoStatus.Information = cbout;KdPrint(("Leave __try block\n"));
}
__except(EXCEPTION_EXECUTE_HANDLER)
{KdPrint(("Catch the exception\n"));KdPrint(("The program will keep going\n"));status = STATUS_UNSUCCESSFUL;
}pIrp->IoStatus.Information = cbout;

http://www.lryc.cn/news/2419702.html

相关文章:

  • GoYouBBS: 基于Go语言构建的强大论坛系统
  • 渗透测试工程师——第一部分 信息扫描实验系列 001主机存活性探测实验
  • Android布局基础知识:wrap_content,match_parent,layout_weight
  • 树莓派安装MJPG-streamer
  • GALGAME文字提取agth 特殊码大全(特殊码表)和使用方法
  • delphi中使用MSWINSCK.OCX控件
  • 私库如何区分正式和测试环境独立的库
  • md5值是什么意思_详解:PER?霍林格效率值?这个最火的高阶数据究竟是什么意思?...
  • 正在载入中......loading页面的几种方法
  • c++求矩阵的秩_Matlab:矩阵的秩,简化梯形矩阵和线性方程组
  • 【C语言】:static和extern的详细介绍和使用
  • softcore -- CPU rasterization
  • xp序列号大全可通过正版验证的XP序列号发布
  • SolrIK分词器-简单介绍与安装
  • MouseArea组件和MouseEvent事件在Qt中是经常使用的重要组件和事件。本文将详细介绍MouseArea组件和MouseEvent事件的用法,并提供...
  • TTYL的完整形式是什么?
  • mongo执行命令: not authorized on admin to execute command
  • 【渝粤教育】电大中专电商运营实操12作业 题库
  • nbtscan局域网扫描的原理
  • VS各个版本之间2005、08、10、12...转换
  • NumberFormat类
  • WritePrivateProfileString() 用法
  • 新发的日常小实验——使用IETester测试不同IE版本的浏览器,测试网页JS的兼容性(console未定义兼容测试)
  • cpio命令的常用方法
  • stm32串口反相输出 SP3232输出反相解决办法
  • FindWindowEx 遍历所有窗口
  • AV1编码各种浏览的版本支持
  • 网安概论(三)
  • 85m gamer主板acpi驱动_关于网卡驱动的问题
  • Python实现经典吃豆豆游戏教程:重温童年经典,学习Python游戏开发,附带完整源码