首页
社区
课程
招聘
[求助]哪位大佬帮忙瞧一眼, 我的键盘驱动卸载后蓝屏
发表于: 2017-2-13 00:12 4581

[求助]哪位大佬帮忙瞧一眼, 我的键盘驱动卸载后蓝屏

2017-2-13 00:12
4581
寒江独钓第四章的例子, 驱动在卸载后就蓝屏了, WIN7 64位虚拟机里面用DriverMonimor加载的, 另外, 我跟踪调试发现驱动在卸载函数最后的右大括号执行完了之后蓝屏的, 在网上找了一个星期了, 还是没找到原因, 求大佬帮帮忙

///////////////////////////////////////////////////////////////////////////////
///
/// Copyright (c) 2017 - <company name here>
///
/// Original filename: KbdclassFilterTest.cpp
/// Project          : KbdclassFilterTest
/// Date of creation : 2017-02-12
/// Author(s)        : <author name(s)>
///
/// Purpose          : <description>
///
/// Revisions:
///  0000 [2017-02-12] Initial revision.
///
///////////////////////////////////////////////////////////////////////////////

// $Id$

#ifdef __cplusplus
extern "C" {
#endif
#include <ntddk.h>
#include <string.h>
#ifdef __cplusplus
}; // extern "C"
#endif

#include "KbdclassFilterTest.h"

#ifdef __cplusplus
namespace { // anonymous namespace to limit the scope of this global variable!
#endif
PDRIVER_OBJECT pdoGlobalDrvObj = 0;
#ifdef __cplusplus
}; // anonymous namespace
#endif





#include <wdm.h>

// Kbdclass驱动的名字
#define KBD_DRIVER_NAME  L"\\Driver\\Kbdclass"

typedef struct _C2P_DEV_EXT 
{ 
	// 这个结构的大小
	ULONG NodeSize; 
	// 过滤设备对象
	PDEVICE_OBJECT pFilterDeviceObject;
	// 同时调用时的保护锁
	KSPIN_LOCK IoRequestsSpinLock;
	// 进程间同步处理  
	KEVENT IoInProgressEvent; 
	// 绑定的设备对象
	PDEVICE_OBJECT TargetDeviceObject; 
	// 绑定前底层设备对象
	PDEVICE_OBJECT LowerDeviceObject; 
} C2P_DEV_EXT, *PC2P_DEV_EXT;

NTSTATUS 
c2pDevExtInit( 
			  IN PC2P_DEV_EXT devExt, 
			  IN PDEVICE_OBJECT pFilterDeviceObject, 
			  IN PDEVICE_OBJECT pTargetDeviceObject, 
			  IN PDEVICE_OBJECT pLowerDeviceObject ) 
{ 
	memset(devExt, 0, sizeof(C2P_DEV_EXT)); 
	devExt->NodeSize = sizeof(C2P_DEV_EXT); 
	devExt->pFilterDeviceObject = pFilterDeviceObject; 
	KeInitializeSpinLock(&(devExt->IoRequestsSpinLock)); 
	KeInitializeEvent(&(devExt->IoInProgressEvent), NotificationEvent, FALSE); 
	devExt->TargetDeviceObject = pTargetDeviceObject; 
	devExt->LowerDeviceObject = pLowerDeviceObject; 
	return( STATUS_SUCCESS ); 
}

// 这个函数是事实存在的,只是文档中没有公开。声明一下
// 就可以直接使用了。
extern "C"
NTSTATUS
ObReferenceObjectByName(
						PUNICODE_STRING ObjectName,
						ULONG Attributes,
						PACCESS_STATE AccessState,
						ACCESS_MASK DesiredAccess,
						POBJECT_TYPE ObjectType,
						KPROCESSOR_MODE AccessMode,
						PVOID ParseContext,
						PVOID *Object
						);

extern "C" POBJECT_TYPE * IoDriverObjectType;
ULONG gC2pKeyCount = 0;
PDRIVER_OBJECT gDriverObject = NULL;

