忆杰的博客

忆杰的博客

驱动程序调用驱动程序2

上一篇说到内核里面用户态这边差不多的函数, 比如ZwCreateFile, ZwReadFile什么之类的, 内核里面其实还有些更加暴力的东西存在的, 比如说用户态这边打开设备只有一个句柄, 但是内核里面是可以直接触摸到指针的, 用户态那边只可以调用几个固定的函数来发送Irp. 那么内核里面可以自定义Irp的发送, 非常的暴力, 好用!
 

首先我们也不用去调用什么ZwCreateFile了, 直接用这个IoGetDeviceObjectPointer函数, 这个函数可以通过设备名获得文件对象指针, 和设备对象指针, 指针啊, 不是句柄了..和ZwCreateFile一样, 末尾记得调用ObDereferenceObject函数, 来解引用.

打开以后就是发送Irp消息了, 基本上有很多的选择, 看下面代码就知道了, 有同步的又异步的, 非常的暴力好用, 看自己喜欢了. 其实看下面代码就知道了, 同步的和异步的其实区别不是很大, 无论如何还是需要一个同步事件来同步的. 只是放在了不同的地方了. 稍微搞一下就明白了, 这个很简单的嘛.

重点说说这个, ObReferenceObjectByName这号函数是未公开的函数, 一般未公开的函数就是暴力啊, 其可以获得各类对象的指针, 而不是和IoGetDeviceObjectPointer这样只能够获取设备对象的指针, ObReferenceObjectByName可以获取各种对象的指针, 非常好用了. ObReferenceObjectByName只是引用下对象的指针, 并没有打开操作, 但是IoGetDeviceObjectPointer内部是有打开操作的. 也就是相当于向设备发送了Irp_Mj_Create的Irp. 明白了他们之间的区别, 那么我们就可以适时的在二者中选择某个函数来做一些事情了.

后面还有一个模拟IoGetDeviceObjectPointer内核函数的实现, 首先要打开设备对象, 获得设备对象的句柄,然后ObReferenceObjectByHandle内核函数将设备对象的句柄, 转换设备对象相关的文件对象的指针. 然后再调用IoGetBaseFileSystemDeviceObject函数, 可以将文件对象指针得到设备对象针! 有点晕了. 主要要把握住, 设备, 文件, 句柄. 这是关键字!

下面就上代码, 测试驱动已经在上一篇中说到了, 那么这边看看代码的情况:
 

/*
	Windows 内核下驱动程序通过设备指针调用其他驱动程序 调用驱动程序
	编译方法参见makefile. TAB = 8
*/
#include <ntddk.h>

//===========================================================================
#define SYSLINK_NAME	L"\\??\\SysLinkTestDriver"

#define MAX_PATH	260

#ifdef __cplusplus
extern "C"
{
#endif
#include <NTDDK.h>

	NTKERNELAPI
	NTSTATUS
	ObReferenceObjectByName(
		IN PUNICODE_STRING ObjectName,
		IN ULONG Attributes,
		IN PACCESS_STATE PassedAccessState OPTIONAL,
		IN ACCESS_MASK DesiredAccess OPTIONAL,
		IN POBJECT_TYPE ObjectType,
		IN KPROCESSOR_MODE AccessMode,
		IN OUT PVOID ParseContext OPTIONAL,
		OUT PVOID *Object
	);
	NTKERNELAPI
	PDEVICE_OBJECT
	NTAPI
	IoGetBaseFileSystemDeviceObject (
		IN PFILE_OBJECT FileObject
	);
	extern POBJECT_TYPE IoDeviceObjectType;
#ifdef __cplusplus
}
#endif

