现在的位置: 首页 > 综合 > 正文

如何anti_anti_hardware_breakpoint

2013年12月05日 ⁄ 综合 ⁄ 共 11492字 ⁄ 字号 评论关闭
文章目录

    之前我写过一篇利用SEH异常清硬件断点,看来真的是有矛就有盾啊,发现了一篇英文文章,专门讲如何anti_anti_hardware_breakpoint(很拗口吧?翻译成中文就是如何对付那些防止我们下硬件断点的程序).

Chapter 1

Hardware breakpoint

    Hardware breakpoints or BPM(Breakpoint on Memory) use the Debug Registers. The way of concretisation it depending of the processor. In the architectural x86 (+386) 4 debug registers(DR0-DR3) exists in the processor. So we can only set 4 BPM. The DR4 and DR5 are reserved registers, the DR6 register is for the debugger and the DR7 register still using for the type of break for the DR0-DR3 registers.

    硬件断点或者BPM(内存断点)利用了调试寄存器.具体的实现以来于处理器.在x86架构下面(+386)处理器有4个调试寄存器(DR0-DR3).所以我们可以下4个内存断点.DR4和DR5属于保留寄存器,DR6是为调试器准备的,DR7是用来描述DR0-DR3寄存器的中断类型的.

1.1 Debug Address Registers (DRO-DR3)

    Each of these registers contains the linear address associated with one of four breakpoint conditions. Each breakpoint condition is further defined by bits in DR7.

    这些调试寄存器保存了一个线型地址,并且可以根据在DR7里面指定的条件产生中断(总共有四种条件,只能对某个寄存器指定一种条件).

    The debug address registers are effective whether or not paging is enabled.The addresses in these registers are linear addresses. If paging is enabled,the linear addresses are translated into physical addresses by the processor’s paging mechanism. If paging is not enabled, these linear addresses are the
same as physical addresses.

    调试地址寄存器不论分页机制是否启用都有效.这些寄存器里面的地址是线型的.如果分页机制启用了,线型地址会根据处理器的分页机制翻译成物理地址,否则这个线型地址就是物理地址.

    Note that when paging is enabled, different tasks may have different linear-to-physical address mappings. When this is the case, an address in a debug address register may be relevant to one task but not to another. For this reason the 80386 has both global and local enable bits in DR7. These bits indicate whether a given debug address has a global (all tasks) or local (current task only) relevance.

    值得注意分页机制启用的时候,不同的任务可能会有线型地址到物理地址的不同映射.在这种情况下,调试寄存器可能只对某个任务有效而不是所有任务.所以80386的DR7里面有个标志位表示调试寄存器是全局的还是局部的,就是用于说明某个指定的调试寄存器是全局相关(对所有任务有效)的还是局部相关(对单个任务有效)的.

1.2 Debug Control Register (DR7)

    The debug control register shown in Figure 12-1 both helps to define the debug conditions and selectively enables and disables those conditions.

    调试控制寄存器(土星按:即DR7)用于定义调试条件并且可以选择关闭某个寄存器(土星按:原文提到的12-1图我没有看到,下面的图是在文章中别的地方找到的).

Dr0-Dr7

图1

    For each address in registers DR0-DR3, the corresponding fields R/W0 through R/W3 specify the type of action that should cause a breakpoint. The processor interprets these bits as follows:

  • 00 – Break on instruction execution only
  • 01 – Break on data writes only
  • 10 – undefined
  • 11 – Break on data reads or writes but not instruction fetches

    对应于DR0-DR3,在DR7里面有对应的R/W0-R/W3来指定在什么情况下发生中断,处理器如下解释这些数据:

  • 00 – 只在执行时中断
  • 01 – 写时中断
  • 10 – undefined
  • 11 – 读或者写(但不是获取指令)时中断

    Fields LEN0 through LEN3 specify the length of data item to be monitored.A length of 1, 2, or 4 bytes may be specified. The values of the length fields are interpreted as follows:

  • 00 – one-byte length
  • 01 – two-byte length
  • 10 – undefined
  • 11 – four-byte length

    LEN0-LEN3指定要监视的数据长度.可以是1,2,4.处理器如下解释这些数据:

  • 00 – 1个字节长
  • 01 – 2个字节长
  • 10 – undefined
  • 11 – 4个字节长

    If RWn is 00 (instruction execution), then LENn should also be 00. Any other length is undefined.

    如果RWn是00(执行是中断),那么对应的LENn也必须为00.其它长度值都是未定义的.

    The low-order eight bits of DR7 (L0 through L3 and G0 through G3) selectively enable the four address breakpoint conditions. There are two levels of enabling: the local (L0 through L3) and global (G0 through G3) levels.The local enable bits are automatically reset by the processor at every task switch to avoid unwanted breakpoint conditions in the new task. The global enable bits are not reset by a task switch; therefore, they can be used for conditions that are global to all tasks.

    DR7的最低8个位可(L0-L3以及G0-G3)可以用于控制四个调试寄存器的开关,这有两种级别:局部(L0-L3)的和全局的(G0-G3).局部的开关在处理器切换任务时会自动重置,这样可以防止在一个新任务中不会产生不想要的结果.而全局开关在切换时不会重置.所以它们用于对全局都有用的条件.

    The LE and GE bits control the ”exact data breakpoint match” feature of the processor. If either LE or GE is set, the processor slows execution so that data breakpoints are reported on the instruction that causes them. It is recommended that one of these bits be set whenever data breakpoints are armed. The processor clears LE at a task switch but does not clear GE.

    LE和GE位用于控制CPU的"精确的数据断点匹配".当其中一个被设置的时候,处理器会放慢执行速度,这样当命令执行的时候可以通知这些数据断点.建议在设置数据断点是需要设置其中一个.切换任务时LE会被清除而GE不会被清除.