// 这个函数经过改造。能打开驱动对象Kbdclass,然后绑定
// 它下面的所有的设备:
NTSTATUS 
c2pAttachDevices( 
				 IN PDRIVER_OBJECT DriverObject, 
				 IN PUNICODE_STRING RegistryPath 
				 ) 
{ 
	NTSTATUS status = 0; 
	UNICODE_STRING uniNtNameString; 
	PC2P_DEV_EXT devExt; 
	PDEVICE_OBJECT pFilterDeviceObject = NULL; 
	PDEVICE_OBJECT pTargetDeviceObject = NULL; 
	PDEVICE_OBJECT pLowerDeviceObject = NULL; 

	PDRIVER_OBJECT KbdDriverObject = NULL; 

	KdPrint(("MyAttach\n")); 

	// 初始化一个字符串,就是Kdbclass驱动的名字。
	RtlInitUnicodeString(&uniNtNameString, KBD_DRIVER_NAME); 
	// 请参照前面打开设备对象的例子。只是这里打开的是驱动对象。
	status = ObReferenceObjectByName ( 
		&uniNtNameString, 
		OBJ_CASE_INSENSITIVE, 
		NULL, 
		0, 
		*IoDriverObjectType, 
		KernelMode, 
		NULL, 
		(PVOID *)&KbdDriverObject 
		); 
	// 如果失败了就直接返回
	if(!NT_SUCCESS(status)) 
	{ 
		KdPrint(("MyAttach: Couldn't get the MyTest Device Object\n")); 
		return( status ); 
	}
	else
	{
		// 这个打开需要解应用。早点解除了免得之后忘记。
		ObDereferenceObject(DriverObject);
	}

	// 这是设备链中的第一个设备	
	pTargetDeviceObject = KbdDriverObject->DeviceObject;
	// 现在开始遍历这个设备链
	while (pTargetDeviceObject) 
	{
		// 生成一个过滤设备,这是前面读者学习过的。这里的IN宏和OUT宏都是
		// 空宏,只有标志性意义,表明这个参数是一个输入或者输出参数。
		status = IoCreateDevice( 
			IN DriverObject, 
			IN sizeof(C2P_DEV_EXT), 
			IN NULL, 
			IN pTargetDeviceObject->DeviceType, 
			IN pTargetDeviceObject->Characteristics, 
			IN FALSE, 
			OUT &pFilterDeviceObject 
			); 

		// 如果失败了就直接退出。
		if (!NT_SUCCESS(status)) 
		{ 
			KdPrint(("MyAttach: Couldn't create the MyFilter Filter Device Object\n")); 
			return (status); 
		} 

		// 绑定。pLowerDeviceObject是绑定之后得到的下一个设备。也就是
		// 前面常常说的所谓真实设备。
		pLowerDeviceObject = 
			IoAttachDeviceToDeviceStack(pFilterDeviceObject, pTargetDeviceObject); 
		// 如果绑定失败了,放弃之前的操作,退出。
		if(!pLowerDeviceObject) 
		{ 
			KdPrint(("MyAttach: Couldn't attach to MyTest Device Object\n")); 
			IoDeleteDevice(pFilterDeviceObject); 
			pFilterDeviceObject = NULL; 
			return( status ); 
		} 

		// 设备扩展!下面要详细讲述设备扩展的应用。
		devExt = (PC2P_DEV_EXT)(pFilterDeviceObject->DeviceExtension); 
		c2pDevExtInit( 
			devExt, 
			pFilterDeviceObject, 
			pTargetDeviceObject, 
			pLowerDeviceObject ); 

		// 下面的操作和前面过滤串口的操作基本一致。这里不再解释了。
		pFilterDeviceObject->DeviceType=pLowerDeviceObject->DeviceType; 
		pFilterDeviceObject->Characteristics=pLowerDeviceObject->Characteristics; 
		pFilterDeviceObject->StackSize=pLowerDeviceObject->StackSize+1; 
		pFilterDeviceObject->Flags |= pLowerDeviceObject->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE) ; 
		//next device 
		pTargetDeviceObject = pTargetDeviceObject->NextDevice;
	}
	return status; 
} 

