Windows驱动开发学习记录-客户端与驱动交换数据之挂起IRP后客户端无法退出问题

  • 景背

最近学习TDI过滤,做一个简单的网络连接控制。当前的需求时驱动层进行了过滤,想把相应的过滤做成事件通知给应用层并显示。

  • 思路

没有特别好的方法,本人采用的思路是驱动创建一个设备进行通讯,客户启动后创建一个线程,该线程调用 DeviceIoControl,驱动层将该操作的IRP挂起,然后有数据时再将数据复制到IRP对应的Irp->AssociatedIrp.SystemBuffer中并完成操作。

  • 驱动关键的大致代码

相关定义 

#define IOCTL_CMD_REQUIRE_LOG	CTL_CODE(FILE_DEVICE_TDI_FW, 0x901, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_CMD_CLEAR_PENDDING 	CTL_CODE(FILE_DEVICE_TDI_FW, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_TRANSFER_TYPE(ioctl)	((ioctl) & 3)


typedef struct _RULE_COMMUNICATION_INFO
{
	ULONG nStructSize;
	GUID guid;
	WCHAR szRuleInfoName[128];
	WCHAR szRuleProcessPath[256];
	ULONG nAction;
	ULONG nProtocol;
	ULONG nDirection;
	ULONG nRequestType;
	ULONG ulAddressFrom;
	ULONG ulMaskFrom;
	USHORT uPortFrom;
	USHORT uPort2From;
	ULONG ulAddressTo;
	ULONG ulMaskTo;
	USHORT uPortTo;
	USHORT uPort2To;
	ULONGnLog;
	ULONG nResult;
	BOOLEAN bEnable;
}RULE_COMMUNICATION_INFO, * PRULE_COMMUNICATION_INFO;

typedef struct _LOG_IRP_LIST
{
	LIST_ENTRY listEntry;
	PIRP	 pIrp;
	HANDLE hPid;
}LOG_IRP_LIST, *PLOG_IRP_LIST;


PLOG_IRP_LIST g_pLogIrpList = NULL;
KSPIN_LOCK g_LogIrpListGuard;

挂起IRP 

