我自己在看《寒江独钓》这本书的时候,书中除了给出了利用过滤的方式来拦截键盘数据之外,也提到了另外一种方法,就是hook键盘分发函数,将它替换成我们自己的,然后再自己的分发函数中获取这个数据的方式,但是书中并没有明确给出代码,我结合书中所说的一些知识加上网上找到的相关资料,自己编写了相关代码,并且试验成功了,现在给出详细的方法和代码。

用这种方式时首先根据ObReferenceObjectByName函数来根据对应的驱动名称获取驱动的驱动对象指针。该函数是一个未导出函数,在使用时只需要先声明即可,函数原型如下:

NTSTATUS ObReferenceObjectByName(
PUNICODE_STRING ObjectName, //对应对象的名称
ULONG Attributes, //相关属性,一般给OBJ_CASE_INSENSITIVE
PACCESS_STATE AccessState, //描述信息的一个结构体指针,一般给NULL
ACCESS_MASK DesiredAccess, //以何种权限打开,一般给0如果或者FILL_ALL_ACCESS给它所有权限
POBJECT_TYPE ObjectType, //该指针是什么类型的指针,如果是设备对象给IoDeviceObjectType如果是驱动对象则给IoDriverObjectType
KPROCESSOR_MODE AccessMode, //一般给NULL
PVOID ParseContext, //附加参数,一般给NULL
PVOID *pObject //用来接收相关指针的输出参数
);

IoDeviceObjectType或者IoDriverObjectType也是未导出的,在使用之前需要先申明他们,例如

extern POBJECT_TYPE IoDriverObjectType;
extern POBJECT_TYPE IoDeviceObjectType;

然后将该驱动对象中原始的分发函数保存起来,以便在hook之后调用或者在驱动卸载时恢复

接下来hook相关函数,要截取键盘的数据,一般采用的是hook read函数

在read函数中设置IRP的完成例程,然后调用原始的分发函数,一定要注意调用原始的分发函数,否则自己很难实现类似的功能,一旦实现不了,那么Windows上的键盘功能将瘫痪。

在完成例程中解析穿回来的IRP就可得到对应键盘的信息。

下面是具体的实现代码

#define  KDB_DRIVER_NAME L"\\Driver\\KbdClass" //键盘驱动的名称为KbdClass

