寒江独钓 第二章(3)Hook分发函数和回调函数

    前面两节讲的是通过在设备栈上绑定一个新的设备实现键盘过滤.这是非常正统的方法,是合法软件行为.一般黑客软件不会采用这么正道的方法.
    黑客可以通过修改一个已经存在的驱动对象(比如前面提到的KbdClass)分发函数的指针来实现过滤所有请求的目的.
4.5.1获得类驱动对象
    首先要获得键盘类驱动对象,才能替换下面的分发函数.这个驱动的名字是"\\Driver\\Kbdclass",可以直接用函数ObReferenceObjectByName来获得.
   
   
  1. //驱动名字
  2. #define KBD_DRIVER_NAME L"\\Driver\\Kbdclass"
  3. //存放驱动对象的指针
  4. PDRIVER_OBJECT KbdDriverObject;
  5. UNICODE_STRING uniNtNameString;
  6. //初始化驱动的名字字符串
  7. RtlInitUnicodeString(&uniNtNameString,KBD_DRIVER_NAME);
  8. //根据名字字符串来获得驱动对象
  9. status = ObReferenceObjectByName(&uniNtNameString,OBJ_CASE_INSENSITIVE,
  10. NULL,0,IoDriverObjectType,KernelMode,NULL,&KbdDriverObject);
  11. if(!NT_SUCCESS(status))
  12. {
  13. //如果失败了
  14. DbgPrint("不能获取驱动对象指针\n");
  15. return STATUS_UNSUCCESSFUL;
  16. }
  17. else
  18. {
  19. //凡是调用了Reference系列的函数都要通过调用ObDereferenceObject来解除引用
  20. ObDereferenceObject(KbdDriverObject)
  21. }
4.5.2修改类驱动的分发函数指针
    这里用到一个原子操作:InterlockedExchangePointer,设置新的函数指针的操作是原子的,不会被打断.
   
   
  1. //这个数组用来保存所有旧的指针
  2. ULONG i
  3. PDRIVER_DISPATCH OldDispatchFunctions[IRP_MJ_MAXIMUM_FUNCTION + 1];
  4. ...
  5. //把所有的分发函数指针都替换成我们自己编写的同一个分发函数
  6. for(i=0;i<=IRP_MJ_MAXIMUM_FUNCTION;++i)
  7. {
  8. //假设MyFilterDipatch是笔者已经写好的一个分发函数
  9. OldDispatchFunction[i] = KbdDriverObject->MajorFunction[i];
  10. //进行原子交换操作
  11. InterlockedExchangePointer(&KbdDriverObject->MajorFunction[i],MyFilterDispatch);
  12. }
4.5.3类驱动之下的端口驱动
    前面的过滤方法是填的分发函数指针.介是这依然比较明显,因为分发函数指针本来是已知的,如果安全监控软件有针对性对这个指针进行检查和保护,就容易发现这个指针已经被替换掉的情况.但是从分发函数出发,下面的各个调用层出不穷,任何一个地方都可能被替换,安全程序又怎么可能一一去保护呢?
    下面是比邪道的方法:
    直接寻找一个用于端口驱动中读取输入缓冲区的函数(但是这个函数实际上 类驱动提供的).Hook这个函数实现过滤.

    KbdClass被称为键盘类驱动,在windows中类驱动通常是指统管一类设备的驱动程序.不管是USB键盘,还是PS/2键盘都经过它,所以在这层做拦截,能获得很好以通用性.类驱动之下和实际硬件交互的驱动被称为"端口驱动".具体到键盘,i8042prt是PS/2键盘的端口驱动,USB键盘则是Kbdhid.
    键盘驱动的主要工作就是,当键盘上有键按下引发中断时,键盘驱动从端口读出按键的扫描码,最终顺利的将它交给键盘设备栈栈顶等待的那个主功能号为IRP_MJ_READ的IRP.为了完成这个任务,键盘驱动使用了两个循环使用的缓冲区.
    i8042prt和KbdClass各有一个自己的可以循环使用的缓冲区.缓冲区的每个单元是一个KEYBOARD_INPUT_DATA结构,用来存放一个扫描码及其相关信息.
