简析利用调试寄存器实现内核函数的HOOK

安卓逆向环境检测--hook 安卓、逆向、检测 阅读详情

某些RK,木马会经常HOOK一些关键函数从而达到隐藏等目的,而相应的ARK检测软件也会通常会先恢复这些关键函数的HOOK(譬如利用硬盘文件恢复),然后再调用来检测RK,这样就可以检测出某些隐藏.下面就介绍利用调试器实现某些内核函数的HOOK.


Intel386以后的系列CPU增加了8个32位的调试寄存器,从Dr0到Dr7,方便调试使用.如果设置了相应的调试信息,在条件满足的情况下将会发生 1 号(DB例外)中断,CPU就会陷入中断例程,执行中断代码,我们的HOOK目的就可以通过这个实现.

首先看下面百度出来的对寄存器组的使用方法的解释:

这八个寄存器中由四个用于断点,两个用于控制,另两个保留未用。对这八个寄存器的访问,只能在0级特权级进行。在其它任何特权级对这八个寄存器中的任意一个寄存器进行读或写访问,都将产生无效操作码异常。此外,这八个寄存器还可用DR6及DR7中的BD位和GD位进行进一步的保护,使其即使是在0级也不能进行读出或写入。 

对这些寄存器的访问使用通常的MOV指令: 
MOV reg Dri 该指令将调试寄存器i中的内容读至通用寄存器reg中; 
MOV Dri reg 该指令将通用寄存器reg中的内容写至调试寄存器i中。此处i的取值可以为0至7中的任意值。 

这些寄存器的功能如下: 
DR0—DR3 寄存器DR0—DR3包含有与四个断点条件的每一个相联系的线性地址(断点条件则在DR7中)。因为这里使用的是线性地址,所以,断点设施的操作,无论分页机制是否启用,都是相同的。 
DR4—DR5 保留。 
DR6是调试状态寄存器。当一个调试异常产生时,处理器设置DR6的相应位,用于指示调试异常发生的原因,帮助调试异常处理程序分析、判断,以及作出相应处理。 
DR7是调试控制寄存器。分别对应四个断点寄存器的控制位,对断点的启用及断点类型的选择进行控制。所有断点寄存器的保护也在此寄存器中规定。(下面这图怎么也改不好,大家还是去百度吧)

    |---------------|----------------|
Dr6 |                               |BBB                     BBB B |
    |                               TSD                     3 2 1 0 |
    | --------------|----------------|
Dr7 |RWE LEN   ...     RWE LEN   | G               GLGLGLGLGL |
    | 3   3   ...           0   0   | D               EE33221100 |
    |---------------|----------------|
  31                               15                       0


DR6各位的功能
B0—B3(对应0-3位) 当断点线性地址寄存器规定的条件被检测到时,将对应的B0—B3位置1。置位B0—B3与断点条件是否被启用无关。即B0—B3的某位被置1,并不表示要进行对应的断点异常处理。 
BD(13位) 如下一条指令要对八个调试寄存器之一进行读或写时,则在指令的边界BD位置1。在一条指令内,每当即将读写调试寄存器时,也BD位置1。BD位置1与DR7中GD位启用与否无关。 
BS(14位) 如果单步异常发生时,BS位被置1。单步条件由EFLAGS寄存器中的TF位启用。如果程序由于单步条件进入调试处理程序,则BS位被置1。与DR6中的其它位不同的是,BS位只在单步陷阱实际发生时才置位,而不是检测到单步条件就置位。 
BT(15位) BT位对任务切换导致TSS中的调试陷阱位被启用而造成的调试异常,指示其原因。对这一条件,在DR7中没有启用位。 

DR6中的各个标志位,在处理机的各种清除操作中不受影响,因此,调试异常处理程序在运行以前,应清除DR6,以避免下一次检测到异常条件时,受到原来的DR6中状态位的影响。 

DR7各位的功能 
LEN LEN为一个两位的字段,用以指示断点的长度。每一断点寄存器对应一个这样的字段,所以共有四个这样的字段分别对应四个断点寄存器。LEN的四种译码状态对应的断点长度如下 

