10、Windows驱动开发技术详解笔记(6) 基本语法回顾

简介: 7、I/O Request Package,输入输出请求包 1)基本概念 IRP 的全名是I/O Request Package,即输入输出请求包,它是Windows 内核中的一种非常重要的数据结构。

<?xml:namespace prefix = o />

7I/O Request Package,输入输出请求包

1)基本概念

IRP 的全名是I/O Request Package,即输入输出请求包,它是Windows 内核中的一种非常重要的数据结构。上层应用程序与底层驱动程序通信时,应用程序会发出I/O 请求,操作系统将相应的I/O 请求转换成相应的IRP,不同的IRP 会根据类型被分派到不同的派遣例程中进行处理。

IRP有两个基本的属性,即MajorFunction MinorFunction,分别记录IRP 的主类型和子类型。操作系统根据MajorFunction 决定将IRP 分发到哪个派遣例程,然后派遣例程根据MinorFunction 进行细分处理。

IRP的概念类似于Windows 应用程序中消息的概念。在Win32 编程中,程序由消息驱动,不同的消息被分发到不同的处理函数中,否则由系统默认处理。

文件I/O的相关函数例如CreateFileReadFileWriteFileCloseHandle等分别会引发操作系统产生 IRP_MJ_CREATEIRP_MJ_READIRP_MJ_WRITEIRP_MJ_CLOSE等不同的IRP,这些IRP会被传送到驱动程序的相应派遣例程中。

wps_clip_image-7876

IRP常见类型

typedef struct _IO_STATUS_BLOCK {

NTSTATUS Status;

ULONG Information;

}IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

2IRP处理

以前知道:在DriverEntry中为不同的IRP设置相应的派遣例程。在派遣例程中处理IRP最简单做法就是将IRP的状态设置为成功,然后结束IRP请求并返回成功,同时还要记得设置这个IRP请求操作了多少字节。

我们在派遣函数中设置IRP的完成状态为STATUS_SUCCESS,发起I/O请求的Win32 API才能返回TRUE,否则Win32 API将返回FALSE,在这个时候可以通过GetLastError获得错误代码,这个错误代码会和此时IRP 被设置的状态一致。

如我们以前描述的派遣函数:

/************************************************************************

* 函数名称:HelloDDKDispatchRoutine

* 功能描述:对读IRP进行处理

* 参数列表:

pDevObj:功能设备对象

pIrp:IO请求包

* 返回 值:返回状态

*************************************************************************/

#pragma PAGEDCODE

NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,

IN PIRP pIrp)

{

KdPrint(("Enter HelloDDKDispatchRoutine\n"));

NTSTATUS status = STATUS_SUCCESS;

// 完成IRP

pIrp->IoStatus.Status = status;

pIrp->IoStatus.Information = 0; // bytes xfered

IoCompleteRequest( pIrp, IO_NO_INCREMENT );

KdPrint(("Leave HelloDDKDispatchRoutine\n"));

return status;

}

3IRPIO_STACK_LOCATION

开发一个驱动要有可能要处理各种IRP。我们先学习应用程序和驱动交互而产生的IRP。应用程序为了和驱动通信,首先必须打开设备。然后发送或者接收信息,最后关闭它;这至少需要三个IRP:第一个是打开请求,第二个发送或者接收信息,第三个是关闭请求。

IRP的种类取决于主功能号。主功能号就是前面的说的DRIVER_OBJECT中的分发函数指针数组中的索引,打开请求的主功能号是IRP_MJ_CREATE,而关闭请求的主功能号是IRP_MJ_CLOSE

如果写有独立的处理IRP_MJ_CREATEIRP_MJ_CLOSE的分发函数,就没有必要自然判断IRP的主功能号。使用一个函数处理所有的IRP,那么首先就要得到IRP的主功能号。IRP的主功能号在IRP的当前栈空间中。

IRP总是发送给一个设备栈,到每个设备上的时候拥有一个当前栈空间来保存在这个设备上的请求信息。

NTSTATUS MyDispatchFunction(PDEVICE_OBJECT device,PIRP irp)

{

// 获得当前irp调用栈空间

PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);

NTSTATUS status = STATUS_UNSUCCESSFUL;

swtich(irpsp->MajorFunction)

{

// 处理打开请求

case IRP_MJ_CREATE:

……

break;

// 处理关闭请求

case IRP_MJ_CLOSE:

……

break;

// 处理设备控制信息

case IRP_MJ_DEVICE_CONTROL:

……

break;

// 处理读请求

case IRP_MJ_READ:

……

break;

// 处理写请求

case IRP_MJ_WRITE:

……

break;

default:

break;

}

return status;

}

4)打开和关闭

在一些有同步限制的驱动中(比如每次只允许一个进程打开设备)编程要更加复杂一点,现在忽略这些问题。

简单的返回一个IRP成功(或者直接失败)是三部曲,如下:

·设置irp->IoStatus.Information0

·设置irp->IoStatus.Status的状态。如果成功则设置STATUS_SUCCESS,否则设置错误码。

·调用IoCompleteRequest (irp,IO_NO_INCREMENT),这个函数完成IRP

以上三步完成后,直接返回irp->IoStatus.Status即可。

NTSTATUS

MyCreateClose(

IN PDEVICE_OBJECT device,

IN PIRP irp)

{

irp->IoStatus.Information = 0;

irp->IoStatus.Status = STATUS_SUCCESS;

IoCompleteRequest (irp,IO_NO_INCREMENT);

return irp->IoStatus.Status;

}

