硬件断点 DrxHook

Posted 狂客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了硬件断点 DrxHook相关的知识,希望对你有一定的参考价值。

硬件断点 DrxHook

硬件断点的实现需要依赖于调试寄存器

DR0~DR7  调试寄存器

DR0~DR3-----调试地址寄存器
DR4~DR5-----保留
DR6 -----调试状态寄存器 指示哪个调试寄存器被命中
DR7 -----调试控制寄存器

 

关于Dr7寄存器每个标志位的解释:

 

总结如下

DR7调试控制寄存器:
R/W0~R/W3:与DR0~DR3相对应,用来指定监控地址的访问类型,表示意义如下:
              00:仅当执行对应的地址时中断
              01:仅当写入对应的地址时中断
              10:基本不用
              11:读取对应的地址时中断,读取指令的指令除外

 

LEN0~LEN3:与DR0~DR3相对应,用来指定监控地址的长度,意义如下:
               00:一个字节长
               01:两个字节长
               10:未定义或者代表8字节,具体视CPU而定
               11:四个字节长

L0~L3:与DR0~DR3相对应,意思表示仅对当前

 

接下来看看两个Windows提供的两个API函数,GetThreadContext和SetThreadContext来获取或者时设置线程的上下文,什么是线程的上下文,就是线程运行时的各种寄存器的信息,比如调试寄存器,浮点寄存器,控制寄存器等等,在应用层是不能直接操作Drx寄存器的值,但可以调用这两个api来读写线程的上下文,来达到设置硬件断点的目的,如果想对某个进程设置硬件断点,就需要对它的每个线程都调用SetThreadContext()函数,设置Drx寄存器的值。

BOOL WINAPI GetThreadContext(
  __in     HANDLE hThread,
  __inout  LPCONTEXT lpContext
);
BOOL WINAPI SetThreadContext(
  __in  HANDLE hThread,
  __in  const CONTEXT *lpContext
);

 在SSDT表中对应的NtGetContextThread和NtSetContextThread

NTSTATUS
NtGetContextThread(
    __in HANDLE ThreadHandle,
    __inout PCONTEXT ThreadContext
    )

 

NTSTATUS
NtSetContextThread(
    __in HANDLE ThreadHandle,
    __in PCONTEXT ThreadContext
    )

 

这两个函数都是调用了PsSet/GetContextThread函数

复制代码
NTSTATUS
PsSetContextThread(
    __in PETHREAD Thread,
    __in PCONTEXT ThreadContext,
    __in KPROCESSOR_MODE Mode
    )

NTSTATUS
PsGetContextThread(
    __in PETHREAD Thread,
    __inout PCONTEXT ThreadContext,
    __in KPROCESSOR_MODE Mode
    )
复制代码

 

我们可以对PsGet/SetContextTread函数Hook来达到对设置硬件断点的一个过滤,对于目标进程获取或者设置线程的Context进行处理。

复制代码
oid __stdcall FilterSetGetContextThread(
    PETHREAD Thread,
    PCONTEXT Context,
    KPROCESSOR_MODE AccessMode)
{
    __try{
        if (AccessMode == UserMode)
        {
            //wrk 参数校验
            ProbeForReadSmallStructure(Context,sizeof(CONTEXT),PROBE_ALIGNMENT(CONTEXT));
        }else{
            *Context = *Context;
        }

        if (strstr(GetProcessNameByThread(Thread),"test.exe")!=NULL)
        {
            if (strstr((char*)PsGetCurrentProcess()+0x16c,"ollydbg") != NULL)
            {
                return;
            }
            //如果是要获得调试寄存器的值,将Flags 的获得调试寄存器清零,

            if (Context->ContextFlags | CONTEXT_DEBUG_REGISTERS)
            {
                Context->ContextFlags = ~CONTEXT_DEBUG_REGISTERS;
            }
        }

    }__except(EXCEPTION_EXECUTE_HANDLER){
        return;
    }
}
复制代码

 