VOID 
c2pDetach(IN PDEVICE_OBJECT pDeviceObject) 
{ 
	PC2P_DEV_EXT devExt; 
	BOOLEAN NoRequestsOutstanding = FALSE; 
	devExt = (PC2P_DEV_EXT)pDeviceObject->DeviceExtension; 
	__try 
	{ 
		__try 
		{ 
			IoDetachDevice(devExt->TargetDeviceObject);
			devExt->TargetDeviceObject = NULL; 
			IoDeleteDevice(pDeviceObject); 
			devExt->pFilterDeviceObject = NULL; 
			DbgPrint(("Detach Finished\n")); 
		} 
		__except (EXCEPTION_EXECUTE_HANDLER){} 
	} 
	__finally{} 
	return; 
}


#define  DELAY_ONE_MICROSECOND  (-10)
#define  DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)
#define  DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000)

VOID 
c2pUnload(IN PDRIVER_OBJECT DriverObject) 
{ 
	PDEVICE_OBJECT DeviceObject; 
	PDEVICE_OBJECT OldDeviceObject; 
	PC2P_DEV_EXT devExt; 

	LARGE_INTEGER	lDelay;
	PRKTHREAD CurrentThread;
	//delay some time 
	lDelay = RtlConvertLongToLargeInteger(100 * DELAY_ONE_MILLISECOND);
	CurrentThread = KeGetCurrentThread();
	// 把当前线程设置为低实时模式,以便让它的运行尽量少影响其他程序。
	KeSetPriorityThread(CurrentThread, LOW_REALTIME_PRIORITY);

	UNREFERENCED_PARAMETER(DriverObject); 
	KdPrint(("DriverEntry unLoading...\n")); 

	// 遍历所有设备并一律解除绑定
	DeviceObject = DriverObject->DeviceObject;
	while (DeviceObject)
	{
		// 解除绑定并删除所有的设备
		c2pDetach(DeviceObject);
		DeviceObject = DeviceObject->NextDevice;
	} 
	ASSERT(NULL == DriverObject->DeviceObject);

	while (gC2pKeyCount)
	{
		KeDelayExecutionThread(KernelMode, FALSE, &lDelay);
	}
	KdPrint(("DriverEntry unLoad OK!\n")); 
	return; 
} 

NTSTATUS c2pDispatchGeneral( 
							IN PDEVICE_OBJECT DeviceObject, 
							IN PIRP Irp 
							) 
{ 
	// 其他的分发函数,直接skip然后用IoCallDriver把IRP发送到真实设备
	// 的设备对象。 
	KdPrint(("Other Diapatch!")); 
	IoSkipCurrentIrpStackLocation(Irp); 
	return IoCallDriver(((PC2P_DEV_EXT)
		DeviceObject->DeviceExtension)->LowerDeviceObject, Irp); 
} 

NTSTATUS c2pPower( 
				  IN PDEVICE_OBJECT DeviceObject, 
				  IN PIRP Irp 
				  ) 
{ 
	PC2P_DEV_EXT devExt;
	devExt =
		(PC2P_DEV_EXT)DeviceObject->DeviceExtension; 

	PoStartNextPowerIrp( Irp ); 
	IoSkipCurrentIrpStackLocation( Irp ); 
	return PoCallDriver(devExt->LowerDeviceObject, Irp ); 
} 

