驱动程序的派遣函数


***【在线视频教程】***

好文章,来自【福优学苑@音视频+流媒体】

简介与章节概览:


基础知识:

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


image.png


irp类型


image.png


对派遣函数的简单处理


//对一般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)

小结  


好文章,来自【福优学苑@音视频+流媒体】
***【在线视频教程】***