复制代码
typedef NTSTATUS (*PSGETCONTEXTTHREAD)(
    PETHREAD Thread,
    PCONTEXT Context,
    KPROCESSOR_MODE AccessMode);

typedef NTSTATUS (*PSSETCONTEXTTHREAD)(
    PETHREAD Thread,
    PCONTEXT Context,
    KPROCESSOR_MODE AccessMode);

//global
PSGETCONTEXTTHREAD PsGetContextThread;
PSSETCONTEXTTHREAD PsSetContextThread;

ULONG    g_JmpGetContextThread;
UCHAR    g_cGetContextCode[5];
BOOLEAN    g_bHookGetContextSuccess;
ULONG    g_JmpSetContextThread;
UCHAR    g_cSetContextCode[5];
BOOLEAN    g_bHookSetContextSuccess;

char* GetProcessNameByThread(PETHREAD Thread)
{
    ULONG    ProcessObj;
    if (MmIsAddressValid(Thread))
    {
        ProcessObj = *(ULONG*)((ULONG)Thread + 0x150);
        return (char*)(ProcessObj+0x16C);
    }
    return 0;
}

void PageProtectOn()
{
    __asm{//恢复内存保护  
        mov  eax,cr0
        or   eax,10000h
        mov  cr0,eax
        sti
    }
}

void PageProtectOff()
{
    __asm{//去掉内存保护
        cli
        mov  eax,cr0
        and  eax,not 10000h
        mov  cr0,eax
    }
}

BOOLEAN    Jmp_HookFunction(
    IN ULONG Destination,
    IN ULONG Source,
    IN UCHAR *Ori_Code
    )
{
    ULONG    Jmp_Offest;
    UCHAR    Jmp_Code[5] = {0xE9};

    KSPIN_LOCK lock;
    KIRQL irql;

    if (Destination==0||Source==0)
    {
        DbgPrint("Params error!");
        return FALSE;
    }
    RtlCopyMemory(Ori_Code,(PVOID)Destination,5);
    Jmp_Offest = Source - Destination-5;
    *(ULONG*)&Jmp_Code[1] = Jmp_Offest;
    
    KeInitializeSpinLock (&lock );
    KeAcquireSpinLock(&lock,&irql);

    PageProtectOff();
    RtlCopyMemory((PVOID)Destination,Jmp_Code,5);
    PageProtectOn();

    KeReleaseSpinLock (&lock,irql);

    return TRUE;
}

VOID Res_HookFunction(
    IN ULONG    Destination,
    IN UCHAR    *Ori_Code,
    IN ULONG    Length
    )
{
    KSPIN_LOCK lock;
    KIRQL irql;

    if (Destination==0||Ori_Code==0){    return;    }

    KeInitializeSpinLock (&lock );
    KeAcquireSpinLock(&lock,&irql);

    PageProtectOff();
    RtlCopyMemory((PVOID)Destination,Ori_Code,Length);
    PageProtectOn();

    KeReleaseSpinLock (&lock,irql);
}

FORCEINLINE
    VOID
    ProbeForReadSmallStructure (
    IN PVOID Address,
    IN SIZE_T Size,
    IN ULONG Alignment
    )  //wrk源码
{
    ASSERT((Alignment == 1) || (Alignment == 2) ||
        (Alignment == 4) || (Alignment == 8) ||
        (Alignment == 16));

    if ((Size == 0) || (Size >= 0x10000)) {

        ASSERT(0);

        ProbeForRead(Address, Size, Alignment);

    } else {
        if (((ULONG_PTR)Address & (Alignment - 1)) != 0) {
            ExRaiseDatatypeMisalignment();
        }

        if ((PUCHAR)Address >= (UCHAR * const)MM_USER_PROBE_ADDRESS) {
            Address = (UCHAR * const)MM_USER_PROBE_ADDRESS;
        }

        _ReadWriteBarrier();
        *(volatile UCHAR *)Address;
    }
}


