硬件断点 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来读写线程的上下文,来达到设置硬件断点的目的。

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;
            for (u_index2 = 0;u_index2<pimage_section_header[u_index1].Misc.VirtualSize;u_index2++)
            {
                if (!MmIsAddressValid((p-SignatureInfo[0].Offset))||
                    !MmIsAddressValid((p-SignatureInfo[4].Offset)))
                {
                    p++;
                    continue;
                }
                __try{
                    if (*(p-SignatureInfo[0].Offset)==SignatureInfo[0].cSingature&&
                        *(p-SignatureInfo[1].Offset)==SignatureInfo[1].cSingature&&
                        *(p-SignatureInfo[2].Offset)==SignatureInfo[2].cSingature&&
                        *(p-SignatureInfo[3].Offset)==SignatureInfo[3].cSingature&&
                        *(p-SignatureInfo[4].Offset)==SignatureInfo[4].cSingature)
                    {
                        return (ULONG_PTR)p;
                    }

                }__except(EXCEPTION_EXECUTE_HANDLER){
                    DbgPrint("Search error!");
                }
                p++;
            }
        }
    }

    return 0;
}

BOOLEAN    Jmp_HookFunction(
    IN ULONG Destination,
    IN ULONG Source,
    IN UCHAR *Ori_Code
    )
{
    ULONG    jmp_offset;
    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_offset = Source - (Destination+5);

    *(ULONG*)&jmp_code[1] = jmp_offset;   //放入偏移

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

    WPOFF();
    RtlCopyMemory((PVOID)Destination,jmp_code,5);
    WPON();

    KeReleaseSpinLock (&lock,irql);

    return TRUE;
}

VOID WPOFF()
{
    ULONG_PTR cr0 = 0;
    Irql = KeRaiseIrqlToDpcLevel();
    cr0 =__readcr0();
    cr0 &= 0xfffffffffffeffff;
    __writecr0(cr0);

}

VOID WPON()
{

    ULONG_PTR cr0=__readcr0();
    cr0 |= 0x10000;
    __writecr0(cr0);
    KeLowerIrql(Irql);
}

//简单的通过链表获得内核模块的基本信息
PLDR_DATA_TABLE_ENTRY SearchDriver(PDRIVER_OBJECT pDriverObject,wchar_t *strDriverName)
{
    LDR_DATA_TABLE_ENTRY    *pdata_table_entry,*ptemp_data_table_entry;
    PLIST_ENTRY                plist;
    UNICODE_STRING            str_module_name;

    RtlInitUnicodeString(&str_module_name,strDriverName);
    pdata_table_entry = (LDR_DATA_TABLE_ENTRY*)pDriverObject->DriverSection;
    if (!pdata_table_entry)
    {
        return 0;
    }
    plist = pdata_table_entry->InLoadOrderLinks.Flink;

    while(plist!= &pdata_table_entry->InLoadOrderLinks)
    {
        ptemp_data_table_entry = (LDR_DATA_TABLE_ENTRY *)plist;

        //DbgPrint("%wZ",&pTempDataTableEntry->BaseDllName);
        if (0==RtlCompareUnicodeString(&ptemp_data_table_entry->BaseDllName,&str_module_name,FALSE))
        {
            return ptemp_data_table_entry;
        }

        plist = plist->Flink;
    }

    return 0;
}

VOID UnloadDriver(PDRIVER_OBJECT DriverObject)
{
    if (g_bHookSuccess)
    {
        ResumeHookFunction(g_RtlDispatchExeceptionAddress,g_cDisExceptionCode,0x5);
    }

}

VOID ResumeHookFunction(
    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);

    WPOFF();
    RtlCopyMemory((PVOID)Destination,Ori_Code,Length);
    WPON();

    KeReleaseSpinLock (&lock,irql);
}
时间: 2024-10-14 21:09:44

硬件断点 DrxHook的相关文章

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

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 在Xcode的GUI界面中只能添加软断点,而无法增加硬断点.但是在dbg窗口中我们可以使用llvm的watchpoint指令添加硬件断点,也就是所谓的数据断点. 我们可以给类的实例变量设置硬件断点,可以选择监视类型是read,write或者是read_write. 下面举一个例子:比如我们的target对象中包括一个PlayerData对象,其中一个成员变量为

如何对抗硬件断点--- 调试寄存器