在键盘驱动中,这个循环使用的缓冲区统一叫输入数据队列(input data queue),i8042prt的那个缓冲区叫做端口键盘输入数据队列,KbdClass的那个缓冲区叫类输入数据队列.
    实际上i8042prt的自定义设备扩展中,保存着一些指针和计数值,用来使用他的输入数据队列.包括:
  1. PKEYBOARD_INPUT_DATA类型的InputData,DataIn,DataOut,DataEnd.
  2. ULONG类型的InputCout.
  • InputData指针,指向输入数据队列的开头.
  • DataEnd指针,指向输入数据队列的结尾.
  • DataIn指针,指向要进入队列的新数据,被放在队列中的位置.
  • DataOut指针,指向要出队列的数据,在队列中开始的位置.
  • InputCount值,为输入数据队列中数据的个数.
    同时,在KbdClass的自定义设备扩展中,也保存着一些指针和计数值,用来使用它的输入数据队列.名字和类型与上面的数据完全一样.

4.5.4端口驱动和类驱动之间的协作机制
    当键盘上一个键被按下时,产生一个Make Code,引发键盘中断,当键盘上一个键被松开时,产生一个Break Code,引发键盘中断.键盘中断导致键盘中断服务例程被执行,导致最终i8042prt的I8042KeyboardInterruptService被执行.
    在i804KeyboardInterruptService中,从端口读出按键的扫描码,存放在一个KEYBOARD_INPUT_DATA中.将这个KEYBOARD_INPUT_DATA放入i804prt的输入数据队列中,一个中断放入一个数据,DataIn后移一格,InputCount加1.最后调用内核API函数KeInsertQueueDpc,一个进行更多处理的延迟过程调用.
    这个调用中,会调用上层处理输入的回调函数(也就是KbdClass处理输入数据的函数),取走i8042prt输入数据队列里的数据.之后i8042prt的输入数据列的DataOut相就后移,InputCount相应减少.KbdClass处理输入数据的函数后,满足那个应用层发来的等着读请求.
    当读请求要求读的数据大小大于等于i8042prt的输入数据队列中的数据时,读请求的处理函数直接从i804prt的输入数据队列中读出所有输入数据,不使用KbdClass的输入数据队列.
    当读请求要求读的数据的大小小于i8042prt的输入数据队列中的数据时,读请求的处理函数直接从i8042prt的输入数据队列中读出它所要求的大小,然后这个读请求被完成.i8042prt的输入数据队列中剩余的数据,被放入KbdClass的输入数据队列中.当应用层发来下一个读请求时,那个读请求将直接从KbdClass的输入数据队列中读取数据,不需要等待.

4.5.5找到关键的回调函数的条件
    从上面的原来来看,I8042KeyboardInterruptService中调用的类驱动的那个回调函数非常关键.如果找到了这个函数,通过Hook就可以轻易的获得键盘的输入了.
    现在问题就是如何定位这个函数指针了.i8042prt驱动的设备扩展我们并不完全清楚;此外,WDK也不可能公开这样一个函数的地址.
     但是"有识之士"根据经验指出:
  1. 这个函数指针应该保存在i8042prt生成的设备自定义设备扩展中.
  2. 这个函数的开始地址应该在内核模块KbClass中.
  3. 内核模块KbClass生成的一个设备对象的指针也保存在那个设备扩展中,而且在我们要找的函数指针之前.
    通过下面的代码就可以判断一个地址是否是在KbdClass这个驱动中了
   
   
  1. PVOID address;
  2. size_t kdbDriverStart = KdbDriverObject->DriverStart;
  3. size_t kdbDriverSize = KdbDriverObject->DriverSize;
  4. ...
  5. if((address>KbdDriverStart)&&(address<(PBYTE)KbdDriverStart+KbdDriverSize))
  6. {
  7. //说明在这个驱动中
  8. }
4.5.6定义常数和数据结构
    下面的方法实现了搜索这个关键的回调函数的指针.涉及到如下3个驱动,这里都定义成字符串.
   
   
  1. //键盘类驱动的名字
  2. #define KBD_DRIVER_NAME L"\\Driver\\Kbdclass"
  3. #define USBKBD_DRIVER_NAME L"\\Driver\\Kbdhid"
  4. //PS/键盘驱动名
  5. #define PS2KBD_DRIVER_NAME L"\\Driver\\i8042prt"
    然后对于我们要搜索的回调函数的类型定义如下:
   
   
  1. typedef VOID(_stdcall *KEYBOARDCLASSSERVICECALLBACK)(
  2. IN PDEVICE_OBJECT DeviceObject,
  3. IN PKEYBOARD_INPUT_DATA InputDataStart,
  4. IN PKEYBOARD_INPUT_DATA InputDataEnd,
  5. IN OUT PULONG InputDataConsumed);
    接下来,定义一个全局变量,来接收搜索到的回调函数.实际上我们不但搜索一个回调函数,还搜索类驱动生成的一个设备对象.这个设备对象的指针保存在端口驱动的设备对象的扩展中,而且必须先找到它,后面才能搜索回调函数.这个设备对象保存在全局变量gKbdClassBack.classDeviceObject中,而gKbdClassBack.serviceCallBack则将保存要搜索的回调函数.下面是全局变量gKbdCallBack的定义
   
   
  1. typedef struct _KBD_CALLBACK
  2. {
  3. PDEVICE_OBJECT classDeviceObject;
  4. KEYBOARDCLASSSERVICECALLBACK serviceCallBack;
  5. }KBD_CALLBACK,*PKBD_CALLBACK;
  6. KBD_CALLBACK gKbdCallBack={0};