void __stdcall FilterSetGetContextThread(
    PETHREAD Thread,
    PCONTEXT Context,
    KPROCESSOR_MODE AccessMode)
{
    __try{
        if (AccessMode == UserMode)
        {
            //wrk 参数校验
            ProbeForReadSmallStructure(Context,sizeof(CONTEXT),PROBE_ALIGNMENT(CONTEXT));
        }else{
            *Context = *Context;
        }

        if (strstr(GetProcessNameByThread(Thread),"test.exe")!=NULL)
        {
            if (strstr((char*)PsGetCurrentProcess()+0x16c,"ollydbg") != NULL)
            {
                return;
            }
            //如果是要获得调试寄存器的值,将Flags 的获得调试寄存器清零,

            if (Context->ContextFlags | CONTEXT_DEBUG_REGISTERS)
            {
                Context->ContextFlags = ~CONTEXT_DEBUG_REGISTERS;
            }
        }

    }__except(EXCEPTION_EXECUTE_HANDLER){
        return;
    }
}

void __declspec(naked) NewGetContextThread()
{
    __asm{
        mov        edi,edi
        push    ebp
        mov        ebp,esp

        push    [ebp+0x10]
        push    [ebp+0xc]
        push    [ebp+0x8]
        call    FilterSetGetContextThread

        mov        esp,ebp
        pop        ebp

        mov        edi,edi
        push    ebp
        mov        ebp,esp
        jmp        g_JmpGetContextThread
    }
}

void __declspec(naked) NewSetContextThread()
{
    __asm{
        mov        edi,edi
        push    ebp
        mov        ebp,esp

        push    [ebp+0x10]
        push    [ebp+0xc]
        push    [ebp+0x8]
        call    FilterSetGetContextThread

        mov        esp,ebp
        pop        ebp

        mov        edi,edi
        push    ebp
        mov        ebp,esp
        jmp        g_JmpSetContextThread
    }
}

void HookSetGetContextThread()
{
    g_JmpGetContextThread = (ULONG)PsGetContextThread + 0x5;
    g_bHookGetContextSuccess = Jmp_HookFunction((ULONG)PsGetContextThread,(ULONG)NewGetContextThread,g_cGetContextCode);

    g_JmpSetContextThread = (ULONG)PsSetContextThread + 0x5;
    g_bHookSetContextSuccess = Jmp_HookFunction((ULONG)PsSetContextThread,(ULONG)NewSetContextThread,g_cSetContextCode);
}

void UnHookSetGetContextThread()
{
    if (g_bHookGetContextSuccess)
    {
        Res_HookFunction((ULONG)PsGetContextThread,g_cGetContextCode,5);
    }

    if (g_bHookSetContextSuccess)
    {
        Res_HookFunction((ULONG)PsSetContextThread,g_cSetContextCode,5);
    }
}

void DriverUnLoad(PDRIVER_OBJECT pDriverObject)
{
    UnHookSetGetContextThread();
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING usRegistPath)
{
    UNICODE_STRING    usFuncName1,usFuncName2;

    RtlInitUnicodeString(&usFuncName1,L"PsGetContextThread");
    RtlInitUnicodeString(&usFuncName2,L"PsSetContextThread");

    PsGetContextThread = (PSGETCONTEXTTHREAD)MmGetSystemRoutineAddress(&usFuncName1);
    PsSetContextThread = (PSSETCONTEXTTHREAD)MmGetSystemRoutineAddress(&usFuncName2);

    HookSetGetContextThread();

    pDriverObject->DriverUnload = DriverUnLoad;
    return STATUS_SUCCESS;
}
复制代码

 

 

 

在ring0可以直接改变Drx寄存器的值,设置硬件断点来达到对某个函数的hook,这里以NtOpenProcess为例。我们知道内核层产生的异常都会由RtlDispatchException函数进行分发处理,所以我们首先要InlineHook RtlDispatchException函数,在对异常进行分发时先对异常进行过滤,如果异常地址是我们设置的硬件断点“监控”的地址,则改变EIP,达到对目标函数“hook”的目的。

先InlineHook RtlDispatchException ,RtlDispatchException未导出,在KiDispatchException中被调用,KiDispatchException也未导出,采用的方法是暴力搜索整个内核文件Ntoskrnl.exe来匹配特征码

