中断,异常,硬件断点

 

Mov  ecx,100

Call  0x1000

Mov  eax,ecx

 

0x1000--->改变了ecx的值

 

 

0x1000:   mov   eax,0

0x1006:   Int    3                  ------->进入内核,找到陷阱处理器KiTrap03

0x1007:  Mov   eax,1

 

一,中断和异常

概述:   

中断和异常是导致处理器转向正常控制流之外代码的两种操作系统条件。

中断是一个异步事件(可以在任何时候发生),并且与处理器当前正在执行的任务毫无关系。中断主要由I/O设备、处理器时钟,或者定时器产生的。

异常是一个同步事件,它是一个特殊指令执行的结果,与当前处理器正在执行的任务有关。

二,IDT(中断描述符表)

系统为每一种中断或者异常提供一个陷阱处理器,当中断或者异常发生时,当前指令的执行就会转到特定的陷阱处理器中,此陷阱处理器完成中断或异常的处理工作。由于中断或异常的类型很多,系统为了方便管理,把各种陷阱处理器放入到一张表中(数组),然后中断和异常发生时,cpu就根据中断或者异常的类型,转入到表中某项去执行中断或异常的处理工作。此表,我们称之为IDT(中断描述符表)。由于中断或异常发生时,转到IDT表项的工作是CPU负责完成的,所以系统中每个CPU都对应一张IDT表,而且每个CPU的IDT表内容都相同。

 

IDT的定义如下:

typedef struct _IDTR

{

 USHORT    limit;      //范围

 ULONG      base;       //IDT表项起始地址

}IDTR,*PIDTR;

 

typedef struct _IDTENTRY

{

       unsignedshort LowOffset;

       unsignedshort selector;

       unsignedchar retention:5;

       unsignedchar zero1:3;

       unsignedchar gate_type:1;

       unsignedchar zero2:1;

       unsignedchar interrupt_gate_size:1;

       unsignedchar zero3:1;

       unsignedchar zero4:1;

       unsignedchar DPL:2;

       unsignedchar P:1;

       unsignedshort HiOffset;

} IDTENTRY,*PIDTENTRY;

 

执行陷阱处理器之前,要把该异常发生时的现场保存下来,方便我们后续指令继续执行。

 

 

陷阱帧定义:

typedef struct_X86_KTRAP_FRAME {

    ULONG  DbgEbp;

    ULONG  DbgEip;

    ULONG  DbgArgMark;

    ULONG  DbgArgPointer;

    ULONG   TempSegCs;

    ULONG  TempEsp;

    ULONG  Dr0;

    ULONG  Dr1;

    ULONG  Dr2;

    ULONG  Dr3;

    ULONG  Dr6;

    ULONG  Dr7;

    ULONG  SegGs;

    ULONG  SegEs;

    ULONG  SegDs;

    ULONG  Edx;

    ULONG  Ecx;

    ULONG  Eax;

    ULONG  PreviousPreviousMode;

    ULONG  ExceptionList;

    ULONG  SegFs;

    ULONG  Edi;

    ULONG  Esi;

    ULONG  Ebx;

    ULONG  Ebp;

    ULONG  ErrCode;

    ULONG  Eip;

    ULONG  SegCs;

    ULONG  EFlags;

    ULONG  HardwareEsp;    // WARNING - segSS:espare only here for stacks

    ULONG  HardwareSegSs;  // that involve aring transition.

    ULONG  V86Es;          // these will bepresent for all transitions from

    ULONG  V86Ds;          // V86 mode

    ULONG  V86Fs;

    ULONG  V86Gs;

} X86_KTRAP_FRAME,*PX86_KTRAP_FRAME;

 

 

获取所有kpcr代码:

 

typedef KAFFINITY

(*KESETAFFINITYTHREAD)(

   __inout PKTHREAD Thread,

   __in KAFFINITY Affinity

   );

 

 

#define   MAX_PROCESSORCOUNT     0x40

ULONG       gkv_KiProcessorBlock[MAX_PROCESSORCOUNT];

 

BOOLEAN PsGetKiProcessorBlock()