Chapter 2

Anti Hardware Breakpoint

2.1 How the system manages the SEH?

Seh_Organnisation

2.2 Explanation

    The DR0,DR1,DR2,DR3,DR6 registers are set to 0 and the DR7 to 155h.But why to 155h?Note: 155h = 0101010101b

    现在DR0,DR1,DR2,DR3,DR6寄存器设置为0而DR7设置为155h.但是为什么是155h呢?注:155h = 0101010101b

DR7

GE LE G3 L3 G2 L2 G1 L1 G0 L0
0 1 0 1 0 1 0 1 0 1
  • L0 = 1 ; Active local DR0 (only for this process). We need to set this value else windows won’t use the new DR0.
  • G0 = 0 ; Disactive global DR0 (for all tasks)
  • L1 = 1 ; Active local DR1 (only for this process). We need to set thisvalue else windows won’t use the new DR1.
  • G1 = 0 ; Disactive global DR1 (for all tasks)
  • L2 = 1 ; Active local DR2 (only for this process). Same.
  • G2 = 0 ; Disactive global DR2 (for all tasks)
  • L3 = 1 ; Active local DR3 (only for this process). Same.
  • G3 = 0 ; Disactive global DR3 (for all tasks)
  • LE = 1 ; Actived for a compatibility problem
  • L0 = 1 ; 在本进程激活DR0,我们需要设置这个值否则Windows不会使用DR0.
  • G0 = 0 ; 在全局屏蔽DR0
  • L1 = 1 ;在本进程激活DR1,我们需要设置这个值否则Windows不会使用DR1
  • G1 = 0 ; 在全局屏蔽DR1
  • L2 = 1 ; 在本进程激活DR2,我们需要设置这个值否则Windows不会使用DR2.
  • G2 = 0 ; 在全局屏蔽DR2
  • L3 = 1 ; 在本进程激活DR3,我们需要设置这个值否则Windows不会使用DR3
  • G3 = 0 ; 在全局屏蔽DR3
  • LE = 1 ; 激活解决兼容性问题

2.3 Implemention

    Using SEH to erase the current BPM is a very common technique, and is very easy to recognise.

    A sample of Anti-Hardware Breakpoint:

    使用SEH清楚内存断点是非常普遍的技术,并且理解起来也很简单.

    下面是个例子:

 

push offset seh handler                                      ; New Handler
xor eax, eax
push dword ptr fs:[eax]                                         ; Old Handler
mov dword ptr fs:[eax],esp                                   ; The TIB pointe on our structure
xor dword ptr [eax],eax                                          ; Exception
pop dword ptr fs:[eax]                                           ; Restore the TIB with the previous Handler
lea esp, dword ptr [esp
+4]                                   ; Ajust ESP
call ExitProcess
retn


seh handler proc ExceptionRecord:DWORD, EstablisherFrame:DWORD,
ContextRecord:DWORD, DispatcherContext:DWORD
mov eax, ContextRecord
assume eax:ptr CONTEXT
mov dword ptr [eax].iDr0, 
0                                    ; DR0 = 0
mov dword ptr [eax].iDr1, 
0                                    ; DR1 = 0
mov dword ptr [eax].iDr2, 
0                                    ; DR2 = 0
mov dword ptr [eax].iDr3, 
0                                     ; DR3 = 0
mov dword ptr [eax].iDr6, 
0                                     ; DR6 = 0
mov dword ptr [eax].iDr7, 155h                              ; DR7 
= 155h
add dword ptr [eax].regEip, 
2                                  ; EIP += 2
ret
seh handler endp