复制代码
VOID HookRtlDispatchException()
{

    PLDR_DATA_TABLE_ENTRY Ldr = NULL;
    //构建RtlDispatchException 的特征码
    //     nt!KiDispatchException+0x160:
    //     83eff040 53              push    ebx
    //     83eff041 ff750c          push    dword ptr [ebp+0Ch]
    //     83eff044 ff7510          push    dword ptr [ebp+10h]
    //     83eff047 ff15bc49fb83    call    dword ptr [nt!KiDebugRoutine (83fb49bc)]
    //     83eff04d 84c0            test    al,al
    //     83eff04f 0f859d000000    jne     nt!KiDispatchException+0x211 (83eff0f2)
    //     83eff055 57              push    edi
    //     83eff056 53              push    ebx
    //     83eff057 e8 a372ffff      call    nt!RtlDispatchException (83ef62ff)


    //     kd> u 83ef62ff
    //     nt!RtlDispatchException:
    //     83ef62ff 8bff            mov     edi,edi
    //     83ef6301 55              push    ebp
    //     83ef6302 8bec            mov     ebp,esp


    //     83ef6304 83e4f8          and     esp,0FFFFFFF8h
    //     83ef6307 83ec6c          sub     esp,6Ch
    //     83ef630a 53              push    ebx
    //     83ef630b 56              push    esi
    //     83ef630c 57              push    edi


    SIGNATURE_INFO SignCode[] = {{0x84,10},{0xc0,9},{0x57,2},{0x53,1},{0xE8,0}};
#ifndef _DEBUG
    __asm int 3
#endif 

    g_bHookSuccess  = FALSE;
    Ldr = SearchDriver(g_LocalDriverObj,L"ntoskrnl.exe");
    if (!Ldr)   return;
    g_RtlDispatchExeceptionAddress = SearchAddressForSignFromPE((ULONG_PTR)(Ldr->DllBase),Ldr->SizeOfImage,SignCode);    
    if (!MmIsAddressValid((PVOID)g_RtlDispatchExeceptionAddress))  return;
    //利用偏移转成绝对地址                                    +5 过e8 a372ffff 这五个字节
    g_RtlDispatchExeceptionAddress = g_RtlDispatchExeceptionAddress+5 + *(ULONG_PTR*)(g_RtlDispatchExeceptionAddress+1);
    //过被占的前5个字节,继续执行的代码
    DbgPrint("RtlDispatchExceptionAddresss:%x",g_RtlDispatchExeceptionAddress);
    g_JmpOrigDispatchException = g_RtlDispatchExeceptionAddress + 5;
    g_bHookSuccess = Jmp_HookFunction(g_RtlDispatchExeceptionAddress,(ULONG_PTR)NewRtlDispatchException,g_cDisExceptionCode);
}
复制代码

 

然后将要“监控”的目标地址写入Dr0寄存器,当目标地址被执行的时候,触发异常,执行RtlDispatchException函数,

复制代码
VOID SetMonitor(PVOID Address)
{

    __asm
    {
        mov eax , Address
        mov DR0 , eax
        mov eax , 0x02  //全局的,仅当执行时产生异常
        mov DR7 , eax
    }
}


VOID CancelMonitor(PVOID Address)
{

    __asm
    {
        xor eax , eax
        mov DR0 , eax
        mov DR7 , eax
    }
}
复制代码

 

RtlDispatchException的过滤函数,在这里改变Eip的值,异常处理完毕以后,开始执行NewNtOpenProcess 

