当前位置: 首页 > 内核 > 正文

分层驱动模型

一、简介

IRP是在设备栈上进行传递的,由高层传递给底层,而每一层的设备都会对应一个IO_STACK_LOCATION类型的IO堆栈用来记录IRP到达本层设备时所作的操作。而当IRP被某一层设备完成时,IO堆栈会一层一层地弹出,这称之为设备栈的回卷。

我们现在看见了,IRP在传递的时候,会经过好几层的设备,每一层同样对于一个驱动,这就称之为分层驱动模型。而正是这种分层的方式,使得我们可以在设备栈上添加一个设备,以达到Hook IRP。

二、Hook IRP

要做到Hook IRP,我们需要先了解一个内核API.

PDEVICE_OBJECT IoAttachDeviceToDeviceStack(
  [in] PDEVICE_OBJECT SourceDevice,
  [in] PDEVICE_OBJECT TargetDevice
);
[in] SourceDevice

指向调用方创建的设备对象的指针。

[in] TargetDevice

指向另一个驱动程序的设备对象的指针,例如之前调用 IoGetDeviceObjectPointer 返回的指针。

此函数正好可以用来将一个设备放置到某个设备栈的最顶层,这样我们的驱动就会第一个捕获到传递到设备栈的IRP。同时此内核API返回原本设备栈最高层的设备。

要将DeviceC插入到设备栈最顶层,那么函数中SourceDevice为DeviceC,TargetDevice为DeviceB。返回值为DeviceB,即原本设备栈最顶层的设备。

三、完成例程

使用IoSetCompleteRoutine对IRP设置完成例程,当IRP向上回卷时,本层若是设置了完成例程,则会进入到完成例程中。但完成例程的使用需要注意以下几点:

1、如果设置了完成例程,则IRP使用IoCallDriver就不能使用IoSkipCurrentIrpStackLocation,必须使用IoCopyCurrentIrpStackLocationToNext。

2.完成例程中返回的是STATUS_SUCCESS或者STATUS_CONTINUE_COMPLETION(SUCCESS别名),则IRP继续向上回卷,驱动不会再获取控制权。返回STATUS_MORE_PROCESSING_REQUIRED,本层驱动会获得控制权,并且设备栈不会向上回卷。而且还需要在本层调用IoCompleteRequest完成IRP

返回STATUS_SUCCESS示例

NTSTATUS
  MyIoCompletion(
    IN PDEVICE_OBJECT  DeviceObject,
    IN PIRP  Irp,
    IN PVOID  Context
    )
{
	//进入此函数标志底层驱动设备将IRP完成
	KdPrint(("Enter MyIoCompletion\n"));
    if (Irp->PendingReturned) 
	{
		//传播pending位
        IoMarkIrpPending( Irp );
    }
	return STATUS_SUCCESS;//同STATUS_CONTINUE_COMPLETION
}

#pragma PAGEDCODE
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
								 IN PIRP pIrp) 
{
	KdPrint(("DriverB:Enter B HelloDDKRead\n"));
	NTSTATUS ntStatus = STATUS_SUCCESS;
	//将自己完成IRP,改成由底层驱动负责

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

	//将当前IRP堆栈拷贝底层堆栈
	IoCopyCurrentIrpStackLocationToNext(pIrp);

	//设置完成例程
	IoSetCompletionRoutine(pIrp,MyIoCompletion,NULL,TRUE,TRUE,TRUE);

	//调用底层驱动
    ntStatus = IoCallDriver(pdx->TargetDevice, pIrp);

	//当IoCallDriver后,并且完成例程返回的是STATUS_SUCCESS
	//IRP就不在属于派遣函数了,就不能对IRP进行操作了
	if (ntStatus == STATUS_PENDING)
	{
		KdPrint(("STATUS_PENDING\n"));
	}
	ntStatus = STATUS_PENDING; 

	KdPrint(("DriverB:Leave B HelloDDKRead\n"));

	return ntStatus;
}

返回STATUS_MORE_PROCSSING_REQUIRED

NTSTATUS
  MyIoCompletion(
    IN PDEVICE_OBJECT  DeviceObject,
    IN PIRP  Irp,
    IN PVOID  Context
    )
{

	if (Irp->PendingReturned == TRUE) 
	{
		//设置事件
		KeSetEvent((PKEVENT)Context,IO_NO_INCREMENT,FALSE);
	}

	return STATUS_MORE_PROCESSING_REQUIRED;
}

#pragma PAGEDCODE
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
								 IN PIRP pIrp) 
{
	KdPrint(("DriverB:Enter B HelloDDKRead\n"));
	NTSTATUS ntStatus = STATUS_SUCCESS;
	//将自己完成IRP,改成由底层驱动负责

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

	//将本层的IRP堆栈拷贝到底层堆栈
	IoCopyCurrentIrpStackLocationToNext(pIrp);

	KEVENT event;
	//初始化事件
	KeInitializeEvent(&event, NotificationEvent, FALSE);

	//设置完成例程
	IoSetCompletionRoutine(pIrp,MyIoCompletion,&event,TRUE,TRUE,TRUE);

	//调用底层驱动
    ntStatus = IoCallDriver(pdx->TargetDevice, pIrp);

	if (ntStatus == STATUS_PENDING)
	{
		KdPrint(("IoCallDriver return STATUS_PENDING,Waiting ...\n"));
		KeWaitForSingleObject(&event,Executive,KernelMode ,FALSE,NULL);
		ntStatus = pIrp->IoStatus.Status;
	}

	//虽然在底层驱动已经将IRP完成了,但是由于完成例程返回的是
	//STATUS_MORE_PROCESSING_REQUIRED,因此需要再次调用IoCompleteRequest!
	IoCompleteRequest (pIrp, IO_NO_INCREMENT);

	KdPrint(("DriverB:Leave B HelloDDKRead\n"));

	return ntStatus;
}

本文固定链接: https://www.socarates.online/index.php/2024/02/20/%e5%88%86%e5%b1%82%e9%a9%b1%e5%8a%a8%e6%a8%a1%e5%9e%8b/ | 安全技术研究

avatar
该日志由 Socarates 于2024年02月20日发表在 内核 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: 分层驱动模型 | 安全技术研究
关键字: , ,
【上一篇】
【下一篇】

分层驱动模型:等您坐沙发呢!

发表评论


快捷键:Ctrl+Enter