<寒江獨釣>學習筆記 -- 第四章 Hook分發函數 過濾鍵盤輸入

轉載請註明出處:http://blog.csdn.net/lup7in/article/details/7015497

最近在學習windows內核編程,下面是《寒江獨釣》第四章hook分發函數過濾鍵盤輸入的源代碼,書上沒有提供完整的代碼,經過調試完成了這部分代碼,下面把代碼分享出來,有不對的地方,希望大牛指教。

#include <ntddk.h> 
#include <ntddkbd.h>
#define NTSTRSAFE_LIB
#include <Ntstrsafe.h>


extern POBJECT_TYPE IoDriverObjectType;




#define KBD_DRIVER_NAME L"\\Driver\\Kbdclass"


//用於延時的宏定義
#define DELAY_ONE_MICROSECOND (-10)
#define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND * 1000)
#define DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND * 1000)


//表示幾個特殊鍵的宏
#define S_SHIFT 0x01//shift鍵
#define S_CAPS  0x02//大寫鎖定鍵
#define S_NUM   0x04//數字鎖定鍵


ULONG gC2pKeyCount = 0;


unsigned char asciiTbl[]={
0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,//normal
0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0x0D, 0x00, 0x61, 0x73,
0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,
0x32, 0x33, 0x30, 0x2E,
0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,//caps
0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x5B, 0x5D, 0x0D, 0x00, 0x41, 0x53,
0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x5A, 0x58, 0x43, 0x56,
0x42, 0x4E, 0x4D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,
0x32, 0x33, 0x30, 0x2E,
0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09,//shift
0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0x0D, 0x00, 0x41, 0x53,
0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,
0x32, 0x33, 0x30, 0x2E,
0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09,//caps + shift
0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x7B, 0x7D, 0x0D, 0x00, 0x61, 0x73,
0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x7A, 0x78, 0x63, 0x76,
0x62, 0x6E, 0x6D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,
0x32, 0x33, 0x30, 0x2E
};


//儲存舊的分發函數的數組
PDRIVER_DISPATCH OldMajorFunction[IRP_MJ_MAXIMUM_FUNCTION+1];


//用於根據驅動名稱打開驅動對象 這個函數文檔沒公佈 所以要聲明一下
NTSTATUS ObReferenceObjectByName(
PUNICODE_STRING ObjectName,
ULONG Attributes,
PACCESS_STATE AccessState,
ACCESS_MASK DesiredAccess,
POBJECT_TYPE ObjectType,
KPROCESSOR_MODE AccessMode,
PVOID ParseContext,
PVOID *Object
);


PDRIVER_OBJECT pKbdDriverObject = NULL;
UNICODE_STRING uniNtNameString;


//打開驅動對象的函數
NTSTATUS OpenKbdDriver()
{
NTSTATUS status;
RtlInitUnicodeString(&uniNtNameString,KBD_DRIVER_NAME);
status = ObReferenceObjectByName(&uniNtNameString,OBJ_CASE_INSENSITIVE,NULL,0,IoDriverObjectType,KernelMode,NULL,&pKbdDriverObject);
if(NT_SUCCESS(status))
{
ObDereferenceObject(pKbdDriverObject);
DbgPrint("open the kbd driver success!");
}
else
{
DbgPrint("open the kbd driver fail!");
}
return status;
}


static int kb_status = S_NUM;
//用於打印實際按鍵字符的函數 
//2011/11/10
void _stdcall print_keystroke(UCHAR sch,USHORT Flags)
{
UCHAR ch = 0;
int off = 0;
//kb_status = S_NUM;
//DbgPrint("%x\n",sch);
//按下
if(Flags == KEY_MAKE/*(sch & 0x80) == 0*/)
{
switch(sch)
{
case 0x3A:
kb_status ^= S_CAPS;
break;
case 0x2A:
case 0x36:
kb_status |= S_SHIFT;
break;
case 0x45:
kb_status ^= S_NUM;
break;
}
}
else if(Flags == KEY_BREAK) //彈起
{
if(sch == 0x2A || sch == 0x36)
kb_status &= ~S_SHIFT;
}


if(kb_status & S_CAPS)
{
off = 84 * 1;
}
if(kb_status & S_SHIFT)
{
off = 84 * 2;
}
if((kb_status & S_SHIFT) && (kb_status & S_CAPS))
{
off = 84 * 3;
}
//DbgPrint("%d\n",off);


if((sch < 0x47) || ((sch >= 0x47 && sch < 0x54) && (kb_status & S_NUM)))
{
ch = asciiTbl[off + sch];
}
if(Flags == KEY_MAKE){
if(ch >= 0x20 && ch < 0x7F)
{
DbgPrint("%C\n",ch);
}
}
}