LEN 说明 
0 0 断点为一字节 
0 1 断点为两字节 
1 0 保留 
1 1 断点为四字节 

这里,如果断点是多字节长度,则必须按对应多字节边界进行对齐。如果对应断点是一个指令地址,则LEN必须为00 
RWE RWE也是两位的字段,用以指示引起断点异常的访问类型。共有四个RWE字段分别对应四个断点寄存器,RWE的四种译码状态对应的访问类型如下 

RWE 说明 
0 0 指令 
0 1 数据写 
1 0 保留 
1 1 数据读和写 

GE/LE GE/LE为分别指示准确的全局/局部数据断点。如果GE或LE被置位,则处理器将放慢执行速度,使得数据断点准确地把产生断点的指令报告出来。如果这些位没有置位,则处理器在执行数据写的指令接近执行结束稍前一点报告断点条件。建议读者每当启用数据断点时,启用LE或GE。降低处理机执行速度除稍微降低一点性能以外,不会引起别的问题。但是,对速度要求严格的代码区域除外。这时,必须禁用GE及LE,并且必须容许某些不太精确的调试异常报告。 
L0—L3/G0—G3 L0—L3及G0—G3位 
分别为四个断点寄存器的局部及全局启用信号。如果有任一个局部或全局启用位被置位,则由对应断点寄存器DRi规定的断点被启用。 
GD GD位启用调试寄存器保护条件。注意,处理程序在每次转入调试异常处理程序入口处清除GD位,从而使处理程序可以不受限制地访问调试寄存器。 

前述的各个L位(即LE,L0—L3)是有关任务的局部位,使调试条件只在特定的任务启用。而各个G位(即GD,G0—G3)是全局的,调试条件对系统中的所有任务皆有效。在每次任务切换时,处理器都要清除L位。 



  如果你耐心把上面的信息看完了,基本上也就应该明白了.其实我们可以利用调试寄存器做的不只是函数的HOOK,也可以进行I/O的HOOK,下面要说的是在指定的内核函数上下指令执行断点,然后挂接1号中断实现HOOK(有很多种方法可以实现,看你自己喜欢哪种了).

  首先把DR0寄存器设置为要挂接的内核函数的地址(譬如ZwCreateFile),然后修改 Dr7的L0和G0(第0和第1位)都为1, Dr7的 R/W0(16.17位) 为00, LEN0(18.19位)位为00,这样当CPU执行到ZwCreateFile地址的时候,就会进入1号中断例程.
  然后我们应该去修改IDT表,将1号中断指向我们的处理程序.
  接着需要考虑在中断例程我们要做的事情,关于中断时CPU具体做了什么,大家可以去搜索.下面只介绍我们所关心的在内核空间发生DB例外时的情况:中断发生时,依次将 EFLAGS,CS,EIP压入堆栈,然后进入中断程序(由于中断地址本来就在内核空间,所以不需要切换堆栈).在中断处理程序中作出相应的处理,然后使用 iretd 指令退出中断.( iretd 指令: 依次将堆栈弹出到 EIP,CS,EFLAGS),我们可以通过修改堆栈中EIP的值,在中断返回时跳转实现HOOK.

  上面就是主要的内容,但是还有点问题.Windows在KiFastCall和线程切换时会修改Drx的值,为了防止我们的断点被清除,可以利用Dr7:GD位保护寄存器,这样任何对调试寄存器的操作(读和写)都会产生DB例外然后进入1号中断例程.这样,我们在中断例程中又需要利用Dr6的标识位处理那些因为操作调试寄存器产生的例外(关于这个我只是简单的跳过了那些对DRX操作的代码,并没有详细分析).

下面可以看详细的实现代码:

简单实现HOOK下ZwCreateFile,XP下,其他系统慎用

/*
drxhook.h
Written By yykingking@126.com
*/

#ifndef _DRX_HOOK
#define _DRX_HOOK

#include <ntddk.h>

