• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > C++实现inline hook的原理及应用实例

C++实现inline hook的原理及应用实例

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了inline hook,android inline hook,c++中inline,c++ inline,c++ inline函数等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

本文实例简述了C++实现inline hook的原理及应用,对于大家更好的理解inline hook原理及其应用有很大的帮助。具体内容如下:

一、Inline Hook简介:

1.INLINE HOOK原理:

Inline Hook通过硬编码的方式向内核API的内存空间(通常是开始的一段字节,且一般在第一个call之前,这么做是为了防止堆栈混乱)写入跳转语句,这样,该API只要被调用,程序就会跳转到我们的函数中来,我们在自己写的函数里需要完成3个任务:

1)重新调整当前堆栈。程序流程在刚刚跳转的时候,内核API并没有执行完,而我们的函数需要根据其结果来进行信息过滤,所以我们需要保证内核API能在顺利执行完毕后返回到我们的函数中来,这就要求对当前堆栈做一个调整。

2)执行遗失的指令。我们向内核API地址空间些如跳转指令(jmp xxxxxxxx)时,势必要覆盖原先的一些汇编指令,所以我们一定要保证这些被覆盖的指令能够顺利执行(否则,你的及其就要BSOD了,呵呵,Blue Screen Of Death)。关于这部分指令的执行,一般是将其放在我们的函数中,让我们的函数“帮助”内核API执行完被覆盖的指令,然后再跳回内核API中被覆盖内后后的地址继续执行剩余内容。跳回去的时候,一定要算好是跳回到什么地址,是内核API起始地址后的第几个字节。

3)信息过滤。这个就不用多说了,内核API顺利执行并返回到我们的函数中,我们自然要根据其结果做一些信息过滤,这部分内容因被hook的API以及Hook目的的不同而不同。

2.Inline hook的工作流程:

1)验证内核API的版本(特征码匹配)。

2)撰写自己的函数,要完成以上三项任务。

3)获取自己函数的地址,覆盖内核API内存,供跳转。

简而言之,inlinehook的原理就是,修改函数,使其跳转到我们指定的地方。

常见的有改函数入口,也有改函数尾,函数中间的
比如,通常函数开头的汇编代码都是这样:mov edi,edi;push esp;mov ebp,esp,而我们便可以通过修改这里进行HOOK。

二、示例代码(该示例摘自看雪)

#include <ntifs.h>
#include <windef.h>
ULONG g_KiInsertQueueApc;
ULONG g_uCr0;
BYTE g_HookCode[5] = { 0xe9, 0, 0, 0, 0 }; //JMP NEAR
BYTE g_OrigCode[5] = { 0 }; // 原函数的前字节内容
BYTE jmp_orig_code[7] = { 0xEA, 0, 0, 0, 0, 0x08, 0x00 }; //JMP FAR
BOOL g_bHooked = FALSE;
VOID
fake_KiInsertQueueApc (
            PKAPC Apc,
            KPRIORITY Increment
            );
VOID
Proxy_KiInsertQueueApc (
            PKAPC Apc,
            KPRIORITY Increment
            );