{

       ULONG Index,Affinity,CurrentAffinity;

       ULONG fnpKeSetAffinityThread;

 

       ULONG kPrcb;

 

       UNICODE_STRING usFuncName;

 

       RtlInitUnicodeString(&usFuncName,L"KeSetAffinityThread");

       fnpKeSetAffinityThread =(ULONG)MmGetSystemRoutineAddress(&usFuncNam);

 

       if (fnpKeSetAffinityThread==0)

       {

                return FALSE;

       }

 

       Affinity = KeQueryActiveProcessors();                    //KeQueryActiveProcessors获取处理器相关的位图(这里的位图可以理解为个数,比如返回1代表一个处理器,返回3表示两个处理器,返回7表示三个处理器,依此类推。也就是说从有多少个处理器,那么Affinity的值就会从低位到高位依此填充多少位)

 

       CurrentAffinity = 1;

       Index = 0;

       while(Affinity)

       {

                //下面只是个简单的算法,使当前线程运行到不同的处理器上

                Affinity &= ~CurrentAffinity;

               ((KESETAFFINITYTHREAD)fnpKeSetAffinityThread)(PsGetCurrentThread(),(KAFFINITY)CurrentAffinity);

                CurrentAffinity <<= 1;

               

                __asm{

                        push        eax

                       mov                eax,fs:[0x20]

                        mov                kPrcb,eax

                        pop                eax

                }

                //得到我们要的东西

                gkv_KiProcessorBlock[Index] =kPrcb;

               Index++;

       }

 

       return TRUE;

}





硬件断点是cpu直接支持的,通过对DR0~DR7寄存器的相关设置,就可以达到监控某地址的执行或者读写情况。

 

内核层中的硬件断点,是可以直接设置DR0~DR7寄存器。

用户层中的硬件断点,是不可以直接设置DR0~DR7寄存器。

 

Windows为我们提供了两个API,可以去帮我我们设置硬件断点。

BOOL GetThreadContext(

 HANDLE hThread,

 LPCONTEXT lpContext

);

BOOL SetThreadContext(

 HANDLE hThread,

 CONST CONTEXT* lpContext

);

以上两个api可以获取和设置一个线程的上下文。

 

以前说过,设置硬件断点是通过改写DR0---DR7寄存器进行的。

 

设置和获取线程上下文最终是通过KeContextToKframes和KeContextFromKframes实现的。

 

KeContextFromKframes内部是通过内核栈上面保存的一个_KTRAP_FRAME结构来获取相关的线程上下文内容。

其中获取调试寄存器的内容后,通过KiUpdateDr7此函数更新一些东西。

线程对象的0x3偏移位置是DebugActive成员,这个成员,是记录哪些调试寄存器是被设置了。

 

KeContextToKframes内部是通过设置内核栈上面保存的一个_KTRAP_FRAME结构来更新线程上下文的。

其中并没有对我们的调试寄存器进行直接的设置工作。这里虽然没有直接的相关设置工作,那么它肯定是在某个地方进行了设置,不然的话,用户层是没办法实现硬件断点的。

首先用户层调用api的原理是:

xxx----进入内核----KiFastCallEntry-----xxx对应的内核函数-----KiFastCallEntry-----退出内核(KiServiceExit)-----返回xxx

 

设置硬件断点的工作如下:

在NtSetContextThread内部更新了目标线程内核栈上的陷阱帧。然后当目标线程切换进内核并退出时,目标线程会把它内核栈上的陷阱帧更新到对应的DR0---DR7调试寄存器中,这样就达到了设置硬件断点的目的。

 

Windows为了区分用户模式硬件断点和内核模式硬件的,它的设计思路如下:

当是内核模式硬件断点的时候,可以直接设置调试寄存器。

当是用户模式硬件断点的时候,因为具体的设置工作是要在内核中进行的,而如果直接在KeContextToKframes中进行设置调试寄存器的工作的话,那是就等于是设置内核模式的调试寄存器了。所以windows选择在目标线程退出内核的那一刻时,进行设置,那么退出内核后,硬件断点就生效了。

 

总结:

1,反硬件断点,我们可以通过对某线程的DebugActive成员进行判断,如果存在值的话,那么就是存在硬件断点。

2,可以通过对目标线程内核栈上的陷阱帧进行获取和设置工作,也可以达到反硬件断点与检测的目的。

 

NtGetContextThread(HANDLEThreadHandle, PCONTEXT Context)

NtSetContextThread(HANDLEThreadHandle, PCONTEXT Context)

 

PsGetContextThread(PETHREADThread, PCONTEXT Context,KPROCESSORMODE AccessMode)

PsSetContextThread(PETHREADThread, PCONTEXT Context,KPROCESSORMODE AccessMode)


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值