复制代码
ULONG_PTR _stdcall
    FilterRtlDispatchException (
    IN PEXCEPTION_RECORD ExceptionRecord,
    IN PCONTEXT ContextRecord
    )
{

    //DbgPrint("Address:%x -- ExceptionCode:%x\\r\\n",ExceptionRecord->ExceptionAddress,ExceptionRecord->ExceptionCode);
    //如果是NtOpenProcess处的异常
    if (ExceptionRecord->ExceptionAddress == (PVOID)KeServiceDescriptorTable.ServiceTableBase[190])
    {
        KdPrint(("<Except addresss>:%X <seh callBack>:%X -- <Except code>:%X",
            ContextRecord->Eip,ExceptionRecord->ExceptionAddress,ExceptionRecord->ExceptionCode));
        //将执行的下一条指令置为NewNtOpenProcess() 函数的地址,CPU接着去执行NewNtOpenProcess
        ContextRecord->Eip = (ULONG_PTR)NewNtOpenProcess;
        //返回TRUE,异常不再进行派发
        return 1;
    }
    return 0;
}
复制代码

 

NewNtOpenProcess只是简单地调用FilterNtOpenProcess进行简单的调用,打印一些基本的信息。

复制代码
void __declspec(naked)  NewNtOpenProcess()
{

    __asm
    {
        pushad
        pushfd

        call FilterNtOpenProcess

        popfd
        popad

        mov        edi , edi
        push       esp
        mov        ebp , esp
        //跳过NtOpenProcess的前五个字节,
        //避免再次触发异常
        jmp        g_JmpOrigNtOpenProcess
    }
}
复制代码

 

完整的工程代码

复制代码
#ifndef CXX_DRXHOOK_H
#define CXX_DRXHOOK_H



#include <ntifs.h>
#include <devioctl.h>
#endif    

typedef struct _SYSTEM_SERVICE_TABLE32 {
    ULONG_PTR*   ServiceTableBase;
    ULONG_PTR*   ServiceCounterTableBase;
    ULONG32 NumberOfServices;
    ULONG_PTR*   ParamTableBase;
} SYSTEM_SERVICE_TABLE32, *PSYSTEM_SERVICE_TABLE32;

typedef struct _SYSTEM_SERVICE_TABLE64{
    ULONG_PTR*         ServiceTableBase; 
    ULONG_PTR*         ServiceCounterTableBase; 
    ULONG64          NumberOfServices; 
    ULONG_PTR*         ParamTableBase; 
} SYSTEM_SERVICE_TABLE64, *PSYSTEM_SERVICE_TABLE64;

#ifndef _WIN64
#define        _SYSTEM_SERVICE_TABLE   _SYSTEM_SERVICE_TABLE64
#define        SYSTEM_SERVICE_TABLE     SYSTEM_SERVICE_TABLE64
#define        PSYSTEM_SERVICE_TABLE     PSYSTEM_SERVICE_TABLE64
#else
#define        _SYSTEM_SERVICE_TABLE   _SYSTEM_SERVICE_TABLE32
#define        SYSTEM_SERVICE_TABLE     SYSTEM_SERVICE_TABLE32
#define        PSYSTEM_SERVICE_TABLE     PSYSTEM_SERVICE_TABLE32
#endif



__declspec(dllimport) SYSTEM_SERVICE_TABLE KeServiceDescriptorTable;

//结构声明
typedef struct _SIGNATURE_INFO{
    UCHAR    cSingature;
    int        Offset;
}SIGNATURE_INFO,*PSIGNATURE_INFO;