4.5.7打开两种键盘端口驱动寻找设备
    原理是这样的:预先不可能知道机器上装的是USB键盘还是PS/2键盘,所以一开始是尝试打开这两个驱动.在很多情况下只有一个可以打开,比较极端的是两个都可以打开(用户同时装有两个种类的键盘),这并不是不可能的,或者是两个都打不开(用户安装一种我们没见过的各类的键盘).对于这两个极端的情况,都简单地返回失败即可.
   
   
  1. NTSTATUS SearchServiceCallBack(IN PDRIVER_OBJECT DriverObject)
  2. {
  3. //定义用到的一组局部变量.这些变量大多是顾名思义的
  4. NTSTATUS status = STATUS_UNSUCCESSFUL;
  5. int i = 0;
  6. UNICODE_STRING uniNtNameString;
  7. PDEVICE_OBJECT pTargetDeviceObject =NULL;
  8. PDRIVER_OBJECT KbdDriverObject = NULL;
  9. PDRIVER_OBJECT KbdhidDriverObject = NULL;
  10. PDRIVER_OBJECT Kbd8042DriverObject = NULL;
  11. PDRIVER_OBJECT UsingDriverObject = NULL;
  12. PDEVICE_OBJECT UsingDeviceObject = NULL;
  13. PVOID KbdDriverStart = NULL;
  14. ULONG KbdDriverSize = 0;
  15. PVOID UsingDeviceExt = NULL;
  16. //这里的代码用来打开USB端口驱动的驱动对象
  17. RtlInitUnicodeString(&uniNtNameString,USBKBD_DRIVER_NAME);
  18. status = ObReferenceObjectByName(&uniNtNameString,OBJ_CASE_INSENSITIVE,NULL,0,IoDriverObjectType,
  19. KernelMode,NULL,(PVOID*)&KbdhidDriverObject);
  20. if(!NT_SUCCESS(status))
  21. {
  22. DbgPrint("获取USB驱动指针失败\n");
  23. }
  24. else
  25. {
  26. ObDereferenceObject(KbdhidDriverObject);
  27. }
  28. //打开PS/2键盘的驱动对象
  29. RtlInitUnicodeString(&uniNtNameString,PS2KBD_DRIVER_NAME);
  30. status = ObReferenceObjectByName(&uniNtNameString,OBJ_CASE_INSENSITIVE,NULL,0,IoDriverObjectType,KernelMode,NULL,(PVOID*)&Kbd8042DriverObject);
  31. if(!NT_SUCCESS(status))
  32. {
  33. DbgPrint("获取PS/2驱动指针失败\n");
  34. }
  35. else
  36. {
  37. ObDereferenceObject(Kbd8042DriverObject);
  38. }
  39. //这段代码只考虑一个键盘起作用的情况.
  40. if(Kbd8042DriverObject && KbdhidDriverObject)
  41. {
  42. DbgPrint("一个以上键盘设备");
  43. return STATUS_UNSUCCESSFUL;
  44. }
  45. //系统用了其它种类的键盘,则返回失败
  46. if(!Kbd8042DriverObject&&!KbdhidDriverObject)
  47. {
  48. DbgPrint("没发现键盘设备");
  49. return STATUS_UNSUCCESSFUL;
  50. }
  51. //找到合适的驱动对象.不管是USB还是PS/2,反正是一定要找到一个的
  52. UsingDriverObject = Kbd8042DriverObject?Kbd8042DriverObject:KbdhidDriverObject;
  53. //找到这个驱动对象下的第一个设备对象
  54. UsingDeviceObject = UsingDriverObject->DeviceObject;
  55. //找到这个设备对象的设备扩展
  56. UsingDeviceExt = UsingDeviceObject->DeviceExtension;
  57. ...
  58. }