当然,在前面设置分发函数的时候,应该加上:

DriverObject->MajorFunctions[IRP_MJ_CREATE] = MyCreateClose;

DriverObject->MajorFunctions[IRP_MJ_CLOSE] = MyCreateClose;

在应用层,打开和关闭这个设备的代码如下:

HANDLE device=CreateFile("\\\\.\\MyCDOSL",

GENERIC_READ|GENERIC_WRITE,0,0,

OPEN_EXISTING,

FILE_ATTRIBUTE_SYSTEM,0);

if (device == INVALID_HANDLE_VALUE)

{

// …. 打开失败,说明驱动没加载,报错即可

}

// 关闭

CloseHandle(device);

目录
相关文章
|
3月前
|
缓存 网络协议 数据安全/隐私保护
[运维笔记] - (命令).Windows server常用网络相关命令总结
[运维笔记] - (命令).Windows server常用网络相关命令总结
185 0
|
1月前
|
数据可视化 数据库 C++
Qt 5.14.2揭秘高效开发:如何用VS2022快速部署Qt 5.14.2,打造无与伦比的Windows应用
Qt 5.14.2揭秘高效开发:如何用VS2022快速部署Qt 5.14.2,打造无与伦比的Windows应用
|
3月前
|
存储 Ubuntu 开发工具
ffmpeg笔记(二)windows下和ubuntu-16.04下ffmpeg编译
ffmpeg笔记(二)windows下和ubuntu-16.04下ffmpeg编译
|
4月前
|
Linux API C++
音视频windows安装ffmpeg6.0并使用vs调试源码笔记
音视频windows安装ffmpeg6.0并使用vs调试源码笔记
111 0
|
4月前
|
监控 安全 API
5.9 Windows驱动开发:内核InlineHook挂钩技术
在上一章`《内核LDE64引擎计算汇编长度》`中,`LyShark`教大家如何通过`LDE64`引擎实现计算反汇编指令长度,本章将在此基础之上实现内联函数挂钩,内核中的`InlineHook`函数挂钩其实与应用层一致,都是使用`劫持执行流`并跳转到我们自己的函数上来做处理,唯一的不同的是内核`Hook`只针对`内核API`函数,但由于其身处在`最底层`所以一旦被挂钩其整个应用层都将会受到影响,这就直接决定了在内核层挂钩的效果是应用层无法比拟的,对于安全从业者来说学会使用内核挂钩也是很重要。
39 1
5.9 Windows驱动开发:内核InlineHook挂钩技术
|
4月前
|
监控 API C++
8.4 Windows驱动开发:文件微过滤驱动入门
MiniFilter 微过滤驱动是相对于`SFilter`传统过滤驱动而言的,传统文件过滤驱动相对来说较为复杂,且接口不清晰并不符合快速开发的需求,为了解决复杂的开发问题,微过滤驱动就此诞生,微过滤驱动在编写时更简单,多数`IRP`操作都由过滤管理器`(FilterManager或Fltmgr)`所接管,因为有了兼容层,所以在开发中不需要考虑底层`IRP`如何派发,更无需要考虑兼容性问题,用户只需要编写对应的回调函数处理请求即可,这极大的提高了文件过滤驱动的开发效率。
40 0
|
4月前
|
监控 Windows
7.4 Windows驱动开发:内核运用LoadImage屏蔽驱动
在笔者上一篇文章`《内核监视LoadImage映像回调》`中`LyShark`简单介绍了如何通过`PsSetLoadImageNotifyRoutine`函数注册回调来`监视驱动`模块的加载,注意我这里用的是`监视`而不是`监控`之所以是监视而不是监控那是因为`PsSetLoadImageNotifyRoutine`无法实现参数控制,而如果我们想要控制特定驱动的加载则需要自己做一些事情来实现,如下`LyShark`将解密如何实现屏蔽特定驱动的加载。
32 0
7.4 Windows驱动开发:内核运用LoadImage屏蔽驱动
|
12天前
|
监控 安全 API
7.3 Windows驱动开发:内核监视LoadImage映像回调
在笔者上一篇文章`《内核注册并监控对象回调》`介绍了如何运用`ObRegisterCallbacks`注册`进程与线程`回调,并通过该回调实现了`拦截`指定进行运行的效果,本章`LyShark`将带大家继续探索一个新的回调注册函数,`PsSetLoadImageNotifyRoutine`常用于注册`LoadImage`映像监视,当有模块被系统加载时则可以第一时间获取到加载模块信息,需要注意的是该回调函数内无法进行拦截,如需要拦截则需写入返回指令这部分内容将在下一章进行讲解,本章将主要实现对模块的监视功能。
29 0
7.3 Windows驱动开发:内核监视LoadImage映像回调
|
4月前
|
监控 安全 API
7.2 Windows驱动开发:内核注册并监控对象回调
在笔者上一篇文章`《内核枚举进程与线程ObCall回调》`简单介绍了如何枚举系统中已经存在的`进程与线程`回调,本章`LyShark`将通过对象回调实现对进程线程的`句柄`监控,在内核中提供了`ObRegisterCallbacks`回调,使用这个内核`回调`函数,可注册一个`对象`回调,不过目前该函数`只能`监控进程与线程句柄操作,通过监控进程或线程句柄,可实现保护指定进程线程不被终止的目的。
29 0
7.2 Windows驱动开发:内核注册并监控对象回调
|
Windows 数据安全/隐私保护 网络协议