• hook键盘驱动中的分发函数实现键盘输入数据的拦截


    我自己在看《寒江独钓》这本书的时候,书中除了给出了利用过滤的方式来拦截键盘数据之外,也提到了另外一种方法,就是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
    ");
    
        //等待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
    ", 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!
    ");
        //每当进入这个分发函数时都需要将这个未完成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
    ");
    
        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;
    }
  • 相关阅读:
    RESTful API 设计指南
    浅析JS中的模块规范(CommonJS,AMD,CMD)
    Gitbucket—快速建立自己的Github
    单点登录详解
    Java常用类--处理日期
    Java常用类--数字常用类
    java常用类--字符串
    java常用类--系统相关
    java常用类--与用户互动
    设置PATH和CLASSPATH
  • 原文地址:https://www.cnblogs.com/lanuage/p/7725706.html
Copyright © 2020-2023  润新知