typedef struct _LDR_DATA_TABLE_ENTRY                         // 24 elements, 0x78 bytes (sizeof) 
{                                                                                                
    /*0x000*/     struct _LIST_ENTRY InLoadOrderLinks;       // 2 elements, 0x8 bytes (sizeof)   
    /*0x008*/     PVOID ExceptionTable;  
    /*0x00C*/      ULONG ExceptionTableSize;
    /*0x010*/     struct _LIST_ENTRY InInitializationOrderLinks; // 2 elements, 0x8 bytes (sizeof)   
    /*0x018*/     VOID*        DllBase;                                                                        
    /*0x01C*/     VOID*        EntryPoint;                                                                     
    /*0x020*/     ULONG32      SizeOfImage;                                                                    
    /*0x024*/     struct _UNICODE_STRING FullDllName;             // 3 elements, 0x8 bytes (sizeof)   
    /*0x02C*/     struct _UNICODE_STRING BaseDllName;             // 3 elements, 0x8 bytes (sizeof)   
    /*0x034*/     ULONG32      Flags;                                                                          
    /*0x038*/     UINT16       LoadCount;                                                                      
    /*0x03A*/     UINT16       TlsIndex;                                                                       
    union                                                    // 2 elements, 0x8 bytes (sizeof)   
    {                                                                                            
    /*0x03C*/     struct _LIST_ENTRY HashLinks;           // 2 elements, 0x8 bytes (sizeof)   
        struct                                          // 2 elements, 0x8 bytes (sizeof)   
        {                                                                                        
            /*0x03C*/             VOID*        SectionPointer;                                                         
            /*0x040*/             ULONG32      CheckSum;                                                               
        };                                                                                       
    };                                                                                           
    union                                                    // 2 elements, 0x4 bytes (sizeof)   
    {                                                                                            
        /*0x044*/         ULONG32      TimeDateStamp;                                                              
        /*0x044*/         VOID*        LoadedImports;                                                              
    };                                                                                           
    /*0x048*/     VOID* EntryPointActivationContext;                                     
    /*0x04C*/     VOID*        PatchInformation;                                                               
    /*0x050*/     struct _LIST_ENTRY ForwarderLinks;                       // 2 elements, 0x8 bytes (sizeof)   
    /*0x058*/     struct _LIST_ENTRY ServiceTagLinks;                      // 2 elements, 0x8 bytes (sizeof)   
    /*0x060*/     struct _LIST_ENTRY StaticLinks;                          // 2 elements, 0x8 bytes (sizeof)   
    /*0x068*/     VOID*        ContextInformation;                                                             
    /*0x06C*/     ULONG32      OriginalBase;                                                                   
    /*0x070*/     union _LARGE_INTEGER LoadTime;                           // 4 elements, 0x8 bytes (sizeof)   
}LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;


ULONG_PTR _stdcall
    FilterRtlDispatchException (
    IN PEXCEPTION_RECORD ExceptionRecord,
    IN PCONTEXT ContextRecord
    );
VOID HookRtlDispatchException();
VOID UnloadDriver(PDRIVER_OBJECT DriverObject);
PLDR_DATA_TABLE_ENTRY SearchDriver(PDRIVER_OBJECT pDriverObject,wchar_t *strDriverName);
BOOLEAN    Jmp_HookFunction(IN ULONG Destination,IN ULONG Source,IN UCHAR *Ori_Code);
VOID ResumeHookFunction(IN ULONG    Destination,IN UCHAR    *Ori_Code,IN ULONG    Length);
ULONG_PTR SearchAddressForSignFromPE(ULONG_PTR uStartBase,
                 ULONG_PTR uSearchLength,
                 SIGNATURE_INFO SignatureInfo[5]);
VOID WPOFF();
NTSTATUS  _stdcall FilterNtOpenProcess ();
VOID WPON();
VOID SetMonitor(PVOID Address);
VOID CancelMonitor(PVOID Address);






#ifndef CXX_DRXHOOK_H
#    include "DrxHook.h"
#endif

#include <ntimage.h>


KIRQL  Irql;
PDRIVER_OBJECT g_LocalDriverObj;
BOOLEAN        g_bHookSuccess;
ULONG_PTR      g_RtlDispatchExeceptionAddress;
ULONG_PTR      g_JmpOrigDispatchException;
UCHAR           g_cDisExceptionCode[5];

ULONG_PTR g_JmpOrigNtOpenProcess;

void __declspec(naked)  NewNtOpenProcess()
{

    __asm
    {
        pushad
        pushfd

        call FilterNtOpenProcess

        popfd
        popad

        mov        edi , edi
        push       esp
        mov        ebp , esp
        //跳过NtOpenProcess的前五个字节,
        //避免再次触发异常
        jmp        g_JmpOrigNtOpenProcess
    }
}


