驅動程式 - Windows NT Driver (Legacy) - 使用範例 - Assembly (MASM32) - Use Cancel-Safe IRP Queue(CSQ)



參考資訊:
https://wasm.in/
http://four-f.narod.ru/
https://github.com/steward-fu/ddk

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
 
MyDeviceExtension struct
    dpc KDPC <>
    csq IO_CSQ <>
    timer KTIMER <>
    queue LIST_ENTRY <>
    locker KSPIN_LOCK <>
    pNextDev PDEVICE_OBJECT ?
MyDeviceExtension 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
MSG_QUEUE   byte "IOCTL_QUEUE",0
MSG_PROCESS byte "IOCTL_PROCESS",0
 
.code
CsqInsertIrp proc uses esi ebx ecx pCsqInfo : PIO_CSQ, pIrp : PIRP
    invoke DbgPrint, $CTA0("CsqInsertIrp")
     
    ; CONTAINING_RECORD 
    mov eax, pCsqInfo
    sub eax, MyDeviceExtension.csq
    mov esi, eax
    lea ebx, (MyDeviceExtension ptr [esi]).queue
    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
    invoke DbgPrint, $CTA0("CsqCompleteCanceledIrp")
     
    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 esi ebx pCsqInfo : PIO_CSQ, pIrp : PIRP, pPeekContext : PVOID
    local nextIrp : PTR _IRP
    local listHead : PTR LIST_ENTRY
    local nextEntry : PTR LIST_ENTRY
    local irpStack : PIO_STACK_LOCATION
     
    invoke DbgPrint, $CTA0("CsqPeekNextIrp")
    mov nextIrp, NULL
     
    ; CONTAINING_RECORD 
    mov eax, pCsqInfo
    sub eax, MyDeviceExtension.csq
    mov esi, eax
    lea eax, (MyDeviceExtension ptr [esi]).queue
    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
            .break
        .endif

        mov eax, irpStack
        mov eax, (IO_STACK_LOCATION ptr [eax]).FileObject
        mov ebx, pPeekContext
        .if eax == ebx
            .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 esi ecx pCsqInfo : PIO_CSQ, pIrql : PKIRQL
    invoke DbgPrint, $CTA0("CsqAcquireLock")
     
    ; CONTAINING_RECORD 
    mov eax, pCsqInfo
    sub eax, MyDeviceExtension.csq
    mov esi, eax
    lea ecx, (MyDeviceExtension ptr [esi]).locker
    fastcall KfAcquireSpinLock, ecx
    mov ecx, pIrql
    mov [ecx], al
    ret
CsqAcquireLock endp
 
CsqReleaseLock proc uses esi ecx edx pCsqInfo : PIO_CSQ, Irql : KIRQL
    ; CONTAINING_RECORD 
    mov eax, pCsqInfo
    sub eax, MyDeviceExtension.csq
    mov esi, eax
    lea ecx, (MyDeviceExtension PTR [esi]).locker

    mov dl, Irql
    .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 esi edi ebx pDpc : PKDPC, pContext : PVOID, pArg1 : PVOID, PArg2 : PVOID
    mov eax, pContext
    mov esi, (DEVICE_OBJECT ptr [eax]).DeviceExtension
    lea edi, (MyDeviceExtension ptr [esi]).queue

    IsListEmpty edi
    .if eax == TRUE
        invoke KeCancelTimer, addr (MyDeviceExtension ptr [esi]).timer
        invoke DbgPrint, $CTA0("Finish")
    .else
        RemoveHeadList edi
     
        ; 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 pMyDevie : 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 esi ebx pMyDevice : PDEVICE_OBJECT, pIrp : PIRP
    local period : LARGE_INTEGER
 
    mov eax, pMyDevice
    push (DEVICE_OBJECT ptr [eax]).DeviceExtension
    pop esi
 
    IoGetCurrentIrpStackLocation pIrp
    mov eax, (IO_STACK_LOCATION ptr [eax]).Parameters.DeviceIoControl.IoControlCode
    .if eax == IOCTL_QUEUE
        invoke DbgPrint, offset MSG_QUEUE
         
        lea ebx, (MyDeviceExtension ptr [esi]).csq
        invoke IoCsqInsertIrp, ebx, pIrp, NULL
        mov eax, STATUS_PENDING
        ret
    .elseif eax == IOCTL_PROCESS
        invoke DbgPrint, offset MSG_PROCESS

        or period.HighPart, -1
        mov period.LowPart, -10000000
        invoke KeSetTimerEx, addr (MyDeviceExtension ptr [esi]).timer, period.LowPart, period.HighPart, 1000, addr (MyDeviceExtension ptr [esi]).dpc
    .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 pMyDriver : PDRIVER_OBJECT
    local szSymName : UNICODE_STRING

    invoke RtlInitUnicodeString, addr szSymName, $CTW0("\\DosDevices\\MyDriver")
    invoke IoDeleteSymbolicLink, addr szSymName
                        
    mov eax, pMyDriver
    invoke IoDeleteDevice, (DRIVER_OBJECT ptr [eax]).DeviceObject
    ret
Unload endp
         
DriverEntry proc pMyDriver : PDRIVER_OBJECT, pMyRegistry : PUNICODE_STRING
    local pMyDevice : PDEVICE_OBJECT
    local szDevName : UNICODE_STRING
    local szSymName : UNICODE_STRING
             
    mov eax, pMyDriver
    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 szDevName, $CTW0("\\Device\\MyDriver")
    invoke RtlInitUnicodeString, addr szSymName, $CTW0("\\DosDevices\\MyDriver")

    invoke IoCreateDevice, pMyDriver, sizeof MyDeviceExtension, addr szDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, addr pMyDevice
    .if eax == STATUS_SUCCESS
        mov eax, pMyDevice
        or (DEVICE_OBJECT ptr [eax]).Flags, DO_BUFFERED_IO
        and (DEVICE_OBJECT ptr [eax]).Flags, not DO_DEVICE_INITIALIZING
             
        mov eax, pMyDevice
        mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
        lea eax, (MyDeviceExtension ptr [eax]).queue
        InitializeListHead eax
 
        mov eax, pMyDevice
        mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
        lea eax, (MyDeviceExtension ptr [eax]).locker
        invoke KeInitializeSpinLock, eax
 
        mov eax, pMyDevice
        mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
        invoke KeInitializeTimer, addr (MyDeviceExtension ptr [eax]).timer
 
        mov eax, pMyDevice
        mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
        invoke KeInitializeDpc, addr (MyDeviceExtension ptr [eax]).dpc, offset OnTimer, pMyDevice
 
        mov eax, pMyDevice
        mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
        lea eax, (MyDeviceExtension ptr [eax]).csq
        invoke IoCsqInitialize, eax, offset CsqInsertIrp, offset CsqRemoveIrp, offset CsqPeekNextIrp, offset CsqAcquireLock, offset CsqReleaseLock, offset CsqCompleteCanceledIrp
        invoke IoCreateSymbolicLink, addr szSymName, addr szDevName
    .endif
    ret
DriverEntry endp
end DriverEntry
.end

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
    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("Queued 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("Processing All 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("Complete\n")
        sub cnt, 1
    .endw
        
    invoke CloseHandle, hFile
    invoke ExitProcess, 0
end start
.end

結果