CONTEXT STRUCT
ContextFlags DWORD 
?
iDr0 DWORD 
?
iDr1 DWORD 
?
iDr2 DWORD 
?
iDr3 DWORD 
?
iDr6 DWORD 
?
iDr7 DWORD 
?
FloatSave FLOATING SAVE AREA ¡¿
regGs DWORD 
?
regFs DWORD 
?
regEs DWORD 
?
regDs DWORD 
?
regEdi DWORD 
?
regEsi DWORD 
?
regEbx DWORD 
?
regEdx DWORD 
?
regEcx DWORD 
?
regEax DWORD 
?
regEbp DWORD 
?
regEip DWORD 
?
regCs DWORD 
?
regFlag DWORD 
?
regEsp DWORD 
?
regSs DWORD 
?
ExtendedRegisters db MAX dup(
?)
CONTEXT ENDS

 

Chapter 3

Anti AntiHardware Breakpoint

3.1 How to hook a SEH?

    In the previous chapter we saw that the system passes the EXCEPTION to ntdll.KiUserExceptionDispatcher in Ring3. Therefore, all we need is to hook the function that calls our HANDLER.

    前面的几章我们看到了,系统把异常传递给了Ring3的ntdll.KiUserExceptionDispatcher .因此我们需要钩住这个函数来调用我们自己的处理函数.

    Those functions differs in every OS.
    In the next pages we will see two flow chart of hooked KiUserExceptionDispatcher.
    In Win2K I replace the jmp ExecuteHandler to jmp MyExecuteHandler.
    In WinXP I replace the call ExecuteHandler2 to call MyExecuteHandler2.

    这些函数在每个系统中是不一样的.
    在下一页我们会看到两个被钩住的KiUserExceptionDispatcher的流程图.
    在Win2k我替换成MyExecuteHandler.
    在WinXP我替换成MyExecuteHandler2.

2kExceptionDetoured

3.2 MyExecuteHandler

    This piece of code is a sample of a modified NewExecute2 function.

    下面是代码片段:

int stdcall NewExecute2(DWORD ExceptionRecord, DWORD EstablisherFrame, LPCONTEXT ContextRecord, DWORD DispatcherContext,SEHandler Handler) {
DRx.Dr0 
= ContextRecord-¿Dr0;
DRx.Dr1 
= ContextRecord-¿Dr1;
DRx.Dr2 
= ContextRecord-¿Dr2;
DRx.Dr3 
= ContextRecord-¿Dr3;
DRx.Dr6 
= ContextRecord-¿Dr6;
DRx.Dr7 
= ContextRecord-¿Dr7;
printf(”Eip: 0x
%08X // SEH Handler : 0x%08X ”,ContextRecord-
>Eip, Handler);
Handler(ExceptionRecord, EstablisherFrame, ContextRecord, Dis
-
patcherContext);
ContextRecord
->Dr0 = DRx.Dr0;
ContextRecord
->Dr1 = DRx.Dr1;
ContextRecord
->Dr2 = DRx.Dr2;
ContextRecord
->Dr3 = DRx.Dr3;
ContextRecord
->Dr6 = DRx.Dr6;
ContextRecord
->Dr7 = DRx.Dr7;
return 1;
}

    NewExecute2是很基本的一个函数. 因此改写很简单. 参数和异常处理函数一样:

    typedef int ( cdecl *SEHandler)(DWORD ExceptionRecord, DWORD EstablisherFrame, LPCONTEXT ContextRecord, DWORD DispatcherContext);

3.3 Standalone Concept

 

/*******************************************************************************
Mattwood^FRET 2oo5 // Magic Source haha //
Standalone Concept // mattwood9@gmail.com
*****************************************************************************
*/

#include 
<windows.h>
#include 
<stdio.h>
#include 
<stdlib.h>
typedef 
struct DebugRegister {
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
}
 DebugRegister;