NTSTATUS ObReferenceObjectByName(
PUNICODE_STRING ObjectName,
ULONG Attributes,
PACCESS_STATE AccessState,
ACCESS_MASK DesiredAccess,
POBJECT_TYPE ObjectType,
KPROCESSOR_MODE AccessMode,
PVOID ParseContext,
PVOID *pObject);
extern POBJECT_TYPE IoDriverObjectType;
PDRIVER_OBJECT g_pKdbDriverObj; //键盘的驱动对象,保存这个是为了在卸载时还原它的分发函数
PDRIVER_DISPATCH g_oldDispatch[IRP_MJ_MAXIMUM_FUNCTION+1];
int g_KeyCount = 0; //记录键盘IRP的数量,当键盘的请求没有被处理完成时不能卸载这个驱动
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
LARGE_INTEGER WaitTime;
int i = 0;
DbgPrint("KBD HOOK: Entry DriverUnload\n"); //等待5s
WaitTime = RtlConvertLongToLargeInteger(-5 * 1000000000 / 100);
//如果IRP没有被处理完成,等待5s再检测是否处理完成
while(0 != g_KeyCount)
{
KeDelayExecutionThread(KernelMode, FALSE, &WaitTime);
} for(i = 0; i < IRP_MJ_MAXIMUM_FUNCTION + 1; i++)
{
//还原对应的分发函数
g_pKdbDriverObj->MajorFunction[i] = g_oldDispatch[i];
}
}
NTSTATUS
c2cReadComplete(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PUCHAR pBuffer;
ULONG uLength;
int i = 0; if(NT_SUCCESS(Irp->IoStatus.Status))
{
pBuffer = (PUCHAR)(Irp->AssociatedIrp.SystemBuffer);
uLength = Irp->IoStatus.Information; for(i = 0; i < uLength; i++)
{
//在完成函数中只是简单的输出了对应的16进制数
DbgPrint("cap2ctrl: Key %02x\r\n", pBuffer[i]);
}
} //每当一个IRP完成时,未完成的IRP数量都需要减一
g_KeyCount--; if(Irp->PendingReturned)
{
IoMarkIrpPending( Irp );
} return Irp->IoStatus.Status;
} NTSTATUS
c2cReadDispathc(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PIO_STACK_LOCATION pIroStack;
DbgPrint("Hook By Me!\n");
//每当进入这个分发函数时都需要将这个未完成IRP数量加一
g_KeyCount++;
//设置完成函数
//在这只能用这种方式,我自己试过用IoSetCompletionRoutine ,它注册的完成函数没有被调用,我也不知道为什么
pIroStack = IoGetCurrentIrpStackLocation(Irp);
pIroStack->Control = SL_INVOKE_ON_SUCCESS|SL_INVOKE_ON_ERROR|SL_INVOKE_ON_CANCEL;
pIroStack->CompletionRoutine = (PIO_COMPLETION_ROUTINE)c2cReadComplete; //调用原始的分发函数
return (g_oldDispatch[IRP_MJ_READ])(DeviceObject, Irp);
} NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
int i = 0;
PDRIVER_OBJECT pKbdDriverObj;
UNICODE_STRING uKbdDriverName;
NTSTATUS status; UNREFERENCED_PARAMETER(pRegistryPath);
DbgPrint("cap2ctrl: Entry DriverEntry\n"); RtlInitUnicodeString(&uKbdDriverName, KDB_DRIVER_NAME);
status = ObReferenceObjectByName(&uKbdDriverName, OBJ_CASE_INSENSITIVE, NULL, 0, IoDriverObjectType, KernelMode, NULL, &g_pKdbDriverObj);
if(!NT_SUCCESS(status))
{
return status;
} //保存原始的派遣函数
for(i = 0; i < IRP_MJ_MAXIMUM_FUNCTION+1; i++)
{
g_oldDispatch[i] = g_pKdbDriverObj->MajorFunction[i];
} //HOOK读请求的派遣函数
g_pKdbDriverObj->MajorFunction[IRP_MJ_READ] = c2cReadDispathc; pDriverObject->DriverUnload = DriverUnload; //绑定设备
return STATUS_SUCCESS;
}