typedef unsigned long DWORD;
typedef unsigned char BOOL;

#pragma pack(push,1)
typedef struct _idtr
{
  //定义中断描述符表的限制,长度两字节;
  short     IDTLimit;
  //定义中断描述服表的基址,长度四字节;
  unsigned int   IDTBase;
}IDTR,*PIDTR;

typedef struct _IDTENTRY
{
  unsigned short LowOffset;
  unsigned short selector;
  unsigned char unused_lo;
  unsigned char segment_type:4;   //0x0E is an interrupt gate
  unsigned char system_segment_flag:1;
  unsigned char DPL:2;   // descriptor privilege level 
  unsigned char P:1; /* present */
  unsigned short HiOffset;
} IDTENTRY,*PIDTENTRY;

#pragma pack(pop)

DWORD GetDBEntry();
void HookDBInt();
void UnHookDBInt();

#endif


/*
drxhook.cpp
Written By yykingking@126.com
*/


#include "drxhook.h"

DWORD g_OldDBEntry;
IDTR g_IDTR;
DWORD g_OldCreateFile;
DWORD g_HookNumber = 0;
DWORD g_CR0;
BOOL g_bExit;

void ReLoadCR0AndSti()
{
  __asm
  {
    push   eax
        mov     eax, g_CR0
        mov     cr0, eax
        pop     eax
        sti
  }
}

void CliAndDisableWPBit()
{
  __asm
  {
    cli
        push   eax
        mov     eax, cr0
        mov     g_CR0, eax
        and     eax, 0xFFFEFFFF
        mov     cr0, eax
        pop     eax
  }
}

 

void PrintHook()
{
  DbgPrint(" Now Get In ZwCreateFile Hook: %d...Pid: %d.../n", g_HookNumber++, (DWORD)PsGetCurrentProcessId());
}

__declspec(naked) void NewZwCreateFile()
{
  __asm
  {   
    pushfd;                 // 仅仅适合于 XP 操作系统
    call PrintHook;
    popfd;
    mov eax,0x25;         
    jmp g_OldCreateFile;
  }
}

void SetHB()         // set hardware breakpoint 设置硬件断点
{
  __asm
  {
    mov eax, ZwCreateFile;     // 想要挂接的函数或者地址
    mov dr0, eax;
    mov eax, dr7;
    or eax, 0x2703;         // 也要修改 dr7:GD 位,以免DrX被操作系统或其他程序修改
    and eax, 0xfff0ffff;
    mov dr7, eax;
  }
}

__declspec(naked) void NewDBEntry()
{
  __asm
  {
    pushfd;
    push eax;

    mov eax, dr6;
    test eax, 0x2000;
    jz NOT_EDIT_DRX;

    // 以下是如果有对DRX的操作的简单处理,如有需要可以修改
    // 我只是简单的跳过这些指令
    and eax, 0xFFFFDFFF;
    mov dr6, eax;         // 清除DR6的标志

    cmp g_bExit, 0;
    jnz MY_DRV_EXIT;     // 驱动 Unload

    mov eax, [esp+8];     // 获取堆栈中的 EIP
    add eax, 3;           // 由于所有对 DRX 的操作全都是3个字节的
    mov [esp+8], eax;     // 修改 EIP ,跳过当前指令,返回时执行下条指令

    jmp MY_INT_END;

NOT_EDIT_DRX:

    mov eax, dr6;
    test eax, 0x1;
    jz SYS_INT;         // 如果不是Dr0 产生的中断,则跳回原系统中断

    mov eax, [esp+8];
    cmp eax, ZwCreateFile;   // 判断一下是不是 ZwCreateFile 的线性地址
    jnz SYS_INT;

    mov eax, NewZwCreateFile;
    mov [esp+8],eax;     // 修改堆栈中的 EIP ,实现返回时跳转


MY_INT_END:   

    mov eax, dr7;
    or eax, 0x2000;     // 恢复 GD 位
    mov dr7, eax;

MY_DRV_EXIT:             // 整个驱动 UnLoad 时,不恢复 Dr7

    pop eax;
    popfd;
    iretd;

SYS_INT:
    pop eax;
    popfd;
    jmp g_OldDBEntry;
    
  }
}