NTSTATUS c2pPnP( 
				IN PDEVICE_OBJECT DeviceObject, 
				IN PIRP Irp 
				) 
{ 
	PC2P_DEV_EXT devExt; 
	PIO_STACK_LOCATION irpStack; 
	NTSTATUS status = STATUS_SUCCESS; 
	KIRQL oldIrql; 
	KEVENT event; 

	// 获得真实设备。
	devExt = (PC2P_DEV_EXT)(DeviceObject->DeviceExtension); 
	irpStack = IoGetCurrentIrpStackLocation(Irp); 

	switch (irpStack->MinorFunction) 
	{ 
	case IRP_MN_REMOVE_DEVICE: 
		KdPrint(("IRP_MN_REMOVE_DEVICE\n")); 

		// 首先把请求发下去
		IoSkipCurrentIrpStackLocation(Irp); 
		IoCallDriver(devExt->LowerDeviceObject, Irp); 
		// 然后解除绑定。
		IoDetachDevice(devExt->LowerDeviceObject); 
		// 删除我们自己生成的虚拟设备。
		IoDeleteDevice(DeviceObject); 
		status = STATUS_SUCCESS; 
		break; 

	default: 
		// 对于其他类型的IRP,全部都直接下发即可。 
		IoSkipCurrentIrpStackLocation(Irp); 
		status = IoCallDriver(devExt->LowerDeviceObject, Irp); 
	} 
	return status; 
}

// 这是一个IRP完成回调函数的原型
NTSTATUS c2pReadComplete( 
						 IN PDEVICE_OBJECT DeviceObject, 
						 IN PIRP Irp, 
						 IN PVOID Context 
						 ) 
{
	PIO_STACK_LOCATION IrpSp;
	ULONG buf_len = 0;
	PUCHAR buf = NULL;
	size_t i;

	IrpSp = IoGetCurrentIrpStackLocation( Irp );

	//  如果这个请求是成功的。很显然,如果请求失败了,这么获取
	//   进一步的信息是没意义的。
	if( NT_SUCCESS( Irp->IoStatus.Status ) ) 
	{
		// 获得读请求完成后输出的缓冲区
		buf = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;
		// 获得这个缓冲区的长度。一般的说返回值有多长都保存在
		// Information中。
		buf_len = (ULONG)Irp->IoStatus.Information;

		//… 这里可以做进一步的处理。我这里很简单的打印出所有的扫
		// 描码。
		for(i=0;i<buf_len;++i)
		{
			DbgPrint("ctrl2cap: %2x\r\n", buf[i]);
		}
	}
	gC2pKeyCount--;

	if( Irp->PendingReturned )
	{ 
		IoMarkIrpPending( Irp ); 
	} 
	return Irp->IoStatus.Status;
}


NTSTATUS c2pDispatchRead( 
						 IN PDEVICE_OBJECT DeviceObject, 
						 IN PIRP Irp ) 
{ 
	NTSTATUS status = STATUS_SUCCESS; 
	PC2P_DEV_EXT devExt; 
	PIO_STACK_LOCATION currentIrpStack; 
	KEVENT waitEvent;
	KeInitializeEvent( &waitEvent, NotificationEvent, FALSE );

	if (Irp->CurrentLocation == 1) 
	{ 
		ULONG ReturnedInformation = 0; 
		KdPrint(("Dispatch encountered bogus current location\n")); 
		status = STATUS_INVALID_DEVICE_REQUEST; 
		Irp->IoStatus.Status = status; 
		Irp->IoStatus.Information = ReturnedInformation; 
		IoCompleteRequest(Irp, IO_NO_INCREMENT); 
		return(status); 
	} 

	// 全局变量键计数器加1
	gC2pKeyCount++;

	// 得到设备扩展。目的是之后为了获得下一个设备的指针。
	devExt =
		(PC2P_DEV_EXT)DeviceObject->DeviceExtension;

	// 设置回调函数并把IRP传递下去。 之后读的处理也就结束了。
	// 剩下的任务是要等待读请求完成。
	currentIrpStack = IoGetCurrentIrpStackLocation(Irp); 
	IoCopyCurrentIrpStackLocationToNext(Irp);
	IoSetCompletionRoutine( Irp, c2pReadComplete, 
		DeviceObject, TRUE, TRUE, TRUE ); 
	return  IoCallDriver( devExt->LowerDeviceObject, Irp ); 	
}

