Windows NT Driver >> Assembly
Cancel-Safe IRP Queue(CSQ)
參考資訊:
1. Four-F
2. Source Code
3. cancel-safe-irp-queues
Cancel-Safe IRP Queues(CSQ)是Microsoft提供的一個Cancel框架,這個框架概念就是讓使用者只專注在Cancel的資料處理上,而不是Cancel的同步處理上,畢竟每個人做出來的Cancel同步機制可能不同,但是原理是一樣的,因此,Microsoft提供了一個專門框架給驅動程式使用並且建議使用這個框架處理Cancel Irp,下圖是說明的圖片:
對於CSQ細節有興趣的使用者可以參考上面的參考資訊,而設定的Callback如下所示:
1. CsqInsertIrp
2. CsqRemoveIrp
3. CsqPeekNextIrp
4. CsqAcquireLock
5. CsqReleaseLock
6. CsqCompleteCanceledIrp
main.asm
.386p .model flat, stdcall option casemap:none include c:\masm32\include\w2k\hal.inc include c:\masm32\include\w2k\ntstatus.inc include c:\masm32\include\w2k\ntddk.inc include c:\masm32\include\w2k\ntoskrnl.inc include c:\masm32\include\w2k\ntddkbd.inc include c:\masm32\include\wxp\csq.inc include c:\masm32\Macros\Strings.mac includelib c:\masm32\lib\w2k\hal.lib includelib c:\masm32\lib\w2k\ntoskrnl.lib includelib c:\masm32\lib\wxp\i386\csq.lib public DriverEntry OurDeviceExtension struct pNextDev PDEVICE_OBJECT ? stQueue LIST_ENTRY <> stDPC KDPC <> stTime KTIMER <> stCsq IO_CSQ <> stLock KSPIN_LOCK <> OurDeviceExtension ends IOCTL_QUEUE equ CTL_CODE(FILE_DEVICE_UNKNOWN, 800h, METHOD_BUFFERED, FILE_ANY_ACCESS) IOCTL_PROCESS equ CTL_CODE(FILE_DEVICE_UNKNOWN, 801h, METHOD_BUFFERED, FILE_ANY_ACCESS) .const DEV_NAME word "\","D","e","v","i","c","e","\","M","y","D","r","i","v","e","r",0 SYM_NAME word "\","D","o","s","D","e","v","i","c","e","s","\","M","y","D","r","i","v","e","r",0 MSG_QUEUE byte "IOCTL_QUEUE",0 MSG_PROCESS byte "IOCTL_PROCESS",0 .code CsqInsertIrp proc uses ebx ecx pCsqInfo:PIO_CSQ, pIrp:PIRP local pdx:PTR DevExt invoke DbgPrint, $CTA0("CsqInsertIrp") ; CONTAINING_RECORD mov eax, pCsqInfo sub eax, OurDeviceExtension.stCsq mov pdx, eax mov ebx, pdx lea ebx, (OurDeviceExtension PTR [ebx]).stQueue mov ecx, pIrp lea ecx, (_IRP PTR [ecx]).Tail.Overlay.ListEntry InsertTailList ebx, ecx ret CsqInsertIrp endp CsqRemoveIrp proc pCsqInfo:PIO_CSQ, pIrp:PIRP invoke DbgPrint, $CTA0("CsqRemoveIrp") mov eax, pIrp lea eax, (_IRP PTR [eax]).Tail.Overlay.ListEntry RemoveEntryList eax ret CsqRemoveIrp endp CsqCompleteCanceledIrp proc pCsqInfo:PIO_CSQ, pIrp:PIRP local pdx:PTR OurDeviceExtension invoke DbgPrint, $CTA0("CsqCompleteCanceledIrp") ; CONTAINING_RECORD mov eax, pCsqInfo sub eax, OurDeviceExtension.stCsq mov pdx, eax mov eax, pIrp mov (_IRP PTR [eax]).IoStatus.Status, STATUS_CANCELLED and (_IRP PTR [eax]).IoStatus.Information, 0 fastcall IofCompleteRequest, eax, IO_NO_INCREMENT ret CsqCompleteCanceledIrp endp CsqPeekNextIrp proc uses ebx pCsqInfo:PIO_CSQ, pIrp:PIRP, pPeekContext:PVOID local pdx:PTR OurDeviceExtension local listHead:PTR LIST_ENTRY local nextEntry:PTR LIST_ENTRY local nextIrp:PTR _IRP local irpStack:PIO_STACK_LOCATION invoke DbgPrint, $CTA0("CsqPeekNextIrp") mov nextIrp, NULL ; CONTAINING_RECORD mov eax, pCsqInfo sub eax, OurDeviceExtension.stCsq mov pdx, eax mov eax, pdx lea eax, (OurDeviceExtension PTR [eax]).stQueue mov listHead, eax mov eax, pIrp .if eax == NULL mov eax, listHead push (LIST_ENTRY PTR [eax]).Flink pop nextEntry .elseif mov eax, pIrp push (_IRP PTR [eax]).Tail.Overlay.ListEntry.Flink pop nextEntry .endif mov eax, nextEntry mov ebx, listHead .while eax != ebx ; nextIrp = CONTAINING_RECORD(nextEntry, IRP, Tail.Overlay.ListEntry) mov eax, nextEntry sub eax, _IRP.Tail.Overlay.ListEntry mov nextIrp, eax IoGetCurrentIrpStackLocation nextIrp mov irpStack, eax mov eax, pPeekContext .if eax != NULL mov eax, irpStack mov eax, (IO_STACK_LOCATION PTR [eax]).FileObject mov ebx, pPeekContext .if eax == ebx .break .endif .elseif .break .endif mov nextIrp, NULL mov eax, nextEntry mov eax, (LIST_ENTRY PTR [eax]).Flink mov nextEntry, eax mov eax, nextEntry mov ebx, listHead .endw mov eax, nextIrp ret CsqPeekNextIrp endp CsqAcquireLock proc uses ecx pCsqInfo:PIO_CSQ, pIrql:PKIRQL local pdx:PTR OurDeviceExtension invoke DbgPrint, $CTA0("CsqAcquireLock") ; CONTAINING_RECORD mov eax, pCsqInfo sub eax, OurDeviceExtension.stCsq mov pdx, eax mov eax, pdx lea ecx, (OurDeviceExtension PTR [eax]).stLock fastcall KfAcquireSpinLock, ecx mov ecx, pIrql mov [ecx], al ret CsqAcquireLock endp CsqReleaseLock proc uses ecx edx pCsqInfo:PIO_CSQ, Irql:KIRQL local pdx:PTR DevExt ; CONTAINING_RECORD mov eax, pCsqInfo sub eax, OurDeviceExtension.stCsq mov pdx, eax mov dl, Irql mov eax, pdx lea ecx, (OurDeviceExtension PTR [eax]).stLock .if dl == DISPATCH_LEVEL fastcall KefReleaseSpinLockFromDpcLevel, ecx push eax invoke DbgPrint, $CTA0("CsqReleaseLock at DPC level\n") pop eax .else and edx, 0FFh fastcall KfReleaseSpinLock, ecx, edx push eax invoke DbgPrint, $CTA0("CsqReleaseLock at Passive level\n") pop eax .endif ret CsqReleaseLock endp OnTimer proc uses ebx pDpc:PKDPC, pContext:PVOID, pArg1:PVOID, PArg2:PVOID mov eax, pContext mov eax, (DEVICE_OBJECT PTR [eax]).DeviceExtension lea eax, (OurDeviceExtension PTR [eax]).stQueue IsListEmpty eax .if eax == TRUE mov eax, pContext mov eax, (DEVICE_OBJECT PTR [eax]).DeviceExtension invoke KeCancelTimer, addr (OurDeviceExtension PTR [eax]).stTime invoke DbgPrint, $CTA0("Finish") .else mov eax, pContext mov eax, (DEVICE_OBJECT PTR [eax]).DeviceExtension lea eax, (OurDeviceExtension PTR [eax]).stQueue RemoveHeadList eax ; CONTAINING_RECORD sub eax, _IRP.Tail.Overlay.ListEntry mov bl, (_IRP PTR [eax]).Cancel .if bl != TRUE mov (_IRP PTR [eax]).IoStatus.Status, STATUS_SUCCESS and (_IRP PTR [eax]).IoStatus.Information, 0 fastcall IofCompleteRequest, eax, IO_NO_INCREMENT mov eax, STATUS_SUCCESS invoke DbgPrint, $CTA0("Complete Irp") .else mov (_IRP PTR [eax]).CancelRoutine, NULL mov (_IRP PTR [eax]).IoStatus.Status, STATUS_CANCELLED and (_IRP PTR [eax]).IoStatus.Information, 0 fastcall IofCompleteRequest, eax, IO_NO_INCREMENT mov eax, STATUS_CANCELLED invoke DbgPrint, $CTA0("Cancel Irp") .endif .endif ret OnTimer endp IrpOpenClose proc pDevObj:PDEVICE_OBJECT, pIrp:PIRP IoGetCurrentIrpStackLocation pIrp movzx eax, (IO_STACK_LOCATION PTR [eax]).MajorFunction .if eax == IRP_MJ_CREATE invoke DbgPrint, $CTA0("IRP_MJ_CREATE") .elseif eax == IRP_MJ_CLOSE invoke DbgPrint, $CTA0("IRP_MJ_CLOSE") .endif mov eax, pIrp and (_IRP PTR [eax]).IoStatus.Information, 0 mov (_IRP PTR [eax]).IoStatus.Status, STATUS_SUCCESS fastcall IofCompleteRequest, pIrp, IO_NO_INCREMENT mov eax, STATUS_SUCCESS ret IrpOpenClose endp IrpIOCTL proc uses ebx pOurDevice:PDEVICE_OBJECT, pIrp:PIRP local pdx:PTR Dev_Ext local stTimePeriod:LARGE_INTEGER mov eax, pOurDevice push (DEVICE_OBJECT PTR [eax]).DeviceExtension pop pdx IoGetCurrentIrpStackLocation pIrp mov eax, (IO_STACK_LOCATION PTR [eax]).Parameters.DeviceIoControl.IoControlCode .if eax == IOCTL_QUEUE invoke DbgPrint, offset MSG_QUEUE mov ebx, pdx lea ebx, (OurDeviceExtension PTR [ebx]).stCsq invoke IoCsqInsertIrp, ebx, pIrp, NULL mov eax, STATUS_PENDING ret .elseif eax == IOCTL_PROCESS invoke DbgPrint, offset MSG_PROCESS or stTimePeriod.HighPart, -1 mov stTimePeriod.LowPart, -10000000 mov ebx, pdx invoke KeSetTimerEx, addr (OurDeviceExtension PTR [ebx]).stTime, stTimePeriod.LowPart, stTimePeriod.HighPart, 1000, addr (OurDeviceExtension PTR [ebx]).stDPC .endif mov eax, pIrp mov (_IRP PTR [eax]).IoStatus.Status, STATUS_SUCCESS and (_IRP PTR [eax]).IoStatus.Information, 0 fastcall IofCompleteRequest, pIrp, IO_NO_INCREMENT mov eax, STATUS_SUCCESS ret IrpIOCTL endp Unload proc pOurDriver:PDRIVER_OBJECT local szSymName:UNICODE_STRING invoke RtlInitUnicodeString, addr szSymName, offset SYM_NAME invoke IoDeleteSymbolicLink, addr szSymName mov eax, pOurDriver invoke IoDeleteDevice, (DRIVER_OBJECT PTR [eax]).DeviceObject ret Unload endp DriverEntry proc pOurDriver:PDRIVER_OBJECT, pOurRegistry:PUNICODE_STRING local pOurDevice:PDEVICE_OBJECT local suDevName:UNICODE_STRING local szSymName:UNICODE_STRING mov eax, pOurDriver mov (DRIVER_OBJECT PTR [eax]).MajorFunction[IRP_MJ_CREATE * (sizeof PVOID)], offset IrpOpenClose mov (DRIVER_OBJECT PTR [eax]).MajorFunction[IRP_MJ_CLOSE * (sizeof PVOID)], offset IrpOpenClose mov (DRIVER_OBJECT PTR [eax]).MajorFunction[IRP_MJ_DEVICE_CONTROL * (sizeof PVOID)], offset IrpIOCTL mov (DRIVER_OBJECT PTR [eax]).DriverUnload, offset Unload invoke RtlInitUnicodeString, addr suDevName, offset DEV_NAME invoke RtlInitUnicodeString, addr szSymName, offset SYM_NAME invoke IoCreateDevice, pOurDriver, sizeof OurDeviceExtension, addr suDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, addr pOurDevice .if eax == STATUS_SUCCESS mov eax, pOurDevice or (DEVICE_OBJECT PTR [eax]).Flags, DO_BUFFERED_IO and (DEVICE_OBJECT PTR [eax]).Flags, not DO_DEVICE_INITIALIZING mov eax, pOurDevice mov eax, (DEVICE_OBJECT PTR [eax]).DeviceExtension lea eax, (OurDeviceExtension PTR [eax]).stQueue InitializeListHead eax mov eax, pOurDevice mov eax, (DEVICE_OBJECT PTR [eax]).DeviceExtension lea eax, (OurDeviceExtension PTR [eax]).stLock invoke KeInitializeSpinLock, eax mov eax, pOurDevice mov eax, (DEVICE_OBJECT PTR [eax]).DeviceExtension invoke KeInitializeTimer, addr (OurDeviceExtension ptr [eax]).stTime mov eax, pOurDevice mov eax, (DEVICE_OBJECT PTR [eax]).DeviceExtension invoke KeInitializeDpc, addr (OurDeviceExtension ptr [eax]).stDPC, offset OnTimer, pOurDevice mov eax, pOurDevice mov eax, (DEVICE_OBJECT PTR [eax]).DeviceExtension lea eax, (OurDeviceExtension PTR [eax]).stCsq invoke IoCsqInitialize, eax, offset CsqInsertIrp, offset CsqRemoveIrp, offset CsqPeekNextIrp, offset CsqAcquireLock, offset CsqReleaseLock, offset CsqCompleteCanceledIrp invoke IoCreateSymbolicLink, addr szSymName, addr suDevName .endif ret DriverEntry endp end DriverEntry .end
DriverEntry()初始化List、DPC Timer和CSQ的Callback
IrpIOCTL()IOCTL_QUEUE把Irp加入List,IOCTL_PROCESS開始處理Irp
OnTimer()定時查看是否有需要處理的Irp
CsqReleaseLock()同步CSQ Irp用
CsqAcquireLock()同步CSQ Irp用
CsqPeekNextIrp()需要手動移除CSQ Irp時使用,目前沒有使用到
CsqCompleteCanceledIrp處理Cancel Irp用
CsqRemoveIrp()Irp被CSQ移除時呼叫
CsqInsertIrp()Irp被加入到CSQ時呼叫
app.asm
.386p .model flat, stdcall option casemap:none include c:\masm32\include\windows.inc include c:\masm32\include\masm32.inc include c:\masm32\include\user32.inc include c:\masm32\include\msvcrt.inc include c:\masm32\include\kernel32.inc include c:\masm32\include\w2k\ntddkbd.inc include c:\masm32\Macros\Strings.mac includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\masm32.lib includelib c:\masm32\lib\msvcrt.lib includelib c:\masm32\lib\kernel32.lib IOCTL_QUEUE equ CTL_CODE(FILE_DEVICE_UNKNOWN, 800h, METHOD_BUFFERED, FILE_ANY_ACCESS) IOCTL_PROCESS equ CTL_CODE(FILE_DEVICE_UNKNOWN, 801h, METHOD_BUFFERED, FILE_ANY_ACCESS) .const DEV_NAME db "\\.\MyDriver",0 .data? cnt dd ? hFile dd ? dwRet dd ? event dd 3 dup(?) ov OVERLAPPED <?> .code start: invoke CreateFile, offset DEV_NAME, GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL, 0 .if eax == INVALID_HANDLE_VALUE invoke crt_printf, $CTA0("failed to open mydriver\n") invoke ExitProcess, -1 .endif mov hFile, eax invoke crt_memset, offset ov, 0, sizeof OVERLAPPED mov cnt, 3 .while cnt > 0 invoke CreateEvent, NULL, TRUE, FALSE, NULL push eax pop ov.hEvent mov ecx, cnt mov edi, offset event mov [edi + ecx * 4], eax invoke crt_printf, $CTA0("queue event\n") invoke DeviceIoControl, hFile, IOCTL_QUEUE, NULL, 0, NULL, 0, offset dwRet, offset ov invoke CloseHandle, ov.hEvent sub cnt, 1 .endw invoke crt_printf, $CTA0("process all of events\n") invoke DeviceIoControl, hFile, IOCTL_PROCESS, NULL, 0, NULL, 0, offset dwRet, NULL invoke Sleep, 1000 invoke CancelIo, hFile mov cnt, 3 .while cnt > 0 mov ecx, cnt mov edi, offset event mov eax, [edi + ecx * 4] push eax pop ov.hEvent invoke WaitForSingleObject, ov.hEvent, INFINITE invoke CloseHandle, ov.hEvent invoke crt_printf, $CTA0("wait complete\n") sub cnt, 1 .endw invoke CloseHandle, hFile invoke ExitProcess, 0 end start
結果