hook键盘驱动中的分发函数实现键盘输入数据的拦截的更多相关文章

  1. 为什么linux驱动中变量或者函数都用static修饰?(知乎问题)

    static定义的全局变量 或函数也只能作用于当前的文件. 世界硬件厂商太多,定义static为了防止变量或 函数 重名,定义成static, 就算不同硬件驱动中的 变更 或函数重名了也没关系 .

  2. 《寒江独钓_Windows内核安全编程》中修改类驱动分发函数

    最近在阅读<寒江独钓_Windows内核安全编程>一书的过程中,发现修改类驱动分发函数这一技术点,书中只给出了具体思路和部分代码,没有完整的例子. 按照作者的思路和代码,将例子补充完整,发 ...

  3. 4.5 HOOK分发函数

    4.5 HOOK分发函数 本节开始深入的探讨键盘的过滤与反过滤.有趣的是,无论是过滤还是反过 滤,其原理都是进行过滤.取胜的关键在于:谁将第一个得到信息. 黑客可能会通过修改一个已经存在的驱动对象(比 ...

  4. [内核编程] 4.5 HOOK分发函数

    4.5 HOOK分发函数 本节开始深入的探讨键盘的过滤与反过滤.有趣的是,无论是过滤还是反过 滤,其原理都是进行过滤.取胜的关键在于:谁将第一个得到信息. 黑客可能会通过修改一个已经存在的驱动对象(比 ...

  5. 【驱动】USB驱动实例·串口驱动·键盘驱动

    Preface   USB体系支持多种类型的设备. 在 Linux内核,所有的USB设备都使用 usb_driver结构描述.    对于不同类型的 USB设备,内核使用传统的设备驱动模型建立设备驱动 ...

  6. 【驱动】USB驱动实例·串口驱动·键盘驱动【转】

    转自:http://www.cnblogs.com/lcw/p/3159370.html Preface USB体系支持多种类型的设备. 在 Linux内核,所有的USB设备都使用 usb_drive ...

  7. Linux设备驱动中的阻塞和非阻塞I/O

    [基本概念] 1.阻塞 阻塞操作是指在执行设备操作时,托不能获得资源,则挂起进程直到满足操作所需的条件后再进行操作.被挂起的进程进入休眠状态(不占用cpu资源),从调度器的运行队列转移到等待队列,直到 ...

  8. 蜕变成蝶~Linux设备驱动中的阻塞和非阻塞I/O

    今天意外收到一个消息,真是惊呆我了,博客轩给我发了信息,说是俺的博客文章有特色可以出本书,,这简直让我受宠若惊,俺只是个大三的技术宅,写的博客也是自己所学的一些见解和在网上看到我一些博文以及帖子里综合 ...

  9. linux内核驱动中对字符串的操作【转】

    转自:http://www.360doc.com/content/12/1224/10/3478092_255969530.shtml Linux内核中关于字符串的相关操作,首先包含头文件: #inc ...

随机推荐

  1. 一起talk C栗子吧(第一百二十六回:C语言实例--statickeyword)

    各位看官们,大家好,上一回中咱们说的内置宏的样例.这一回咱们说的样例是:static关键字. 闲话休提.言归正转. 让我们一起talk C栗子吧! 看官们,C语言提供了static关键字.它常常出如今 ...

  2. 3D Game Programming withDX11 学习笔记(一) 数学知识总结

    在图形学中,数学是不可或缺的一部分,所以本书最开始的部分就是数学知识的复习.在图形学中,最常用的是矢量和矩阵,所以我根据前面三个章节的数学知识,总结一下数学知识. 一.矢量 数学中的矢量,拥有方向和长 ...

  3. 采用Opserver来监控你的ASP.NET项目系列(三、监控你的服务器状态)

    前言 之前有过2篇关于如何监控ASP.NET core项目的文章,有兴趣的也可以看看. 今天我们主要来介绍一下,如何使用Opserver监控我们的服务器状态. Opserver的功能其实很强大,他可以 ...

  4. for in,Object.keys()与for of的区别

    for in 1.for in一般用于遍历对象的属性: 2.作用于数组的for in除了会遍历数组元素外,还会遍历自定义可枚举的属性,以及原型链上可枚举的属性:3.作用于数组的for in的遍历结果是 ...

  5. 【java设计模式】【结构模式Structural Pattern】装饰模式Decorator Pattern

    public class Client { public static void main(String[] args) { Component component=new ConcreteCompo ...

  6. ios单独的页面支持横竖屏的状态调整,HTML5加载下(更新2)

    单独的页面支持横竖屏的状态调整,HTML5加载下 工程中设置只支持竖屏状态,在加载HTML5的界面可以是横竖屏的,在不对工程其他界面/设置做调整的同时,可以这样去 #import "View ...

  7. mysql-5.7.17.msi安装

    mysql-5.7.17.msi安装,跟着截图摩擦,一步一步是爪牙,是魔鬼的步伐 开始: 可以创建其他用户 我自己改了日志名

  8. C#又能出来装个B了。一步一步微信跳一跳自动外挂

    PS:语言只是载体.思维逻辑才是王道 前天看见了个python的脚本.于是装python.配置环境变量.装pip.折腾了一上午,最终装逼失败. 于是进入博客园,顶部有篇文章吸引了我 .NET开发一个微 ...

  9. bzoj 3999: [TJOI2015]旅游

    Description 为了提高智商,ZJY准备去往一个新世界去旅游.这个世界的城市布局像一棵树.每两座城市之间只有一条路径可 以互达.每座城市都有一种宝石,有一定的价格.ZJY为了赚取最高利益,她会 ...

  10. 转载|chrome developer tool—— 断点调试篇

    断点,调试器的功能之一,可以让程序中断在需要的地方,从而方便其分析.也可以在一次调试中设置断点,下一次只需让程序自动运行到设置断点位置,便可在上次设置断点的位置中断下来,极大的方便了操作,同时节省了时 ...