1.前言 在我跨入ollydbg的门的时候,就对ollydbg里面的各种断点充满了疑问,以前我总是不明白普通断点,内存断点,硬件断点有什么区别,他们为什么 有些时候不能混用,他们的原理是什么,在学习了前辈们的文章以后,终于明白了一些东西.希望这篇文章能让你对硬件断点的原理和使用有一些帮助 2.正文-------------------------------------------------- i.硬件断点的原理 在寄存器中,有这么一些寄存器,它们用于调试.人们把他们称为调试寄存器,调试寄存器

ring3硬件断点

4个断点寄存器DR0~DR3用来设置断点的线性地址. DR6为状态寄存器,DR7为控制寄存器. DR4和DR5保留.当CR4.DE==1时,访问DR4和DR5产生#UD异常:IF CR4.DE==0,访问DR4和DR5将是对DR6和DR7的访问. 下面这张表非常清楚: |---------------|----------------| Dr0|                 用于一般断点的线性地址 |---------------|----------------| Dr1|        

windbg-bp、 bm、 bu、 bl、 bc、 ba(断点、硬件断点)

bp bp 命令是在某个地址下断点, 可以 bp 0x7783FEB 也可以 bp MyApp!SomeFunction . 对于后者,WinDBG 会自动找到MyApp!SomeFunction 对应的地址并设置断点. 但是使用bp的问题在于: 1)当代码修改之后,函数地址改变,该断点仍然保持在相同位置,不一定继续有效: 2)WinDBG 不会把bp断点保存工作空间中 bp  Address或bp 伪寄存器或bp符号名称: 0:000> x Simple1Demo!CSimple1DemoAp

硬件断点、陷阱和JTAG

R/W 0 和LEN 0对应断点0线性地址: DR则是调试寄存器 硬件断点: 基于CPU的调试寄存器 可以对代码.数据访问和IO访问设置断点 断点被触发时,CPU产生的是1号异常 受调试寄存器的数量限制 WinDbg 的ba命令设置的便是硬件断点 在多处理器系统中,硬件断点是与CPU相关的,也就是说针对一个CPU设置的硬件断点并适用于其他CPU X86 经典异常 比如说INT 3 (CC)断点最终会调用 3Breakpoint 单步执行则是 1号表Debug Exception 代码除0则触发

硬件断点的原理与实现

硬件断点的原理 Intel 80306以上的CPU给我们提供了调试寄存器用于软件调试,硬件断点是通过设置调试寄存器实现的. 上图为Intel手册提供的32位操作系统下8个调试寄存器的图示(Intel手册卷3 17章第二节 Debug Registers,有兴趣的朋友可以查阅),根据介绍,DR0-DR3为设置断点的地址,DR4和DR5为保留,DR6为调试异常产生后显示的一些信息,DR7保存了断点是否启用.断点类型和长度等信息. 我们在使用硬件断点的时候,就是要设置调试寄存器,将断点的位置设置到DR

Windows硬件断点-实现单步异常

触犯单步异常 改变的是当前Eflags 而不是触发异常的Eflags 也就是 PUSHF MOV EAX, DWORD PTR[ESP]       OR EAX, 0x100       MOV DWORD PTR [ESP], EAX   POPF 来实现单步异常(特别需要注意单步异常设置后下一条语句也会触发单步异常.如果不做处理.会造成死机 甚至蓝大妈)

X86逆向教程10:学会使用硬件断点

111 ------------------------------------------------------------本章难度:★★★★☆☆☆☆☆☆课程课件:CM_10.zip------------------------------------------------------------ 111 原文地址:https://www.cnblogs.com/LyShark/p/11181812.html

认识OD的两种断点

OllyDBG从原理上来区分,有两种不同的断点:软件断点和硬件断点. 也许会有朋友说那不是还有内存断点吗? 内存断点严格来说是属于一种特殊的软件断点. 内存断点: 内存断点每次只能设置一个,假如你设置了另一个内存断点,则上一个会被自动删除. 设置一个内存断点,会改变整块(4KB)内存的属性,哪怕你只设置一个字节的内存断点. 另外还需要提一下的是,内存断点会明显降低OD的性能,因为OD经常会校对内存. 软件断点: 当我们按下F2设置的断点就是软件断点. 设置该断点的原理是在断点处重写代码,插入一个