忆杰的博客

忆杰的博客

完成例程

在将Irp发送给底层驱动程序, 或者其他驱动之前, 我们可以对Irp设置一个完成例程, 这样一旦在底层驱动程序将Irp完成以后, Irp完成例程将被触发, 通过设置完成例程可以方便的使程序员了解其他驱动对Irp进行的处理.

完成例程是一种很暴力的东西, 非常有用, 必须要熟练掌握, 当调用IoCallDriver将Irp的控制权交给被动驱动程序的时候有两种情况, 一种是调用的设备同步完成了这个Irp, 那么这时候从IoCallDriver返回的时候Irp已经被完成了, 另外一种的话, 就是调用的时候是异步操作. IoCallDriver会立刻返回IoCallDriver, 但此时并没有真正的完成Irp.在这种情况下, 调用IoCallDriver前, 先对Irp注册一个完成例程, 当底层驱动或者其他驱动完成的此Irp时, 此完成例程立刻被调用.

这就是空手套白狼啊, 过滤的暴力之处, 要设置完成例程只需要在当前的Io堆栈(Io_Stack_Location)中的CompletionRoutine子域. 如果这个字段非空, 那么在Irp完成的时候会回卷看到这个字段非空就会调用我们设置的完成例程. 当然微软已经提供了宏方便我们设置这个区域, IoSetCompletionRoutine. 就是它..

还有一点, 虽然我们没有动Irp但是这里不能够直接跳过当前的堆栈, 必须调用IoCopyCurrentIrpStackToNext函数, 将本层的I/O堆栈Copy到下一层的I/O堆栈. 这样在Irp完成的时候就会进入我们设置的完成例程, 这时候我们的完成例程处理完后可以返回两个值, 一个是Status_Success(Status_Continue_Completion)两个是等价的, 如果返回这个值, 那么故事就在这里就结束了, Irp改回卷回卷, 改干嘛干嘛. 但是如果返回Status_Continue_Completion, 那么我们的分发函数就又会获得控制权, 多暴力啊.

还有一个问题就是传播Pending位了, 这个东西的话我们设置了完成例程必须我们帮忙传播这个位了.但是是不可以在IoCallDriver后去动Irp的, 记住这点在调用IoCallDriver以后Irp就不属于驱动这一层了. 所以这个事情的话只能留给完成例程来做了. 所以记得在完成里面里面调用IoMarkIrpPending就好了..

这边是代码, 一个测试驱动和前面一样, 还有一个用户态这边的程序, 就是打开设备一个写设备, 一个读设备和前面也一样就不贴了:

/*
	Windows 内核下驱动程序调用驱动程序设置了完成例程 测试驱动
	编译方法参见makefile. TAB = 8
*/
#include <ntddk.h>

#define DEVICE_NAME		L"\\Device\\DevCompletionRoutine"
#define SYSLINK_NAME		L"\\??\\SysLinkCompletionRoutine"
#define TARGET_DEVICE_NAME	L"\\Device\\DevTestDriver"

typedef struct tagDevice_Ext {
	PDEVICE_OBJECT pDeviceObj;
	PDEVICE_OBJECT pLowDevice;
	UNICODE_STRING USzDeviceName;
	UNICODE_STRING USzSysLinkName;
} DEVICE_EXT, *PDEVICE_EXT;

//===========================================================================
//驱动卸载例程
//===========================================================================
VOID DriverUnLoad( PDRIVER_OBJECT pDriverObj ) {
	PDEVICE_EXT pDeviceExt = NULL;
	PDEVICE_OBJECT pNextDeviceObj = NULL;

	pNextDeviceObj = pDriverObj->DeviceObject;

	while ( pNextDeviceObj  ) {
		pDeviceExt = pNextDeviceObj->DeviceExtension;

		IoDetachDevice( pDeviceExt->pLowDevice );

		IoDeleteDevice( pDeviceExt->pDeviceObj );

		IoDeleteSymbolicLink( &pDeviceExt->USzSysLinkName );

		KdPrint( ( "删除%wZ设备成功!\n", &pDeviceExt->USzDeviceName ) );

		pNextDeviceObj = pNextDeviceObj->NextDevice;
	}
}
//===========================================================================
//所有不关心的Irp处理
//===========================================================================
NTSTATUS DispatchRoutine( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
	NTSTATUS Status;
	PDEVICE_EXT pDeviceExt = NULL;

	pDeviceExt = pDeviceObj->DeviceExtension;

	//跳过当前堆栈
	IoSkipCurrentIrpStackLocation( pIrp );

	//调用下层驱动
	Status = IoCallDriver( pDeviceExt->pLowDevice, pIrp );

	KdPrint( ( "不关心的Irp来了一次!\n" ) );
	return Status;

}