4.5.8搜索在KbdClass类驱动的地址
    接着写前面那个函数中没有完成的代码.目的就是为了寻找UsingDeviceExt中保存的一个在驱动KbdClass中的地址.
  
  
  1. //首先必须打开驱动KdbClass,以便从驱动对象中得到其开始地址和大小
  2. RtlInitUnicodeString(&uniNtNameString,KBD_DRIVER_NAME);
  3. status = ObReferenceObjectByName(&uniNtNameString,OBJ_CASE_INSENSITIVE,NULL,0,IoDriverObjectType,KernelMode,NULL,(PVOID*)&KbdDriverObject);
  4. if(!NT_SUCCESS(status))
  5. {
  6. DbgPrint("获取键盘类指针失败");
  7. return STATUS_UNSUCCESSFUL;
  8. }
  9. else
  10. {
  11. ObDereferenceObject(KbdDriverObject);
  12. //如果成功了,就找到了KbdClass的开始地址和大小
  13. KbdDriverStart = KbdDriverObject->DriverStart;
  14. KbdDriverSize = KbdDriverObject->DriverSize;
  15. }
    下面就是搜索过程.首先遍历KdbClass下面的所有设备,找到驱动对象下的第一个设备对象,然后找设备对象下的Next指针连续遍历即可.在这些设备中,有一个会保存在端口驱动的设备扩展中(也就是DeviceExt指针开始的地址),这就是我们要寻找的,当然,更重要的是要寻找到那个回调函数.
    所以我们定义了一个临时的指针DeviceExt,从前面得到的UsingDeviceExt的地址开始遍历,每次增加一个指针的宽度.
  
  
  1. //遍历kbdDriverObject下的设备对象
  2. pTargetDeviceObject = KbdDriverObject->DeviceObject;
  3. PVOID AddrServiceCallBack;
    //发现原代码有一个Bug,没有考虑UsingDriverObject->Next, 正常应该是再嵌套一个循环
    //while(UsingDeviceObject)
    //{
    //  UsingDeviceExt = UsingDeviceObject->DeviceExtension;
    // while(pTargetDeviceObject)
    // {
    // ...
    // }
    // UsingDriverObject->Next;
    //}
  4. while(pTargetDeviceObject)
  5. {
  6. PUCHAR DeviceExt = (PUCHAR)UsingDeviceExt;
  7. for(;i<4096;i++,DeviceExt +=sizeof(PUCHAR))
  8. {
  9. PVOID tmp;
  10. if(!MmIsAddressValid(DeviceExt))
  11. break;
  12. //找到后会填写到这个全局变量中.这里检查是否已经填好了
  13. //如果已经填好就不用继续找,可以直接跳出了
  14. if(gKbdCallBack.classDeviceObject && gKbdCallBack.serviceCallBack)
  15. {
  16. status = STATUS_SUCCESS;
  17. break;
  18. }
  19. //在端口驱动的设备扩展中,找到了类驱动的设备对象,填好类驱动设备对象后继续
  20. tmp = *(PVOID*)DeviceExt;
  21. if(tmp == pTargetDeviceObject)
  22. {
  23. gKbdCallBack.classDeviceObject = (PDEVICE_OBJECT)tmp;
  24. continue;
  25. }
  26. //如果在设备扩展上找到一个地址位于KbdClass这个驱动中,就可以认为,这就是我们要找的回调函数地址
  27. if((tmp>KbdDriverStart)&&(tmp<(PBYTE)KbdDriverStart+KbdDriverSize)&&MmIsAddressValid(tmp))
  28. {
  29. //将这个回调函数记录下来
  30. gKbdCallBack.serviceCallBack=(KEYBOARDCLASSSERVICECALLBACK)tmp;
  31. AddrServiceCallBack =(PVOID*)DeviceExt;
  32. }
  33. }
  34. //换成下一个设备,继续遍历
  35. pTargetDeviceObject =pTargetDeviceObject->NextDevice;
  36. }
  37. //如果成功的找好了,就把这个函数替换成我们自己的回调函数
  38. //之后的过滤就可以自己想做什么就做什么了
  39. if(AddrServiceCallBack && gKbdCallBack.serviceCallBack)
  40. {
  41. *AddrServiceCallBack = MyKeyboardClassServiceCallback;
  42. }
  43. return status;