//===========================================================================
//模拟系统内部IoGetDeviceObjectPointer实现
//===========================================================================
_IoGetDeviceObjectPointer( PUNICODE_STRING pObjName,  ACCESS_MASK DesiredAccess,
			  PFILE_OBJECT* ppFileObj, PDEVICE_OBJECT* ppDeviceObj  ) {
	HANDLE hDevice = NULL;
	NTSTATUS Status;
	PFILE_OBJECT pFileObj = NULL;
	OBJECT_ATTRIBUTES ObjAttr;
	IO_STATUS_BLOCK Status_block;

	//初始化要打开的设备名称
	InitializeObjectAttributes( &ObjAttr, pObjName, OBJ_KERNEL_HANDLE, NULL, NULL );

	//打开设备 对象
	Status = ZwOpenFile( &hDevice, DesiredAccess, &ObjAttr, &Status_block,
		0, FILE_NON_DIRECTORY_FILE );
	if ( !NT_SUCCESS( Status ) ) {
		return Status;
	}

	//通过设备对象句柄得到设备对象指针
	Status = ObReferenceObjectByHandle( hDevice, 0, *IoFileObjectType,
		KernelMode, &pFileObj, NULL );
	if ( !NT_SUCCESS( Status ) ) {
		return Status;
	}

	*ppFileObj = pFileObj;

	//通过设备相关文件对象指针得到设备对象指针
	*ppDeviceObj = IoGetBaseFileSystemDeviceObject( pFileObj );

	if ( hDevice ) {
		ZwClose(hDevice);
	}
	return STATUS_SUCCESS;
}
//===========================================================================
//打开设备
//===========================================================================
NTSTATUS OpenDevice() {
	NTSTATUS Status;
	HANDLE hSysLink;
	HANDLE hDevice = NULL;
	ULONG ulStrLen, i;
	UCHAR ucBuf[10];
	KEVENT Event;
	PIRP pNewIrp = NULL;
	OBJECT_ATTRIBUTES ObjAttr;
	UNICODE_STRING USzDeviceName = {0};
	PIO_STACK_LOCATION pStack = NULL;
	IO_STATUS_BLOCK Status_Block;
	LARGE_INTEGER liOffset;
	PDEVICE_OBJECT pDevice = NULL;
	PFILE_OBJECT pFileObj = NULL;
	UNICODE_STRING USzDeviceLinkName = RTL_CONSTANT_STRING( SYSLINK_NAME );

	do {
		//这边是内核的对象, 加了个内核属性
		InitializeObjectAttributes( &ObjAttr, &USzDeviceLinkName,
					    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL );

		//获取符号链接句柄
		Status = ZwOpenSymbolicLinkObject( &hSysLink, FILE_ALL_ACCESS, &ObjAttr );
		if ( !NT_SUCCESS( Status ) ) {
			KdPrint( ( "打开符号链接失败!\n" ) );
			break;
		}

		USzDeviceName.Buffer = ExAllocatePool( PagedPool, MAX_PATH );
		USzDeviceName.MaximumLength = MAX_PATH;
		ASSERT( USzDeviceName.Buffer );

		//通过符号名称得到设备名称
		Status = ZwQuerySymbolicLinkObject( hSysLink, &USzDeviceName, &ulStrLen );
		if ( !NT_SUCCESS( Status ) ) {
			KdPrint( ( "符号名称转换成链接名称失败!\n" ) );
			break;
		}

		KdPrint( ( "设备名称%wZ\n", &USzDeviceName ) );

		//这个打开设备比ZwCreateFile好多了, 参数简单,
		//还可以直接获取到文件对象指针, 设备对象指针
		//这边还模拟了一个系统这个部分的实现.
		Status = _IoGetDeviceObjectPointer( &USzDeviceName, FILE_ALL_ACCESS,
						   &pFileObj, &pDevice );
		if ( !NT_SUCCESS( Status ) ) {
			KdPrint( ( "设备名转文件对象指针失败!\n" ) );
			break;
		}

//---------------------------------------------------------------------------
		//创建同步IRP写入请求, 这里的话, 其实同步和异步差不多
//---------------------------------------------------------------------------
		liOffset.QuadPart = 0i64;
		KeInitializeEvent( &Event, NotificationEvent, FALSE );
		RtlFillMemory( ucBuf, sizeof( ucBuf ), 'a' );

		//创建同步IRP请求
		pNewIrp = IoBuildSynchronousFsdRequest( IRP_MJ_WRITE, pDevice, &ucBuf[0],
							sizeof( ucBuf ), &liOffset,
							&Event, &Status_Block );

		//得到下一层的I/O堆栈(如果驱动不是只有一层, 应该怎么做呢?)
		pStack = IoGetNextIrpStackLocation( pNewIrp );

		//设置I/O堆栈的文件对象指针
		pStack->FileObject = pFileObj;

		//调用下一层驱动
		Status = IoCallDriver( pDevice, pNewIrp );
		if ( !NT_SUCCESS( Status ) ) {
			KdPrint( ( "调用驱动失败!\n" ) );
			break;
		}

		if ( Status == STATUS_PENDING ) {

			KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );

			Status = Status_Block.Status;

			if ( NT_SUCCESS( Status_Block.Status ) ) {
				KdPrint( ( "写入设备成功, 总共写入了%d字节!\n",
					   Status_Block.Information ) );
			} else {
				KdPrint( ( "写入设备失败!\n" ) );
				break;
			}
		}
//---------------------------------------------------------------------------
		//创建异步Irp读取请求,
//---------------------------------------------------------------------------
		pNewIrp = NULL;
		pStack = NULL;
		KeResetEvent( &Event );
		liOffset.QuadPart = 0i64;
		RtlZeroBytes( &ucBuf[0], sizeof( ucBuf ) );

		//创建异步Irp
		pNewIrp = IoBuildAsynchronousFsdRequest( IRP_MJ_READ, pDevice, &ucBuf[0],
				sizeof( ucBuf ), &liOffset, &Status_Block );

		//Irp完成后会通过这里通知这边
		pNewIrp->UserEvent = &Event;

		//获取下一层I/O堆栈
		pStack = IoGetNextIrpStackLocation( pNewIrp );

		pStack->FileObject = pFileObj;

		Status = IoCallDriver( pDevice, pNewIrp );
		if ( !NT_SUCCESS( Status ) ) {
			KdPrint( ( "读取设备请求失败!" ) );
			break;
		}

		if ( Status == STATUS_PENDING ) {
			KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
			Status = Status_Block.Status;

			if ( NT_SUCCESS( Status_Block.Status ) ) {

				KdPrint( ( "读取设备成功!\n" ) );

				for( i = 0; i < Status_Block.Information; i++ ) {
					KdPrint( ( "%c\t", ucBuf[i] ) );
				}
				KdPrint( ( "\n" ) );

			} else {
				KdPrint( ( "读取设备失败!\n" ) );
				break;
			}
		}
//---------------------------------------------------------------------------
		//手工创建Irp
//---------------------------------------------------------------------------
		pNewIrp = NULL;
		pStack = NULL;
		KeResetEvent( &Event );
		liOffset.QuadPart = 0i64;
		RtlZeroBytes( &ucBuf[0], sizeof( ucBuf ) );

		//这个创建出来的是最原始的IRP
		pNewIrp = IoAllocateIrp( pDevice->StackSize, FALSE );

		//设置同步事件
		pNewIrp->UserEvent = &Event;
		pNewIrp->UserIosb = &Status_Block;

		//填写新Irp的线程号
		pNewIrp->Tail.Overlay.Thread = PsGetCurrentThread();

		//填写缓冲区
		pNewIrp->AssociatedIrp.SystemBuffer = ucBuf;

		//获取下一层堆栈
		pStack = IoGetNextIrpStackLocation( pNewIrp );

		//填写功能号
		pStack->MajorFunction = IRP_MJ_READ;
		pStack->MinorFunction = IRP_MN_NORMAL;

		//填写文件对象
		pStack->FileObject = pFileObj;

		//填写缓冲区长度和偏移
		pStack->Parameters.Read.Length = sizeof( ucBuf );
		pStack->Parameters.Read.ByteOffset = liOffset;

		Status = IoCallDriver( pDevice, pNewIrp );
		if ( !NT_SUCCESS( Status ) ) {
			KdPrint( ( "Irp发送失败!\n" ) );
			break;
		}

		if ( Status == STATUS_PENDING  ) {
			KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );

			Status = Status_Block.Status;

			if ( NT_SUCCESS( Status_Block.Status ) ) {

				KdPrint( ( "读取设备成功!\n" ) );

				for( i = 0; i < Status_Block.Information; i++ ) {
					KdPrint( ( "%c\t", ucBuf[i] ) );
				}
				KdPrint( ( "\n" ) );

			} else {
				KdPrint( ( "读取设备失败!\n" ) );
				break;
			}

		}

	} while ( FALSE );
