我自己在看《寒江独钓》这本书的时候,书中除了给出了利用过滤的方式来拦截键盘数据之外,也提到了另外一种方法,就是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. hdu4553约会安排 线段树

    //DS QT  找一段最靠前的长度为QT的空间 //NS QT  找一段最靠前的长度为QT的空间.假设没找到能够将DS占领的空间当做空暇空间,找一段最靠前的空间 //STUDY!! L R  清空L ...

  2. 查询linux版本命令

    [环境] Ubuntu [本文命令记录] uname -a lsb_release cat /etc/issue cat /proc/version [截图效果] (1)uname -a (2)lsb ...

  3. TCP/IP、HTTP、HTTPS、HTTP2.0

    TCP/IP.HTTP.HTTPS.HTTP2.0 HTTP,全称超文本传输协议(HTTP,HyperText Transfer Protocol),是一个客户端和服务器端请求和应答的标准(TCP), ...

  4. 获取AJAX加载的内容

    1.有些网页内容使用AJAX加载,AJAX一般返回的是JSON,直接对AJAX地址进行post或get,就返回JSON数据了. 2.用抓包工具分析https://movie.douban.com/j/ ...

  5. CentOS 7 学习(一) 配置LAMP和Nginx

    CentOS 7 学习(一) 配置LAMP和Nginx CentOS是RedHat Linux企业版的代码编译版本,属于比较通用的服务器Linux版本,据说Ubuntu Server更通用,呵呵,不过 ...

  6. 【Jenkins】通过ANT构建JMeter任务时提示找不到jtl文件时的解决方法

  7. iOS 蓝牙开发资料记录

    一.蓝牙基础认识:   1.iOS蓝牙开发:  iOS蓝牙开发:蓝牙连接和数据读写   iOS蓝牙后台运行  iOS关于app连接已配对设备的问题(ancs协议的锅)          iOS蓝牙空中 ...

  8. 小白的Python之路 day4 生成器

    一.列表生成式  看下面例子: 列表生成式的作用:主要是让代码更简洁(还有装X的效果) 二.生成器 通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.而且,创建一个包 ...

  9. JavaWeb之数据源连接池(4)---自定义数据源连接池

    [续上文<JavaWeb之数据源连接池(3)---Tomcat>] 我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究其原 ...

  10. MAMP升级mysql5.6到5.7

    RT 1.先把mamp环境停掉 sudo sh /Applications/MAMP/bin/stop.sh 2.然后使用brew安装mysql5.7 brew install mysql 3.默认安 ...