void WPOFF()
{
  ULONG uAttr;
  _asm
  {
    push eax;
    mov eax, cr0;
    mov uAttr, eax;
    and eax, 0FFFEFFFFh; // CR0 16 BIT = 0
    mov cr0, eax;
    pop eax;
    cli
  };
  g_uCr0 = uAttr; //保存原有的 CRO 屬性
}
VOID WPON()
{
  _asm
  {
    sti
      push eax;
    mov eax, g_uCr0; //恢復原有 CR0 屬性
    mov cr0, eax;
    pop eax;
  };
}
//
// 停止inline hook
//
VOID UnHookKiInsertQueueApc ()
{
  KIRQL oldIrql;
  WPOFF();
  oldIrql = KeRaiseIrqlToDpcLevel();
  RtlCopyMemory ( (BYTE*)g_KiInsertQueueApc, g_OrigCode, 5 );
  KeLowerIrql(oldIrql);
  WPON();
  g_bHooked = FALSE;
}
//
// 开始inline hook -- KiInsertQueueApc
//
VOID HookKiInsertQueueApc ()
{
  KIRQL oldIrql;
  if (g_KiInsertQueueApc == 0) {
    DbgPrint("KiInsertQueueApc == NULL\n");
    return;
  }
  //DbgPrint("开始inline hook -- KiInsertQueueApc\n");
  DbgPrint( "KiInsertQueueApc的地址t0x%08x\n", (ULONG)g_KiInsertQueueApc );
  DbgPrint( "fake_KiInsertQueueApc的地址t0x%08x\n", (ULONG)fake_KiInsertQueueApc );
  
  // 保存原函数的前字节内容
  RtlCopyMemory (g_OrigCode, (BYTE*)g_KiInsertQueueApc, 5);
  //jmp指令,此处为短跳,计算相对偏移,同时,jmp xxxxxx这条指令占了5个字节
  *( (ULONG*)(g_HookCode + 1) ) = (ULONG)fake_KiInsertQueueApc - (ULONG)g_KiInsertQueueApc - 5;
  // 禁止系统写保护,提升IRQL到DPC
  WPOFF();
  oldIrql = KeRaiseIrqlToDpcLevel();
  RtlCopyMemory ( (BYTE*)g_KiInsertQueueApc, g_HookCode, 5 );
  *( (ULONG*)(jmp_orig_code + 1) ) = (ULONG) ( (BYTE*)g_KiInsertQueueApc + 5 );
  RtlCopyMemory ( (BYTE*)Proxy_KiInsertQueueApc, g_OrigCode, 5);
  RtlCopyMemory ( (BYTE*)Proxy_KiInsertQueueApc + 5, jmp_orig_code, 7);
  // 恢复写保护,降低IRQL
  KeLowerIrql(oldIrql);
  WPON();
  g_bHooked = TRUE;
}
//
// 跳转到我们的函数里面进行预处理,裸函数,有调用者进行堆栈的平衡
//
__declspec (naked)
VOID
fake_KiInsertQueueApc (
            PKAPC Apc,
            KPRIORITY Increment
            )
{
  // 去掉DbgPrint,不然这个hook会产生递归
  //DbgPrint("inline hook -- KiInsertQueueApc 成功\n");
  __asm
  {
    jmp Proxy_KiInsertQueueApc
  }
}
//
// 代理函数,负责跳转到原函数中继续执行
//
__declspec (naked)
VOID
Proxy_KiInsertQueueApc (
            PKAPC Apc,
            KPRIORITY Increment
            )
{
  __asm { // 共字节
    _emit 0x90
      _emit 0x90
      _emit 0x90
      _emit 0x90
      _emit 0x90 // 前字节实现原函数的头字节功能
      _emit 0x90 // 这个填充jmp
      _emit 0x90
      _emit 0x90
      _emit 0x90
      _emit 0x90 // 这字节保存原函数+5处的地址
      _emit 0x90 
      _emit 0x90 // 因为是长转移,所以必须是0x0080
  }
}
ULONG GetFunctionAddr( IN PCWSTR FunctionName)
{
  UNICODE_STRING UniCodeFunctionName;
  RtlInitUnicodeString( &UniCodeFunctionName, FunctionName );
  return (ULONG)MmGetSystemRoutineAddress( &UniCodeFunctionName ); 
}
//根据特征值,从KeInsertQueueApc搜索中搜索KiInsertQueueApc
ULONG FindKiInsertQueueApcAddress()
{
  char * Addr_KeInsertQueueApc = 0;
  int i = 0;
  char Findcode[] = { 0xE8, 0xcc, 0x29, 0x00, 0x00 };
  ULONG Addr_KiInsertQueueApc = 0;
  Addr_KeInsertQueueApc = (char *) GetFunctionAddr(L"KeInsertQueueApc");
  for(i = 0; i < 100; i ++)
  {
    if( Addr_KeInsertQueueApc[i] == Findcode[0] &&
      Addr_KeInsertQueueApc[i + 1] == Findcode[1] &&
      Addr_KeInsertQueueApc[i + 2] == Findcode[2] &&
      Addr_KeInsertQueueApc[i + 3] == Findcode[3] &&
      Addr_KeInsertQueueApc[i + 4] == Findcode[4]
    )
    {
      Addr_KiInsertQueueApc = (ULONG)&Addr_KeInsertQueueApc[i] + 0x29cc + 5;
      break;
    }
  }
  return Addr_KiInsertQueueApc;
}
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
  DbgPrint("My Driver Unloaded!");
  UnHookKiInsertQueueApc();
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
  DbgPrint("My Driver Loaded!");
  theDriverObject->DriverUnload = OnUnload;
  g_KiInsertQueueApc = FindKiInsertQueueApcAddress();
  HookKiInsertQueueApc();
  return STATUS_SUCCESS;
}
</div> </div>
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • Inline Hook(ring3)的简单C++实现方法
  • C++实现inline hook的原理及应用实例

相关文章

  • 2017-05-28关于数组做函数参数的问题集合汇总
  • 2017-05-28浅析Linux下精确控制时间的函数
  • 2017-05-28了解C++编程中指定的异常和未经处理的异常
  • 2017-05-28C++中几种将整数转换成二进制输出的方法总结
  • 2017-05-28C++基于递归和非递归算法判定两个二叉树结构是否完全相同(结构和数据都相同)
  • 2017-05-28C语言 二叉查找树性质详解及实例代码
  • 2017-05-28Prim(普里姆)算法求最小生成树的思想及C语言实例讲解
  • 2017-05-28在C++中自定义宏的简单方法
  • 2017-05-28ipv6实现tcp编程示例
  • 2017-05-28jquery ready函数深入分析

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • 对比C语言中execv相关的执行文件的三个函数
    • 详解C语言的exp()函数和ldexp()函数以及frexp()函数
    • 浅谈c和c++的某些小区别
    • 解析C语言基于UDP协议进行Socket编程的要点
    • C++编程中的格式化输出详解
    • 学习C和C++的9点经验总结
    • Linux环境下段错误的产生原因及调试方法小结
    • c++将数组名作为函数参数对数组元素进行相应的运算
    • VC++时钟函数
    • 深入const int *p与int * const p的区别详解(常量指针与指向常量的指针)

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有