extern "C" NTSTATUS DriverEntry( 
					 IN PDRIVER_OBJECT DriverObject, 
					 IN PUNICODE_STRING RegistryPath 
					 ) 
{ 
	ULONG i; 
	NTSTATUS status; 
	KdPrint (("c2p.SYS: entering DriverEntry\n")); 

	// 填写所有的分发函数的指针
	for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) 
	{ 
		DriverObject->MajorFunction[i] = c2pDispatchGeneral; 
	} 

	// 单独的填写一个Read分发函数。因为要的过滤就是读取来的按键信息
	// 其他的都不重要。这个分发函数单独写。
	DriverObject->MajorFunction[IRP_MJ_READ] = c2pDispatchRead; 

	// 单独的填写一个IRP_MJ_POWER函数。这是因为这类请求中间要调用
	// 一个PoCallDriver和一个PoStartNextPowerIrp,比较特殊。
	DriverObject->MajorFunction [IRP_MJ_POWER] = c2pPower; 

	// 我们想知道什么时候一个我们绑定过的设备被卸载了(比如从机器上
	// 被拔掉了?)所以专门写一个PNP(即插即用)分发函数
	DriverObject->MajorFunction [IRP_MJ_PNP] = c2pPnP; 

	// 卸载函数。
	DriverObject->DriverUnload = c2pUnload; 
	gDriverObject = DriverObject;
	// 绑定所有键盘设备
	status =c2pAttachDevices(DriverObject, RegistryPath);

	return status; 
}


[培训]科锐逆向工程师培训第53期2025年7月8日开班!

收藏
免费 0
支持
分享
最新回复 (7)
雪    币: 1515
活跃值: (5997)
能力值: ( LV13,RANK:240 )
在线值:
发帖
回帖
粉丝
2
键盘驱动 一般不建议卸载。我猜测是 irp已经发下去了。但是还没有完成。此时你卸载了,如果IRP完成了。但找不到返回的处理例程 自然就蓝屏了
2017-2-13 09:28
0
雪    币: 19
活跃值: (1111)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
像这种驱动加载了就不需要去卸载了,重启卸载即可
2017-2-13 12:45
0
雪    币: 3
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
用IRP计数器也不行吗?每当一个IRP到来就++, IRP结束就--, 我的代码加了IRP计数器的
2017-2-13 18:27
0
雪    币: 8833
活跃值: (2419)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
5
你的计数器--操作需要在IRP完成时进行...你在IoMark前就--了...作死1
另外还有你的检测计数器需要在unload的一开始就进行...并且你需要确保Detch过程中不产生IRP——作死2
还有IRP操作里考虑一下Ext有效性啊啊?唉,太多问题了,真的不认直视——作死3+n+n
另外不要用++、--操作计数器,有巨大的bug!!原子性使用interlocked系列函数——作死N
2017-2-13 20:15
0
雪    币: 3
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
谢谢大佬,大佬可以推荐几本学习驱动开发的书吗?
2017-2-13 20:25
0
雪    币: 12876
活跃值: (9352)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
7
我这边内核函数 inline hook 用Interlocked调用前+1,调用后-1保证跳板内存不被提前释放 都经常出现卸载时变量>0卸载卡住的情况
2017-2-14 10:25
0
雪    币: 8833
活跃值: (2419)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
8
[QUOTE=hzqst;1463795]我这边内核函数 inline hook 用Interlocked调用前+1,调用后-1保证跳板内存不被提前释放 都经常出现卸载时变量>0卸载卡住的情况[/QUOTE]

不要用while(XXX>0)要用Interlocked系列的
不然会被优化出事儿
2017-2-14 12:33
0
游客
登录 | 注册 方可回帖
返回