DWORD GetDBEntry()
{
  PIDTENTRY IdtEntry;
  DWORD Entry;

  __asm sidt g_IDTR;

  IdtEntry = (PIDTENTRY)(g_IDTR.IDTBase + 8);

  Entry = IdtEntry->HiOffset << 16;
  
  Entry |= IdtEntry->LowOffset;

  return Entry; 
}

void HookDBInt()
{
  DWORD NewEntry;
  PIDTENTRY IdtEntry;

  NewEntry = (DWORD)NewDBEntry;

  g_OldCreateFile = (DWORD)ZwCreateFile + 5;     // 新的要跳转过去的地址

  g_OldDBEntry = GetDBEntry();

  IdtEntry = (PIDTENTRY)(g_IDTR.IDTBase + 8);

  CliAndDisableWPBit();

  IdtEntry->LowOffset = (USHORT)NewEntry;

  IdtEntry->HiOffset = (USHORT)( NewEntry >> 16 );

  ReLoadCR0AndSti();

  SetHB();

  g_bExit = FALSE;

  return;
}

void UnHookDBInt()
{
  PIDTENTRY IdtEntry;
  DWORD Entry;
  
  __asm sidt g_IDTR;
  
  IdtEntry = (PIDTENTRY)(g_IDTR.IDTBase + 8);

  CliAndDisableWPBit();

  g_bExit = TRUE;

  __asm mov eax, dr7;           // 产生一次例外并且清除Dr7:GD

  if ( g_OldDBEntry != 0 )
  {
    IdtEntry->LowOffset = (USHORT)g_OldDBEntry;
    
    IdtEntry->HiOffset = (USHORT)( g_OldDBEntry >> 16 );     
  }

  ReLoadCR0AndSti();
  
  DbgPrint(" UnLoad drx hook../n");

  return;
}