//===========================================================================
//读取请求的完成例程
//===========================================================================
NTSTATUS CompletionRead( PDEVICE_OBJECT pDeviceObj, PIRP pIrp, PVOID pContext ) {
	ULONG ulReadLen;
	PIO_STACK_LOCATION pStack = NULL;
	PDEVICE_EXT pDeviceExt = NULL;

	_asm int 3;
	pStack = IoGetCurrentIrpStackLocation( pIrp );

	pDeviceExt = pDeviceObj->DeviceExtension;
	ulReadLen = pStack->Parameters.Read.Length;

	//再次将数据改写
	RtlFillMemory( pIrp->AssociatedIrp.SystemBuffer, ulReadLen, 'c' );

	//进入此函数标志底层驱动设备将IRP完成
	KdPrint( ( "Completion:读取请求的完成例程\n" ) );
	if ( pIrp->PendingReturned ) {
		//传播pending位
		IoMarkIrpPending( pIrp );
	}

	//如果想分发函数继续获得控制器那么返回
	//STATUS_MORE_PROCESSING_REQUIRED
	return STATUS_CONTINUE_COMPLETION;

}
//===========================================================================
//读取请求处理例程
//===========================================================================
NTSTATUS DispatchRead( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
	NTSTATUS Status;
	PDEVICE_EXT pDeviceExt = NULL;

	_asm int 3;
	pDeviceExt = pDeviceObj->DeviceExtension;

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

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

	//调用底层驱动程序(只要调用了函数就不能够去操作Irp
	Status = IoCallDriver( pDeviceExt->pLowDevice, pIrp );

	if ( Status == STATUS_PENDING ) {
		KdPrint( ( "Completion: DispatchRead->Irp被挂起!\n" ) );
	}

	KdPrint( ( "Completion:DispatchRead来了一次\n" ) );
	return Status;
}
//===========================================================================
//写入请求处理例程
//===========================================================================
NTSTATUS DispatchWrite( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
	ULONG uWriteLen;
	NTSTATUS Status;
	PDEVICE_EXT pDeviceExt = NULL;
	PIO_STACK_LOCATION pStack = NULL;

	pStack = IoGetCurrentIrpStackLocation( pIrp );

	uWriteLen = pStack->Parameters.Write.Length;
	pDeviceExt = pDeviceObj->DeviceExtension;

	//将用户层写入的数据修改掉
	RtlFillMemory( pIrp->AssociatedIrp.SystemBuffer, uWriteLen, 'b' );

	//跳过当前堆栈
	IoSkipCurrentIrpStackLocation( pIrp );

	Status = IoCallDriver( pDeviceExt->pLowDevice, pIrp );
	return Status;
}
//===========================================================================
//驱动程序入口
//===========================================================================
NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pUSzRegPath ) {
	NTSTATUS Status;
	ULONG i;
	PDEVICE_EXT pDeviceExt = NULL;
	PFILE_OBJECT pTargetFileObj = NULL;
	PDEVICE_OBJECT pTargetDevice = NULL;
	PDEVICE_OBJECT pLowDevice = NULL;
	PDEVICE_OBJECT pDeviceObj = NULL;
	UNICODE_STRING USzDeviceName = RTL_CONSTANT_STRING( DEVICE_NAME );
	UNICODE_STRING USzSysLinkName = RTL_CONSTANT_STRING( SYSLINK_NAME );
	UNICODE_STRING USzTargetDeviceName = RTL_CONSTANT_STRING( TARGET_DEVICE_NAME );

	for( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++ ) {
		pDriverObj->MajorFunction[i] = &DispatchRoutine;
	}
	pDriverObj->MajorFunction[IRP_MJ_WRITE] = &DispatchWrite;
	pDriverObj->MajorFunction[IRP_MJ_READ] = &DispatchRead;
	pDriverObj->DriverUnload = &DriverUnLoad;
//---------------------------------------------------------------------------
	do {
		_asm int 3;
		Status = IoCreateDevice( pDriverObj, sizeof( DEVICE_EXT ),
					 &USzDeviceName, FILE_DEVICE_UNKNOWN, 0,
					 TRUE, &pDeviceObj );
		if ( !NT_SUCCESS( Status ) ) {
			KdPrint( ( "创建设备失败!\n" ) );
			break;
		}

		Status = IoCreateSymbolicLink( &USzSysLinkName, &USzDeviceName );
		if ( !NT_SUCCESS( Status ) ) {
			KdPrint( ( "创建符号链接失败!\n" ) );
			break;
		}

		//获取目标设备的设备对象
		Status = IoGetDeviceObjectPointer( &USzTargetDeviceName, FILE_ALL_ACCESS,
						   &pTargetFileObj, &pTargetDevice );
		if ( !NT_SUCCESS( Status ) ) {
			KdPrint( ( "获取目标设备失败!\n" ) );
			break;
		}

		//将自己的设备对象挂载在目标设备对象上
		pLowDevice = IoAttachDeviceToDeviceStack( pDeviceObj, pTargetDevice );
		if ( !pLowDevice ) {
			KdPrint( ( "设备挂载失败!\n" ) );
			Status = STATUS_UNSUCCESSFUL;
			break;
		}

		//设置设备类型, 属性
		pDeviceObj->DeviceType = pLowDevice->DeviceType;
		pDeviceObj->Characteristics = pLowDevice->DeviceType;
		//启动设备
		pDeviceObj->Flags &= ~DO_DEVICE_INITIALIZING;
		//设置缓冲区操作方式
		pDeviceObj->Flags |= pLowDevice->Flags & ( DO_BUFFERED_IO | DO_DIRECT_IO );
		//设置设备扩展
		pDeviceExt = pDeviceObj->DeviceExtension;
		pDeviceExt->pDeviceObj = pDeviceObj;
		pDeviceExt->pLowDevice = pLowDevice;
		pDeviceExt->USzDeviceName = USzDeviceName;
		pDeviceExt->USzSysLinkName = USzSysLinkName;

		if ( pTargetFileObj ) {
			ObDereferenceObject( pTargetFileObj );
			pTargetFileObj = NULL;
		}
		KdPrint( ( "设备绑定成功!\n" ) );

	} while ( FALSE );
//---------------------------------------------------------------------------
	if ( !NT_SUCCESS( Status ) ) {
		if ( pDeviceObj ) {
			IoDeleteSymbolicLink( &USzSysLinkName );
			IoDeleteDevice( pDeviceObj );
		}

		//这边要卸载的是文件对象
		if ( pTargetFileObj ) {
			ObDereferenceObject( pTargetFileObj );
		}
	}
	return Status;
}

 

发表评论

您必须登录 才能进行评论。