驱动程序的派遣函数
好文章,来自【福优学苑@音视频+流媒体】
简介与章节概览:
基础知识:
A、IRP是什么?
B、派遣函数是什么?
C、设备读写的三种方式:Buffer,Direct,Other
D、设备控制的三种方式:DeviceIoControl(...)
IRP: IO Request Package
ReadFile(handle, inbuf, len, &retSize, NULL...)
IO管理器,IRP_MJ_READ, Major, Minor(Mn)
数据结构与API:
IO堆栈:用来存储更详细的用户(应用层)参数
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
ULONG ulReadLength = stack->Parameters.Read.Length;
/// 缓冲区方式读写:复制两遍(1.设备到内核、2.内核到应用层)
memset(pIrp->AssociatedIrp.SystemBuffer, 0xAA, ulReadLength);
DO_DIRECT_IO
MDL:内存描述符表
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
stack->Parameters.Read.Length;
MmGetMdlByteCount(pIrp->MdlAddress)
MmGetMdlVirtualAddress(pIrp->MdlAddress)
MmGetMdlByteOffset(pIrp->MdlAddress)
PVOID pKernelBuf = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress);
irp与派遣函数
Irp是什么?
I/O Request Package
MajorFunction
MinorFunction
irp类型
对派遣函数的简单处理
//对一般IRP的简单操作,后面会介绍对IRP更复杂的操作
NTSTATUS status = STATUS_SUCCESS;
// 完成IRP
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
通过设备链接打开设备
HANDLE hDevice =
CreateFile("\\\\.\\Chapter7_1_HelloDDK",
GENERIC_READ | GENERIC_WRITE,
0, // share mode none
NULL, // no security
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL ); // no template
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("Failed to obtain file handle to device: "
"%s with Win32 error code: %d\n",
"MyWDMDevice", GetLastError() );
return 1;
}
CloseHandle(hDevice);
编写一个更通用的派遣函数
IO_STACK_LOCATION : IO堆栈
跟踪irp的利器irptrace
缓冲区方式读写操作
缓冲区设备
缓冲区设备读写
缓冲区设备模拟文件读写
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) { KdPrint(("Enter HelloDDKRead\n")); //对一般IRP的简单操作,后面会介绍对IRP更复杂的操作 NTSTATUS status = STATUS_SUCCESS; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp); ULONG ulReadLength = stack->Parameters.Read.Length; ReadFile(hDevice, usrBuffer, len, &retSize,0); // 完成IRP //设置IRP完成状态 pIrp->IoStatus.Status = status; //设置IRP操作了多少字节 /// 返回给应用层的字节数,ReadFile(,,,4)的第4个参数 pIrp->IoStatus.Information = ulReadLength;// bytes xfered /// 模式真实物理设备的【读写】 /// 缓冲区方式读写:复制两遍(1.设备到内核、2.内核到应用层) memset(pIrp->AssociatedIrp.SystemBuffer, 0xAA, ulReadLength); //处理IRP IoCompleteRequest( pIrp, IO_NO_INCREMENT ); KdPrint(("Leave HelloDDKRead\n")); return status; }
直接方式读写操作
直接读取设备
DO_DIRECT_IO
MDL:内存描述符表
ULONG mdl_length = MmGetMdlByteCount(pIrp->MdlAddress);
PVOID mdl_address = MmGetMdlVirtualAddress(pIrp->MdlAddress);
ULONG mdl_offset = MmGetMdlByteOffset(pIrp->MdlAddress);
//用MmGetSystemAddressForMdlSafe得到MDL在内核模式下的映射
PVOID kernel_address = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
KdPrint(("kernel_address:0X%08X\n",kernel_address));
直接读取设备的读写
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) { KdPrint(("Enter HelloDDKRead\n")); PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension; NTSTATUS status = STATUS_SUCCESS; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp); ULONG ulReadLength = stack->Parameters.Read.Length; KdPrint(("ulReadLength:%d\n",ulReadLength)); ULONG mdl_length = MmGetMdlByteCount(pIrp->MdlAddress); PVOID mdl_address = MmGetMdlVirtualAddress(pIrp->MdlAddress); ULONG mdl_offset = MmGetMdlByteOffset(pIrp->MdlAddress); KdPrint(("mdl_address:0X%08X\n",mdl_address)); KdPrint(("mdl_length:%d\n",mdl_length)); KdPrint(("mdl_offset:%d\n",mdl_offset)); if (mdl_length!=ulReadLength) { //MDL的长度应该和读长度相等,否则该操作应该设为不成功 pIrp->IoStatus.Information = 0; status = STATUS_UNSUCCESSFUL; }else { //用MmGetSystemAddressForMdlSafe得到MDL在内核模式下的映射 PVOID kernel_address = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority); KdPrint(("kernel_address:0X%08X\n",kernel_address)); memset(kernel_address,0XAA,ulReadLength); pIrp->IoStatus.Information = ulReadLength;// bytes xfered } pIrp->IoStatus.Status = status; IoCompleteRequest( pIrp, IO_NO_INCREMENT ); KdPrint(("Leave HelloDDKRead\n")); return status; }
其他方式读写操作
其他方式设备
Neither
其他方式读写
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) { KdPrint(("Enter HelloDDKRead\n")); PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension; NTSTATUS status = STATUS_SUCCESS; //得到当前堆栈 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp); //得到读的长度 ULONG ulReadLength = stack->Parameters.Read.Length; //得到读的偏移量 ULONG ulReadOffset = (ULONG)stack->Parameters.Read.ByteOffset.QuadPart; //得到用户模式地址 PVOID user_address = pIrp->UserBuffer; KdPrint(("user_address:0X%0X\n",user_address)); __try { KdPrint(("Enter __try block\n")); //判断空指针是否可写,显然会导致异常 ProbeForWrite(user_address,ulReadLength,4); memset(user_address,0xAA,ulReadLength); //由于在上面引发异常,所以以后语句不会被执行! 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.Status = status; pIrp->IoStatus.Information = ulReadLength;// bytes xfered IoCompleteRequest( pIrp, IO_NO_INCREMENT ); KdPrint(("Leave HelloDDKRead\n")); return status; }
IO设备控制操作
deviceiocontrol与驱动交互
DeviceIoControl(...)
缓冲内存模式ioctl
#define IOCTL_TEST1 CTL_CODE(\
FILE_DEVICE_UNKNOWN, \
0x800, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
直接内存模式ioctl
#define IOCTL_TEST2 CTL_CODE(\
FILE_DEVICE_UNKNOWN, \
0x801, \
METHOD_IN_DIRECT, \
FILE_ANY_ACCESS)
其他内存模式ioctl
#define IOCTL_TEST3 CTL_CODE(\
FILE_DEVICE_UNKNOWN, \
0x802, \
METHOD_NEITHER, \
FILE_ANY_ACCESS)
小结
***【在线视频教程】***