//---------------------------------------------------------------------------
	if ( USzDeviceName.Buffer ) {
		ExFreePool( USzDeviceName.Buffer );
	}
	if ( hSysLink ) {
		ZwClose( hSysLink );
	}
	if ( pFileObj ) {
		ObDereferenceObject( pFileObj );
	}

	return Status;
}

//===========================================================================
//驱动入口
//===========================================================================
NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pUSzRegPath ) {
	NTSTATUS Status;
	PDEVICE_OBJECT pDevice = NULL;
	PFILE_OBJECT pFileObj = NULL;
	UNICODE_STRING USzDeviceName = RTL_CONSTANT_STRING( SYSLINK_NAME );

	//这号函数可以通过名字获取设备指针, 可以获取各种对象的指针(事件, 互斥量等)
	Status = ObReferenceObjectByName( &USzDeviceName, OBJ_CASE_INSENSITIVE,
		NULL, FILE_ALL_ACCESS, IoDeviceObjectType,
					  KernelMode, NULL, &pDevice );
	if ( !NT_SUCCESS( Status ) ) {
		KdPrint( ( "通过名称获取对象指针失败!\n" ) );
		return Status;
	} else {
		KdPrint( ( "通过名称获取对象指针成功!\n" ) );
	}

	if ( pDevice ) {
		ObDereferenceObject( pDevice );
		pDevice = NULL;
	}
	Status = OpenDevice();
	if ( !NT_SUCCESS( Status ) ) {
		KdPrint( ( "操作设备失败!\n" ) );
		return Status;
	} else {
		KdPrint( ( "操作设备成功!\n" ) );
	}

	return STATUS_UNSUCCESSFUL;
}

发表评论

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