派遣函数(1) - 初识派遣函数
来源:互联网 发布:怎样学好历史高中知乎 编辑:程序博客网 时间:2024/04/28 04:46
转载自;http://mzf2008.blog.163.com/blog/static/3559978620101114103859212/
IRP与派遣函数< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" />
IRP的处理机制类似Windows应用程序的“消息处理”机制,驱动程序接收到不同类型的IRP后,会进入不同的派遣函数,在派遣函数中IRP得到处理。
IRP
在Windows内核中,有一种数据结构叫做IPR(I/O Request Package),即输入输出请求包。它是与输入输出相关的重要的数据结构。上层应用程序与底层驱动程序通信时,应用程序会发出I/O请求包。操作系统将I/O请求转化为IRP数据,不用类型的IRP会根据类型传递到不同的派遣函数中。
IRP是一个很复杂的数据结构。其中两个很重要的属性MajorFunction和MinorFunction,分别记录IRP的主类型和子类型。操作系统根据MajorFunction将IRP“派遣”到不同的派遣函数中,在派遣函数中还可以继续判断这个IRP属于哪种MinorFunction。
一般来说,NT式驱动程序和WDM驱动程序都是在DriverEntry中注册IRP的派遣函数。
如下代码:
#pragma INITCODE
extern "C" NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObj,
IN PUNICODE_STRING pRegisterPath )
{
KdPrint(("Entry DriverEntry!\n"));
//设置卸载函数
pDriverObj->DriverUnload = HelloDDKUnload;
//设置派遣函数
pDriverObj->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;
pDriverObj->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;
pDriverObj->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;
pDriverObj->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;
NTSTATUS status;
//创建设备对象
status = CreateDevice(pDriverObj);
KdPrint(("Leave DriverEntry!\n"));
return STATUS_SUCCESS;
}
在DriverEntry的驱动对象pDriverObj中,有个函数指针数组MajorFunction,这个数据的每个元素都记录着一个函数的地址。通过设置这个数组,可以将IRP的类型和派遣函数关联起来。
在上面的例子中,只设置了4种类型的IRP派遣函数。对于其他没有设置的IRP类型,系统默认这些IRP类型与_IopInvalidDeviceRequest函数关联。
在进入DriverEntry之前,操作系统会将_IopInvalidDeviceRequest的地址填满整个MajorFunction数组。
IRP类型
IRP类型
来源
IRP_MJ_CREATE
创建设备,CreateFile会产生此IRP
IRP_MJ_CLOSE
关闭设备,CloseHandle会产生此IRP
IRP_MJ_CLEARUP
清除工作,CloseHandle会产生此IRP
IRP_MJ_DEVICE_CONTROL
DeviceIoControl函数会产生此IRP
IRP_MJ_PNP
即插即用消息,只有WDM才支持此种IRP
IRP_MJ_PNP_POWER
在操作系统处理电源消息时产生此IRP
IRP_MJ_QUERY _INFORMATION
获取文件长度,GetFileSize会产生此IRP
IRP_MJ_READ
读取设备内容,ReadFIle会产生此IRP
IRP_MJ_SET_INFORMATION
设置文件长度,GetFileSize会产生此IRP
IRP_MJ_SHUTDOWN
关闭系统前会产生此IRP
IRP_MJ_SYSTEM_CONTROL
系统内部产生的控制信息
IRP_MJ_WRITE
对设备进行WriteFile时产生此IRP
对派遣函数的简单处理
如下代码:
#pragma PAGEDCODE
NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
KdPrint(("Enter HelloDDKDispatchRoutine\n"));
NTSTATUS status = STATUS_SUCCESS;
// 设置IRP完成状态为STATUS_SUCCESS,这样,发起I/O请求的API函数(如WriteFile)
//将会返回TRUE;
pIrp->IoStatus.Status = status;
//设置IRP操作了多少字节,如果是ReadFile产生的IRP,这个字节数代表从设备中读
//了多少字节。
pIrp->IoStatus.Information = 0; // bytes xfered
// IoCompleteRequest
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint(("Leave HelloDDKDispatchRoutine\n"));
return status;
}
应用程序通过符号链接打开设备
在应用程序中,设备可以通过符号链接进行访问。在驱动程序中,我们通过函数IoCreateSymbolicLink来创建符号链接。加入我们定义的符号链接为\??\HelloDevice,那么我们在应用程序中应该把前面的\??\改写成\\.\,即\\.\HelloDevice,写成C语言字符串形式则为\\\\.\\HelloDevice
示例代码:(注:每个IRP对应着自己相应的派遣函数)
int main(void)
{
HANDLE hDevice;
//打开设备句柄,触发IRP_MJ_CREATE
hDevice = CreateFile("\\\\.\\HelloDDK",GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
DWORD dwError = GetLastError();
printf("%d\n", dwError);
}
//关闭设备句柄,触发IRP_MJ_CLEANUP 和IRP_MJ_CLOSE
CloseHandle(hDevice);
return 1;
}
编写一个更通用的派遣函数
首先介绍一个重要的数据结构IO_STACK_LOCATION,即I/O堆栈,这个数据结构跟IRP紧密相连。
我们知道,驱动对象会创建一个个的设备对象,并将这些设备对象叠成一个垂直结构。这种垂直的结构很像栈,因此被称为“设备栈”。
IRP会被操作系统发送到设备栈的顶层,如果顶层的设备对象的派遣函数结束了IRP的请求,则这次I/O请求结束。如果没有将IRP的请求结束,那么操作系统会将IRP转发到设备栈的下一层设备进行处理。如果这个设备的派遣函数依然不能结束IRP请求,那么则会继续向下层设备转发。
因此,一个IRP可能会被转发多次。为了记录IRP在每层设备中做的操作。IRP会有一个IO_STACK_LOCATION数组。数组 的元素数应该大于IRP穿越的设备数。每个IO_STACK_LACTION元素记录着对应设备中做的操作。对于本层设备对应 的IO_STACK_LOCATION,可以通过IoGetCurrentIrpStackLocation函数得到。
PIO_STACK_LOCATION
IoGetCurrentIrpStackLocation(
IN PIRP Irp
);
注意:IO_STACK_LOCATION结构中的MajorFunction子域会记录当前IRP的类型。
下面的代码演示了派遣函数如何获得当前的IO_STACK_LOCATION,以及如何获得IRP的类型。
示例代码:(注:每个IRP对应着同一个派遣函数)
#pragma PAGEDCODE
NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDeviceObj, IN PIRP pIrp)
{
KdPrint(("进入派遣函数中!\n"));
//获取当前设备中的IO_STACK_LOCATION结构
PIO_STACK_LOCATION pIos;
pIos = IoGetCurrentIrpStackLocation(pIrp);
//建立一个字符串数组,与IRP类型对应起来
static CHAR* irpName[] = {
"IRP_MJ_CREATE",
"IRP_MJ_CREATE_NAMED_PIPE",
"IRP_MJ_CLOSE",
"IRP_MJ_READ",
"IRP_MJ_WRITE",
"IRP_MJ_QUERY_INFORMATION",
"IRP_MJ_SET_INFORMATION",
"IRP_MJ_QUERY_EA",
"IRP_MJ_SET_EA",
"IRP_MJ_FLUSH_BUFFERS",
"IRP_MJ_QUERY_VOLUME_INFORMATION",
"IRP_MJ_SET_VOLUME_INFORMATION",
"IRP_MJ_DIRECTORY_CONTROL",
"IRP_MJ_FILE_SYSTEM_CONTROL",
"IRP_MJ_DEVICE_CONTROL",
"IRP_MJ_INTERNAL_DEVICE_CONTROL",
"IRP_MJ_SHUTDOWN",
"IRP_MJ_LOCK_CONTROL",
"IRP_MJ_CLEANUP",
"IRP_MJ_CREATE_MAILSLOT",
"IRP_MJ_QUERY_SECURITY",
"IRP_MJ_SET_SECURITY",
"IRP_MJ_POWER",
"IRP_MJ_SYSTEM_CONTROL",
"IRP_MJ_DEVICE_CHANGE",
"IRP_MJ_QUERY_QUOTA",
"IRP_MJ_SET_QUOTA",
"IRP_MJ_PNP"
};
UCHAR irpType = pIos->MajorFunction;
if (irpType >= arraysize(irpName))
{
KdPrint(("不明真相的类型,类型为:%x", irpType));
}
else
{
KdPrint(("%s\n", irpName[irpType]));
}
NTSTATUS status = STATUS_SUCCESS;
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("离开派遣函数!\n"));
return status;
}
- 派遣函数(1) - 初识派遣函数
- 派遣函数
- IRP与派遣函数
- IRP派遣函数
- 派遣函数IRP
- IRP 与 派遣函数
- 【驱动之三】派遣函数
- 派遣函数学习1)缓冲区方式读写
- 派遣函数学习2)直接读写
- 派遣函数(3) - IO设备控制操作
- Windows驱动开发——派遣函数
- Windows驱动程序开发(七)--- 派遣函数
- Windows驱动派遣函数的学习
- Windows驱动开发(8) - 派遣函数
- unhook——如何得到原始派遣函数的地址
- 我对 HOOK 派遣例程函数表的体会。。。
- <学习笔记>Windows驱动开发技术详解__派遣函数
- <学习笔记>Windows驱动开发技术详解__派遣函数
- FLTK学习-6-使用FLUID编程(4)
- FLTK学习-7-使用FLUID编程(5)
- Windows内核函数(3) - 内核模式下的注册表操作
- 锐捷重启
- FLTK学习-8-鼠标事件处理
- 派遣函数(1) - 初识派遣函数
- fltk动态分配数据显示图像
- 派遣函数(2) - 设备对象的3种读写方式
- 木马是如何穿过防火墙的
- pip --pre error
- 派遣函数(3) - IO设备控制操作
- Android开发之Sqlite的使用(二)
- 线程同步(1) - 用户模式下的线程同步
- CSS border-width 属性