NTSTATUS DriverUnload(IN PDRIVER_OBJECT DriverObject)
{   
  UnHookDBInt();
  
  return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(
              IN PDRIVER_OBJECT DriverObject,
              IN PUNICODE_STRING RegistryPath
              )
{
  HookDBInt();
  
  DriverObject->DriverUnload = DriverUnload;

  DbgPrint("Load drxhook Driver Ok.../n");

  return STATUS_SUCCESS;
}
/***********************/

以上代码实现了简单的ZwCreateFile 函数的HOOK,可以拿DbgView查看效果.

由于本人水平有限,代码中难免有错误出现,希望指正.
同时也希望各位牛人来指点,yykingking@126.com




/**************下面是罗嗦几句********************/
1.这个方法呢首先不怎么实用,因为你用了调试寄存器后某些壳也想用,因此就冲突了,可能会使某些东西失效,不如传统的HOOK好用(据说利用缺页中断HOOK也比较好用,没试过)。还有人认为呢这个HOOK虽然是HOOK成功了,但是还得HOOK中断向量,没有必要。其实呢,只是多了种思路罢了,多给大家提供一些想法而已。
2.其次呢,这个方法是我在调试某ARK时想到的,这个ARK的作者说他们会恢复函数的inline hook然后才去调用(大面积的恢复,甚至是整个文件的恢复),于是我就用调试器在该函数上下断点,结果自然是没有断下了,因为下的 (0xcc)断点被恢复了。于是就索性下了个硬件断点,这下就断住了,然后呢就想到了拿这个东西来HOOK。然后就去网上搜资料,发现不少人还是稍微提到过这个方法的,包括 vxk,xikug,都说过。

游戏修改器制作教程八:D3D函数hook 本章介绍怎么hook D3D函数,实现在游戏画面中显示自己的文字 阅读详情

相关推荐

三步走掌握内核级Inline Hook技术

在计算机系统中,内核级Inline Hook是一种强大的技术,它允许开发者在运行时修改操作系统内核函数的行为,而不需要改动原始的源代码。这在安全审计、性能优化、恶意软件分析等领域具有广泛的应用。Inline Hook使得在函数执行前后插入自定义代码成为可能,从而使开发者能观察或改变函数的行为,或者在特定的执行点触发一些额外的操作。原子操作在操作系统中有着明确的定义,它指的是在执行过程中不能被外部事件(如中断、多线程切换)打断的操作。

weixin_42573757的博客 1053

各种HOOK方式和检测对抗方法

hook翻译过来是拦截的意思, 我们很多时候也叫钩子,其实是很形象的.hook有什么作用呢?1.当代码执行到某行时,获取寄存器值和内存里的值,进行调试分析,例如hook明文包.2.当代码执行到某行时,插入想执行的代码.例如迅雷拦截发包函数.3.当代码执行到某行时,修改寄存器,达到某些篡改目的.

任鸟飞2217777779的博客 1万+

分享一个大神的hook示例,理论上能Hook任意地址和获取寄存器数据

分享一个大神的hook示例,理论上能Hook任意地址和获取寄存器数据

Hook 技术初探

一名优秀的Android开发,需要一份完备的 知识体系,在这里,让我们一起成长为自己所想的那样~。 Hook 技术 Hook 是钩子的意思,在 Android 操作系统中系统维护着自己的一套事件分发机制。应用程序,包括应用触发事件和后台逻辑处理,也是根据事件流程一步步地向下执行。 而钩子的意思,就是在事件传送到终点前截获监控事件的传输,像个钩子钩上事件一样,并且能够在钩上事件时,处理一些...

Android 开发架构 983

C++Hook发包函数核心代码

C++Hook发包函数核心代码 游戏辅助专用 不得不学 杀猪的

Hook函数任意地址 c++

该实例实现了Hook函数任意地址的功能,并可以获取CPU寄存器的内容。

简析利用调试器寄存器实现内核函数的HOOK

简析利用调试寄存器实现内核函数的HOOK创建时间:2007-09-23文章转自:www.xfocus.net文章提交:yykingking (yykingking_at_126.com)某些RK,木马会经常HOOK一些关键函数从而达到隐藏等目的,而相应的ARK检测软件也会通常会先恢复这些关键函数的HOOK(譬如利用硬盘文件恢复),然后再调用来检测RK,这样就可以检测出某些隐藏.下面就介绍利用调

yuntianhai的专栏 651

伪任意地址HOOK类

代码HOOK技术不管在安全领域还是在木马病毒方面都运用很广泛,因为他可以改变程序执行流程,悄无声息执行我们自己的代码。 之前我也用过一些hook类,如mhook等,但很多都局限于API HOOK,有的时候无法满足实际应用。也因此,我自己的hook类就诞生了。 详细说明: http://blog.youkuaiyun.com/sunflover454/article/details/49029615

【Linux内核调试】使用Ftrace来Hook linux内核函数

目标:hook几个Linux内核函数调用,如打开文件和启动进程,并利用它来启用系统活动监控并抢先阻止可疑进程。 一、方案比较 1. 使用Linux安全API 方法:内核代码的关键点包含安全函数调用,这些调用可能触发安全模块安装的回调,该模块可以分析特定操作的上下文,并决定是允许还是禁止它。 限制:安全模块无法动态加载,所以需要重新编译内核。 2. 修改系统调用表 方法:所有Linux系统调用处理程...

panhewu9919的博客 1934

APIHook、InlineHook库,使用C++11编写,可将回调函数绑定到类成员函数

APIHook、InlineHook库,使用C++11编写,可将回调函数绑定到类成员函数。代码风格良好,适合学习。

PLL锁相环性能上不去?可能是电荷泵的电流失配在捣鬼(附Jae-Shin Lee经典方案解析)

本文深入分析了PLL锁相环性能瓶颈的根源——电荷泵电流失配问题,详细解析了Current Mismatch和Current Deviation的区别及其对系统的影响。重点介绍了Jae-Shin Lee的多级电流镜技术,该方案通过运放构建虚拟短路和四级电流镜结构,显著降低了电流失配,使参考杂散改善15dB以上。文章还提供了从仿真到流片的完整优化流程,帮助工程师系统解决电荷泵性能问题。

weixin_30684743的博客 370

linux内核中的hook,【Linux内核调试】使用Ftrace来Hook linux内核函数

目标:hook几个Linux内核函数调用,如打开文件和启动进程,并利用它来启用系统活动监控并抢先阻止可疑进程。一、方案比较1. 使用Linux安全API方法:内核代码的关键点包含安全函数调用,这些调用可能触发安全模块安装的回调,该模块可以分析特定操作的上下文,并决定是允许还是禁止它。限制:安全模块无法动态加载,所以需要重新编译内核。2. 修改系统调用表方法:所有Linux系统调用处理程序都存储在s...

weixin_32050773的博客 697

【深度解析】AURIX TC387 UCB Flash架构与避坑指南(第一篇)

摘要:UCB(用户配置块)是AURIX TC3xx芯片启动与安全的关键配置区域,位于DFLASH0存储区。每个512字节的UCB包含启动模式、安全设置等关键参数,并通过强制CRC32校验确保完整性。UCB分为启动头(UCB_BMHD)、安全配置(UCB_ORIG)及其备份(UCB_COPY)三种主要类型。不同于常规Flash,UCB具有自动擦除特性,错误配置可能导致芯片永久锁死。开发中应谨慎修改UCB,必须同时编程原始和备份版本,并确保正确的CRC校验。后续文章将详细介绍UCB编程方法和调试技巧。

2511_94612429的博客 1234

代码注入之Hook注入

介绍通过Hook实现代码注入的方法,具体给出了两种非代码Hook方式的具体实现,即Hook内核回调表和Hook IP寄存器,最后给出了检测这类Hook的建议。

leegh0000的博客 2150

HALCON 20.11实战:5步搞定工业图像异常检测(附完整代码)

本文提供了一份基于HALCON 20.11的工业图像异常检测实战指南。通过五个核心步骤——环境准备、数据预处理、模型训练、推理解析与工程化部署,详细演示了如何利用深度学习技术,仅使用正常样本快速构建并优化金属表面划痕等缺陷的检测模型,并附有完整可运行的HDevelop代码,助力工程师实现高效、智能的工业视觉质检。

weixin_29243279的博客 140

HIDL概述

HIDL背景 Treble 是 Google Android 团队的一项重大项目,意在 Android 操作系统框架在架构方面的一项重大改变,旨在让制造商以更低的成本更轻松、更快速地将设备更新到新版 Android 系统。Android 7.x 及更早版本中没有正式的供应商接口,因此设备制造商必须更新大量 Android 代码才能将设备更新到新版 Android 系统: 图1 Treble ...

u013357557的专栏 2万+

hook Linux 内核函数

可以尝试使用如下几种方案来拦截Linux内核函数: 使用Linux安全API 修改系统调用表 使用kprobes 拼接 使用ftrace处理程序 使用Linux安全API 从最佳实践的角度来说,我们认为使用Linux安全API的hook函数是最佳选择,因为这个接口就是为此而设计的。 内核代码的关键点包含安全函数调用,这些调用可能导致安全模块安装的回调。该模块可以研究特定操作的上下文,并决定是允许还是禁止它。不幸的是,Linux Security API有两个主要限制: 安全模块无法动态加载,因此我们需

m0_46671092的博客 666

Silvaco TCAD 2018安装后,如何配置TonyPlot 5.0.22.R并解决绘图报错?

本文详细介绍了在Windows系统上安装Silvaco TCAD 2018后,如何正确配置TonyPlot 5.0.22.R并解决常见的绘图报错问题。内容包括版本兼容性分析、环境变量设置、常见错误解决方案以及性能优化建议,帮助用户顺利完成仿真结果的可视化工作。

weixin_31031711的博客 420
上一篇: 用ASP实现反向连接控制
下一篇: 优化C++程序编译效率的实例
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值