我对这一节的想法:就算成功Hook掉这个回调函数,系统也不会去调用我们的函数,当系统在加载驱动的时候,i8042prt就记住了该回调函数真实地址,在回调的时候,不需要再去查设备扩展里的这个回调函数 地址了.
不过获得这个回调函数还是很有意义的,比如我们可以自己写程序,直接调用这个回调函数发送键盘驱动到上层应用程序,实现模拟键盘操作,跳过一些网络游戏的模拟键盘保护.
以上只是我的推测,如果不对,欢迎批评指正.

 下面是我把这一小节修改过后的代码:
   
   
  1. #include "ntddk.h"
  2. #include <ntddkbd.h>
  3. //键盘类驱动的名字
  4. #define KBD_DRIVER_NAME L"\\Driver\\Kbdclass"
  5. #define USBKBD_DRIVER_NAME L"\\Driver\\Kbdhid"
  6. //PS/键盘驱动名
  7. #define PS2KBD_DRIVER_NAME L"\\Driver\\i8042prt"
  8. typedef VOID(_stdcall *KEYBOARDCLASSSERVICECALLBACK)(
  9. IN PDEVICE_OBJECT DeviceObject,
  10. IN PKEYBOARD_INPUT_DATA InputDataStart,
  11. IN PKEYBOARD_INPUT_DATA InputDataEnd,
  12. IN OUT PULONG InputDataConsumed);
  13. KEYBOARDCLASSSERVICECALLBACK gServiceCallBack;
  14. PULONG gAddrServiceCallBack = NULL;
  15. VOID MyKeyboardClassServiceCallback(
  16. IN PDEVICE_OBJECT DeviceObject,
  17. IN PKEYBOARD_INPUT_DATA InputDataStart,
  18. IN PKEYBOARD_INPUT_DATA InputDataEnd,
  19. IN OUT PULONG InputDataConsumed)
  20. {
  21. DbgPrint("MyKeyboardClassServiceCallback 被调用\n");
  22. gServiceCallBack(DeviceObject,InputDataStart,InputDataEnd,InputDataConsumed);
  23. }
  24. //IoDriverObjectType实际上是一个全局变量,但是头文件中没有,只要声明就可以使用了
  25. extern POBJECT_TYPE IoDriverObjectType;
  26. //下面这个函数是事实存在的,只是文档中没有公开,声明下就可以使用了
  27. NTSTATUS ObReferenceObjectByName
  28. (
  29. PUNICODE_STRING ObjectName,
  30. ULONG Attributes,
  31. PACCESS_STATE AccessState,
  32. ACCESS_MASK DesiredAccess,
  33. POBJECT_TYPE ObjectType,
  34. KPROCESSOR_MODE AccessMode,
  35. PVOID ParseContext,
  36. PVOID *Object
  37. );
  38. NTSTATUS SearchServiceCallBack()
  39. {
  40. //定义用到的一组局部变量.这些变量大多是顾名思义的
  41. int i = 0;
  42. NTSTATUS status = STATUS_UNSUCCESSFUL;
  43. UNICODE_STRING uniNtNameString;
  44. PDEVICE_OBJECT pTargetDeviceObject =NULL;
  45. PDRIVER_OBJECT KbdDriverObject = NULL;
  46. PDRIVER_OBJECT KbdhidDriverObject = NULL;
  47. PDRIVER_OBJECT Kbd8042DriverObject = NULL;
  48. PDRIVER_OBJECT UsingDriverObject = NULL;
  49. PDEVICE_OBJECT UsingDeviceObject = NULL;
  50. PVOID KbdDriverStart = NULL;
  51. ULONG KbdDriverSize = 0;
  52. PULONG UsingDeviceExt = NULL;
  53. //这里的代码用来打开USB端口驱动的驱动对象
  54. RtlInitUnicodeString(&uniNtNameString,USBKBD_DRIVER_NAME);
  55. status = ObReferenceObjectByName(&uniNtNameString,OBJ_CASE_INSENSITIVE,NULL,0,IoDriverObjectType,
  56. KernelMode,NULL,(PVOID*)&KbdhidDriverObject);
  57. if(!NT_SUCCESS(status))
  58. {
  59. DbgPrint("获取USB驱动指针失败\n");
  60. }
  61. else
  62. {
  63. ObDereferenceObject(KbdhidDriverObject);
  64. }
  65. //打开PS/2键盘的驱动对象
  66. RtlInitUnicodeString(&uniNtNameString,PS2KBD_DRIVER_NAME);
  67. status = ObReferenceObjectByName(&uniNtNameString,OBJ_CASE_INSENSITIVE,NULL,0,IoDriverObjectType,KernelMode,NULL,(PVOID*)&Kbd8042DriverObject);
  68. if(!NT_SUCCESS(status))
  69. {
  70. DbgPrint("获取PS/2驱动指针失败\n");
  71. }
  72. else
  73. {
  74. ObDereferenceObject(Kbd8042DriverObject);
  75. }
  76. //这段代码只考虑一个键盘起作用的情况.
  77. if(Kbd8042DriverObject && KbdhidDriverObject)
  78. {
  79. DbgPrint("一个以上键盘设备");
  80. return STATUS_UNSUCCESSFUL;
  81. }
  82. //系统用了其它种类的键盘,则返回失败
  83. if(!Kbd8042DriverObject&&!KbdhidDriverObject)
  84. {
  85. DbgPrint("没发现键盘设备");
  86. return STATUS_UNSUCCESSFUL;
  87. }
  88. //找到合适的驱动对象.不管是USB还是PS/2,反正是一定要找到一个的
  89. UsingDriverObject = Kbd8042DriverObject?Kbd8042DriverObject:KbdhidDriverObject;
  90. //找到这个驱动对象下的第一个设备对象
  91. UsingDeviceObject = UsingDriverObject->DeviceObject;
  92. //找到这个设备对象的设备扩展
  93. UsingDeviceExt = (PULONG)UsingDeviceObject->DeviceExtension;
  94. //首先必须打开驱动KdbClass,以便从驱动对象中得到其开始地址和大小
  95. RtlInitUnicodeString(&uniNtNameString,KBD_DRIVER_NAME);
  96. status = ObReferenceObjectByName(&uniNtNameString,OBJ_CASE_INSENSITIVE,NULL,0,IoDriverObjectType,KernelMode,NULL,(PVOID*)&KbdDriverObject);
  97. if(!NT_SUCCESS(status))
  98. {
  99. DbgPrint("获取键盘类指针失败");
  100. return STATUS_UNSUCCESSFUL;
  101. }
  102. else
  103. {
  104. ObDereferenceObject(KbdDriverObject);
  105. //如果成功了,就找到了KbdClass的开始地址和大小
  106. KbdDriverStart = KbdDriverObject->DriverStart;
  107. KbdDriverSize = KbdDriverObject->DriverSize;
  108. }
  109. //下面是主要修改的地方,因为用windebug查看后知道,回调函数就存放在对应设备对象的下一个内存地址
  110. while (UsingDeviceObject)
  111. {
  112. pTargetDeviceObject = KbdDriverObject->DeviceObject;
  113. while (pTargetDeviceObject)
  114. {
  115. UsingDeviceExt = (PULONG)UsingDeviceObject->DeviceExtension;
  116. for (i = 0; i < 200; i++)
  117. {
  118. if (UsingDeviceExt[i] == (ULONG)pTargetDeviceObject &&
  119. UsingDeviceExt[i + 1] > (ULONG)KbdDriverStart &&
  120. UsingDeviceExt[i + 1] < (ULONG)KbdDriverStart + KbdDriverSize)
  121. {
  122. gServiceCallBack=(KEYBOARDCLASSSERVICECALLBACK)UsingDeviceExt[i + 1];
  123. gAddrServiceCallBack = &(UsingDeviceExt[i + 1]);
  124. DbgPrint("回调函数存放地址 = %x\n",gAddrServiceCallBack);
  125. DbgPrint("回调函数地址 == 0x%x\n", gServiceCallBack);
  126. *gAddrServiceCallBack=(ULONG)MyKeyboardClassServiceCallback;
  127. DbgPrint("hook后回调函数地址== 0x%x\n", UsingDeviceExt[i + 1]);
  128. return STATUS_SUCCESS;
  129. }
  130. }
  131. pTargetDeviceObject = pTargetDeviceObject->NextDevice;
  132. }
  133. UsingDeviceObject = UsingDeviceObject->NextDevice;
  134. }
  135. //主要修改的地方
  136. return STATUS_UNSUCCESSFUL;
  137. }
  138. void Unload(PDRIVER_OBJECT drv)
  139. {
  140. if (gAddrServiceCallBack)
  141. {
  142. *gAddrServiceCallBack = gServiceCallBack;
  143. }
  144. }
  145. NTSTATUS DriverEntry(PDRIVER_OBJECT driver,PUNICODE_STRING reg_path)
  146. {
  147. driver->DriverUnload = Unload;
  148. return SearchServiceCallBack();
  149. }







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值