DebugRegister DRx;
typedef 
int ( cdecl *SEHandler)(DWORD ExceptionRecord, DWORD EstablisherFrame, LPCONTEXT ContextRecord, DWORD DispatcherContext);
int stdcall NewExecute2(DWORD ExceptionRecord, DWORD EstablisherFrame, LPCONTEXT ContextRecord, DWORD DispatcherContext,SEHandler Handler);
int main(int argc, char **argv) {
unsigned 
int i, j;
DWORD pKiUserExceptionDispatcher, pRtlDispatchException,pRtlpExecuteHandlerForException, pExecuteHandler;
DWORD pNewExecute2;
DWORD lpWrByte;
pKiUserExceptionDispatcher 
= (DWORD )GetProcAddress(LoadLibrary(”ntdll.dll”), ”KiUserExceptionDispatcher”);
printf(”KiUserExceptionDispatcher : 
%08X ”, pKiUserExceptionDispatcher);
for(i=0; ((BYTE *)(pKiUserExceptionDispatcher))[i] != 0xE8;i++){continue;}//土星按:找到第一个CALL指令,即RtlDispatchException
printf(”call RtlDispatchException : %08X ”, pKiUserExceptionDispatcher+i);
pRtlDispatchException 
= pKiUserExceptionDispatcher+i;
pRtlDispatchException 
+= ( (DWORD *)(pRtlDispatchException+1) )[0+ 5;//土星按:计算得到RtlDispatchException物理地址
printf(”RtlDispatchException: %08Xn ”, pRtlDispatchException);
j
=0;
i
=-1;
//土星按:同理搜索获得RtlpExecuteHandlerForException的地址
while(!j) {
i
++;
for(; ((BYTE *)(pRtlDispatchException))[i] != 0xE8; i++)
continue;
if(((BYTE *)(pRtlDispatchException))[i+5== 0xF6 && ((BYTE*)(pRtlDispatchException))[i+6== 0x05)
j
++;
}

printf(”call RtlpExecuteHandlerForException : 
%08X ”, pRtlDispatchException+i);
pRtlpExecuteHandlerForException 
= pRtlDispatchException+i;
pRtlpExecuteHandlerForException 
+= ( (DWORD *)(pRtlpExecuteHandlerForException+1) )[0+ 5;
printf(”RtlpExecuteHandlerForException: 
%08X ”, pRtlpExecuteHandlerForException);
//土星按:根据Windows版本来下钩子
if((WORD)GetVersion() == 0x0005{
// — Win 2K Begin —
printf(”Windows 5.0 ”);
pExecuteHandler 
= pRtlpExecuteHandlerForException+5;
pNewExecute2 
= (DWORD)&NewExecute2;
pNewExecute2 
-= pExecuteHandler+5;
WriteProcessMemory((HANDLE)
-1,(LPVOID)(pExecuteHandler),”é”,1,&lpWrByte);
WriteProcessMemory((HANDLE)
-1,(LPVOID)(pExecuteHandler+1),&pNewExecute2,4,&lpWrByte);
// — Win 2k END —
}

else if((WORD)GetVersion() == 0x0105{
// — Win XP BEGIN —
printf(”Windows 5.1 ”);
pExecuteHandler 
= (pRtlpExecuteHandlerForException) + (((BYTE*)(pRtlpExecuteHandlerForException))[6]) + 7;
printf(”ExecuteHandler : 
%08X ”, pExecuteHandler);
for(i=0; ((BYTE *)(pExecuteHandler))[i] != 0xE8; i++)
continue;
pExecuteHandler 
= pExecuteHandler+i;
pNewExecute2 
= (DWORD)&NewExecute2;
pNewExecute2 
-= pExecuteHandler+5;
WriteProcessMemory((HANDLE)
-1,(LPVOID)pExecuteHandler,” xE8”,1,&lpWrByte);
WriteProcessMemory((HANDLE)
-1,(LPVOID)(pExecuteHandler+1),&pNewExecute2,4,&lpWrByte);
// — Win XP END —
}

//产生异常并修改DR来测试
asm {
push offset SEH
push dword ptr fs:[
0]
mov dword ptr fs:[
0], esp
xor eax, eax
mov [eax], eax
mov esp, dword ptr fs:[
0]
pop dword ptr fs:[
0]
add esp, 
4
retn
SEH:
mov ecx,dword ptr ss:[esp
+0xC]
add dword ptr ds:[ecx
+0xB8],2
xor eax,eax
mov dword ptr ds:[ecx
+0x4],eax
mov dword ptr ds:[ecx
+0x8],eax
mov dword ptr ds:[ecx
+0xC],eax
mov dword ptr ds:[ecx
+0x10],eax
mov dword ptr ds:[ecx
+0x14],eax
mov dword ptr ds:[ecx
+0x18],155
retn
}

抱歉!评论已关闭.