void __declspec(naked) NewRtlDispatchException()
{
    __asm
    {
        mov   edi,edi
        push  ebp
        mov   ebp , esp
        pushad     //保存所有寄存器
        pushfd     //保存标志寄存器
        push    [ebp+0xc]
        push    [ebp+0x8]
        call    FilterRtlDispatchException
        //检测返回值是否为0
        test    eax , eax
        jz        __SafeExit  // 若eax为0 跳转__SafeExit
        popfd
        popad
        mov        esp , ebp
        pop        ebp
        //  将KiDispatchException中对于RtlDispatchException的返回值进行校验,
        //  如果为0 则对异常进行重新派发,为1则不再做处理
        mov        eax ,0x01   
        retn    0x8     //平衡堆栈,两个参数8字节

__SafeExit:

        popfd
        popad
        mov        esp , ebp
        pop        ebp

        //先执行RtlDispatchException原来的5个字节的内容
        mov        edi , edi
        push    ebp
        mov        ebp , esp
        jmp g_JmpOrigDispatchException
    }
}


NTSTATUS  _stdcall FilterNtOpenProcess ()
{
    DbgPrint("FilterNtOpenProcess---%s\\r\\n",(ULONG_PTR)PsGetCurrentProcess()+0x16c);
    return  STATUS_SUCCESS;
}



ULONG_PTR _stdcall
    FilterRtlDispatchException (
    IN PEXCEPTION_RECORD ExceptionRecord,
    IN PCONTEXT ContextRecord
    )
{

    //DbgPrint("Address:%x -- ExceptionCode:%x\\r\\n",ExceptionRecord->ExceptionAddress,ExceptionRecord->ExceptionCode);
    //如果是NtOpenProcess处的异常
    if (ExceptionRecord->ExceptionAddress == (PVOID)KeServiceDescriptorTable.ServiceTableBase[190])
    {
        KdPrint(("<Except addresss>:%X <seh callBack>:%X -- <Except code>:%X",
            ContextRecord->Eip,ExceptionRecord->ExceptionAddress,ExceptionRecord->ExceptionCode));

        //将执行的下一条指令置为NewNtOpenProcess() 函数的地址,CPU接着去执行NewNtOpenProcess
        ContextRecord->Eip = (ULONG_PTR)NewNtOpenProcess;
        //返回TRUE,异常不再进行派发
        return 1;
    }
    return 0;
}
VOID SetMonitor(PVOID Address)
{

    __asm
    {
        mov eax , Address
        mov DR0 , eax
        mov eax , 0x02  //全局的,仅当执行时产生异常
        mov DR7 , eax
    }
}

VOID CancelMonitor(PVOID Address)
{

    __asm
    {
        xor eax , eax
        mov DR0 , eax
        mov DR7 , eax
    }
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING RegistryString)
{
    NTSTATUS    Status = STATUS_SUCCESS;
    g_LocalDriverObj = pDriverObject;
    HookRtlDispatchException();
    g_JmpOrigNtOpenProcess = (ULONG_PTR)(KeServiceDescriptorTable.ServiceTableBase[190] + 0x5);
    //为了方便,这里写死了,NtOpenProcess  Win7 x86 
    SetMonitor((PVOID)KeServiceDescriptorTable.ServiceTableBase[190]);
    return Status;
}