NTSTATUS c2pReadComplete(
    PDEVICE_OBJECT  DeviceObject,
    PIRP  Irp,
    PVOID  Context
    )
{
PIO_STACK_LOCATION IrpSp;
ULONG buf_len = 0;
//systembuffer 的數據結構形式 是一個結構體 KEYBOARD_INPUT_DATA
PUCHAR buf = NULL;
size_t i;
ULONG numKeys = 0;
PKEYBOARD_INPUT_DATA KeyData = NULL;


IrpSp = IoGetCurrentIrpStackLocation(Irp);


//如果成功
if(NT_SUCCESS(Irp->IoStatus.Status))
{
//獲取讀請求完成後的輸出緩衝區
buf = Irp->AssociatedIrp.SystemBuffer;
KeyData = (PKEYBOARD_INPUT_DATA)buf;
//獲得緩衝區長度
buf_len = Irp->IoStatus.Information;
numKeys = buf_len / sizeof(KEYBOARD_INPUT_DATA);
for(i = 0;i < numKeys;i++)
{
//打印出一些按鍵信息
//DbgPrint("numKeys:%d",numKeys);
//DbgPrint("ScanCode:%x",KeyData->MakeCode);
//DbgPrint("%s\n",KeyData->Flags ? "UP" : "DOWN");
print_keystroke((UCHAR)KeyData->MakeCode,KeyData->Flags);
//這裏是一個小實驗 當用戶按下ctrl鍵時 
//把其掃描碼改成大寫鎖定鍵CAPS_LOCK的掃描碼
//當按下ctrl鍵時就會啓動大寫鎖定
//這說明按鍵是可以被截獲並且人爲修改的
//if(KeyData->MakeCode == LCONTROL)
//{
// KeyData->MakeCode = CAPS_LOCK;
//}
}


//這裏可以做其他事情
//打印按鍵的掃描碼
//for(i = 0;i < buf_len;i++)
//{
// DbgPrint("ctrl2cap:%2x\r\n",buf[i]);
//}
}
gC2pKeyCount--;
//DbgPrint("count:%d",gC2pKeyCount);
if(Irp->PendingReturned)
{
IoMarkIrpPending(Irp);
}
return Irp->IoStatus.Status;
}


//自己寫的分發函數 
NTSTATUS MyDispatchFunction (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
)
{
PUCHAR buf = NULL;
ULONG buf_len = 0;
ULONG i;
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(Irp);
//如果是讀請求 就hook之
if(irpsp->MajorFunction == IRP_MJ_READ)
{
irpsp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL;
//保留原來的完成函數,如果有的話
irpsp->Context = irpsp->CompletionRoutine;
irpsp->CompletionRoutine = (PIO_COMPLETION_ROUTINE)c2pReadComplete; 
//DbgPrint("已設置回調函數.../n");
gC2pKeyCount++;
//DbgPrint("count:%d",gC2pKeyCount);
//調用原來的分發函數,完成應該有的內容
return OldMajorFunction[irpsp->MajorFunction](DeviceObject,Irp);
}
else
{
return OldMajorFunction[irpsp->MajorFunction](DeviceObject,Irp);
}
}


//替換分發函數指針的函數
VOID ReplaceDispatch()
{
ULONG i;
for(i = 0;i < IRP_MJ_MAXIMUM_FUNCTION + 1;i++)
{
//保存分發函數指針
OldMajorFunction[i] = pKbdDriverObject->MajorFunction[i];
//把分發函數指針設置成我們自己寫的分發函數 這個函數替換指針是原子操作 防止替換時被打斷
InterlockedExchangePointer(&pKbdDriverObject->MajorFunction[i],MyDispatchFunction);
DbgPrint("replace successful!");
}
}


//恢復分發函數指針的函數
VOID ReSetDispatchFunction()
{
ULONG i;
for(i = 0;i < IRP_MJ_MAXIMUM_FUNCTION + 1;i++)
{
InterlockedExchangePointer(&pKbdDriverObject->MajorFunction[i],OldMajorFunction[i]);
DbgPrint("reset dispatch function success!");
}
}


//卸載函數
//2011/11/19
VOID HookDispatchUnload(IN PDRIVER_OBJECT DriverObject)
{
PDEVICE_OBJECT DeviceObject;
PRKTHREAD pCurrentThread;
LARGE_INTEGER lDelay;


lDelay = RtlConvertLongToLargeInteger(100 * DELAY_ONE_MILLISECOND);
pCurrentThread = KeGetCurrentThread();
//把線程設置成低實時模式 減少對其他程序的影響
KeSetPriorityThread(pCurrentThread,LOW_REALTIME_PRIORITY);


UNREFERENCED_PARAMETER(DriverObject);
KdPrint(("DriverEntry unLoading...\n"));


//恢復分發函數指針
ReSetDispatchFunction();


while(gC2pKeyCount)
{
KeDelayExecutionThread(KernelMode,FALSE,&lDelay);
}
KdPrint(("DriverEntry unLoad OK!\n"));
}


//入口函數
NTSTATUS DriverEntry( 
     PDRIVER_OBJECT  DriverObject,
     PUNICODE_STRING  RegistryPath 
    )
{
NTSTATUS status;
DriverObject->DriverUnload = HookDispatchUnload;
//打開鍵盤內核對象
status = OpenKbdDriver();
if(NT_SUCCESS(status))
{
//替換分發函數
ReplaceDispatch();
}
return status;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章