NTSTATUS ProcessUserRequest(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{
	UNREFERENCED_PARAMETER(pDeviceObject);
	NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
	PIO_STACK_LOCATION pIosp = IoGetCurrentIrpStackLocation(pIrp);
	ULONG ulControlCode = pIosp->Parameters.DeviceIoControl.IoControlCode;
	ULONG ulInputBufferLength = pIosp->Parameters.DeviceIoControl.InputBufferLength;
	ULONG ulOutputBufferLength = pIosp->Parameters.DeviceIoControl.OutputBufferLength;
	PLOG_IRP_LIST pLogList = NULL;
	KIRQL irql = PASSIVE_LEVEL;
	switch (ulControlCode)
	{
	case IOCTL_CMD_REQUIRE_LOG:
		if (IOCTL_TRANSFER_TYPE(ulControlCode) == METHOD_NEITHER)
		{
			ntStatus = STATUS_INFO_LENGTH_MISMATCH;
			break;
		}
		if ((ulInputBufferLength < sizeof(RULE_COMMUNICATION_INFO)) ||
			((ulOutputBufferLength < sizeof(RULE_COMMUNICATION_INFO))))
		{
			ntStatus = STATUS_INFO_LENGTH_MISMATCH;
			break;
		}
		if (g_pLogIrpList == NULL)
		{
			ntStatus = STATUS_INSUFFICIENT_RESOURCES;
			break;
		}
		pLogList = (PLOG_IRP_LIST)ExAllocatePoolWithTag(
			NonPagedPool, sizeof(LOG_IRP_LIST), MmTagTypeLogIRPList);
		if (!pLogList)
		{
			KDPRINTEX("【TDIFilter】", "Allocate Log List Failed!\r\n");
			ntStatus = STATUS_INSUFFICIENT_RESOURCES;
			break;
		}
		pLogList->pIrp = pIrp;
		pLogList->hPid = PsGetCurrentProcessId();
🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟这里是关键🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟🠟
		IoSetCancelRoutine(pIrp, LogIRPCancelRoutine);
☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝☝
		KDPRINTEX("【TDIFilter】", "Pendding Log Irp \r\n");
		KeAcquireSpinLock(&g_LogIrpListGuard, &irql);
		InsertTailList(&g_pLogIrpList->listEntry, &pLogList->listEntry);
		KeReleaseSpinLock(&g_LogIrpListGuard, irql);
		IoMarkIrpPending(pIrp); 
		ntStatus = STATUS_PENDING;
		pIrp->IoStatus.Status = STATUS_PENDING;
		break;
    default:
        ntStatus = STATUS_NOT_SUPPORTED;
	   	break;
    }
    return ntStatus;    
}

相关任务完成为:

VOID LogToClient(PFILTER_REQUEST pRequest, RULE_ACTION_TYPE nAction)
{
	if (pRequest)
	{
		ULONG ulBufferSize = sizeof(RULE_COMMUNICATION_INFO);
		PRULE_COMMUNICATION_INFO pRuleCom =
			(PRULE_COMMUNICATION_INFO)ExAllocatePoolWithTag(
				NonPagedPool, ulBufferSize, MmTagTypeLogInfo);
		KIRQL irql = PASSIVE_LEVEL;
		if (pRuleCom)
		{
			RtlZeroMemory(pRuleCom, ulBufferSize);
			......

			while (!IsListEmpty(pListEntry))
			{
				PLIST_ENTRY pTemp = pListEntry->Flink;
				RemoveHeadList(pListEntry);
				PLOG_IRP_LIST pLogIrp = CONTAINING_RECORD(pTemp, LOG_IRP_LIST, listEntry);
				__try
				{
					if (pLogIrp)
					{
						if (pLogIrp->pIrp)
						{
							RtlCopyMemory(pLogIrp->pIrp->AssociatedIrp.SystemBuffer, pRuleCom, ulBufferSize);
							pLogIrp->pIrp->IoStatus.Information = ulBufferSize;
							pLogIrp->pIrp->IoStatus.Status = STATUS_SUCCESS;
							IoCompleteRequest(pLogIrp->pIrp, IO_NO_INCREMENT);
							KDPRINTEX("【TDIFilter】", "添加日志\r\n");
						}
						ExFreePoolWithTag(pLogIrp, MmTagTypeLogIRPList);
					}
				}
				__except (EXCEPTION_EXECUTE_HANDLER)
				{
					KDPRINTEX("【TDIFilter】", "IRP Exception \r\n");
				}
			}
			if (pRuleCom)
			{
				ExFreePoolWithTag(pRuleCom, MmTagTypeLogInfo);
			}
			KeReleaseSpinLock(&g_LogIrpListGuard, irql);
		}
		else
		{
			KDPRINTEX("【TDIFilter】", "ExAllocatePoolWithTag failed \r\n");
		}
	}
}
  • 实际遇到的问题

第二段代码中的关键部分在一开始并没有,也就是说开始没有设置IRP的取消例程,这样在客户端关闭时IRP还是处于挂起状态,导致客户端退不出后。

后来加上取消例程就就没有问题了 ,取消函数也简单,代码如下:

VOID LogIRPCancelRoutine(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{
	UNREFERENCED_PARAMETER(pDeviceObject);
    IoReleaseCancelSpinLock(pIrp->CancelIrql); 
	pIrp->IoStatus.Status = STATUS_CANCELLED;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	
}

写取消函数也遇到了点问题,详见:

Windows驱动开发学习记录-IRP取消例程问题

posted @ 2023-02-27 17:53  禁锢在时空之中的灵魂  阅读(59)  评论(0编辑  收藏  举报