VOID HookRtlDispatchException()
{

    PLDR_DATA_TABLE_ENTRY Ldr = NULL;
    //构建RtlDispatchException 的特征码
    //     nt!KiDispatchException+0x160:
    //     83eff040 53              push    ebx
    //     83eff041 ff750c          push    dword ptr [ebp+0Ch]
    //     83eff044 ff7510          push    dword ptr [ebp+10h]
    //     83eff047 ff15bc49fb83    call    dword ptr [nt!KiDebugRoutine (83fb49bc)]
    //     83eff04d 84c0            test    al,al
    //     83eff04f 0f859d000000    jne     nt!KiDispatchException+0x211 (83eff0f2)
    //     83eff055 57              push    edi
    //     83eff056 53              push    ebx
    //     83eff057 e8 a372ffff      call    nt!RtlDispatchException (83ef62ff)


    //     kd> u 83ef62ff
    //     nt!RtlDispatchException:
    //     83ef62ff 8bff            mov     edi,edi
    //     83ef6301 55              push    ebp
    //     83ef6302 8bec            mov     ebp,esp


    //     83ef6304 83e4f8          and     esp,0FFFFFFF8h
    //     83ef6307 83ec6c          sub     esp,6Ch
    //     83ef630a 53              push    ebx
    //     83ef630b 56              push    esi
    //     83ef630c 57              push    edi


    SIGNATURE_INFO SignCode[] = {{0x84,10},{0xc0,9},{0x57,2},{0x53,1},{0xE8,0}};
#ifndef _DEBUG
    __asm int 3
#endif 

    g_bHookSuccess  = FALSE;
    Ldr = SearchDriver(g_LocalDriverObj,L"ntoskrnl.exe");
    if (!Ldr)   return;
    g_RtlDispatchExeceptionAddress = SearchAddressForSignFromPE((ULONG_PTR)(Ldr->DllBase),Ldr->SizeOfImage,SignCode);    
    if (!MmIsAddressValid((PVOID)g_RtlDispatchExeceptionAddress))  return;
    //利用偏移转成绝对地址                                    +5 过e8 a372ffff 这五个字节
    g_RtlDispatchExeceptionAddress = g_RtlDispatchExeceptionAddress+5 + *(ULONG_PTR*)(g_RtlDispatchExeceptionAddress+1);
    //过被占的前5个字节,继续执行的代码
    DbgPrint("RtlDispatchExceptionAddresss:%x",g_RtlDispatchExeceptionAddress);
    g_JmpOrigDispatchException = g_RtlDispatchExeceptionAddress + 5;
    g_bHookSuccess = Jmp_HookFunction(g_RtlDispatchExeceptionAddress,(ULONG_PTR)NewRtlDispatchException,g_cDisExceptionCode);
}

//搜索整个PE文件的
ULONG_PTR SearchAddressForSignFromPE(ULONG_PTR uStartBase,ULONG_PTR uSearchLength,SIGNATURE_INFO SignatureInfo[5])
{
    UCHAR *p;
    ULONG_PTR u_index1,u_index2;

    //ULONG uIndex;
    PIMAGE_DOS_HEADER pimage_dos_header;
    PIMAGE_NT_HEADERS pimage_nt_header;
    PIMAGE_SECTION_HEADER pimage_section_header;

    if(!MmIsAddressValid((PVOID)uStartBase))
    {    return 0;    }

    pimage_dos_header = (PIMAGE_DOS_HEADER)uStartBase;
    pimage_nt_header = (PIMAGE_NT_HEADERS)((ULONG)uStartBase+pimage_dos_header->e_lfanew);
    pimage_section_header = (PIMAGE_SECTION_HEADER)((ULONG)pimage_nt_header+sizeof(IMAGE_NT_HEADERS));

    for (u_index1 = 0;u_index1<pimage_nt_header->FileHeader.NumberOfSections;u_index1++)
    {
        //#define IMAGE_SCN_MEM_EXECUTE                0x20000000  // Section is executable.
        //#define IMAGE_SCN_MEM_READ                   0x40000000  // Section is readable.
        //#define IMAGE_SCN_MEM_WRITE                  0x80000000  // Section is writeable.
        //0x60000000 = IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ
        if (pimage_section_header[u_index1].Characteristics&0x60000000)
        {
            p = (UCHAR*)uStartBase + pimage_section_header[u_index1].VirtualAddress;
    

以上是关于硬件断点 DrxHook的主要内容,如果未能解决你的问题,请参考以下文章

使用GDB进行断点调试

硬件断点陷阱和JTAG

ida中怎样设置硬件断点的快捷键

SIGTRAP 尽管没有设置断点;隐藏硬件断点?

Xcode中使用数据(硬件)断点调试

Windows 逆向OD 调试器工具 ( 分析 OD 硬件断点处的关键代码 | 添加硬件断点 | 关键代码 | MOV 指令 | EAX 寄存器值分析 | 使用命令查看 esi+0cc 地址 )(代码