如何anti_anti_hardware_breakpoint

    之前我寫過一篇利用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 // [email protected]
*****************************************************************************
*/

#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
}

/*
pExecuteHandler += ( (DWORD *)(pExecuteHandler+1) )[0] + 5;
printf(”ExecuteHandler2 : %08Xnn”, pExecuteHandler);
for(i=0; ; i++) {
if( ((BYTE *)(pExecuteHandler))[i] == 0xFF && ((BYTE *)(pExecuteHandler))[i+1] == 0xD1)
break;
}
printf(”0x%08X : call ecx”, pExecuteHandler+i);
return 1;
}
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, DispatcherContext);
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;
}
 

 

3.4 Is it the same as in the Anti-Anti-Hardware plugin in ollydbg?(土星按:作者給OllyDbg寫過這個插件)

    Yes, except the NewExecute2 Handler is:

    是的,除了NewExecute2 函數如下: 

int declspec(dllexport) stdcall NewExecute2(DWORD ExceptionRecord, DWORD EstablisherFrame, LPCONTEXT Contex-
tRecord, DWORD DispatcherContext,SEHandler Handler) 
{
//DWORD FckHandler;
// asm mov FckHandler, edx
DRx.Dr0 = ContextRecord->Dr0;
DRx.Dr1 
= ContextRecord->Dr1;
DRx.Dr2 
= ContextRecord->Dr2;
DRx.Dr3 
= ContextRecord->Dr3;
DRx.Dr6 
= ContextRecord->Dr6;
DRx.Dr7 
= ContextRecord->Dr7;
if(if changed) {
ContextRecord
->Dr0 = DRx.Dr0;
ContextRecord
->Dr1 = DRx.Dr1;
ContextRecord
->Dr2 = DRx.Dr2;
ContextRecord
->Dr3 = DRx.Dr3;
ContextRecord
->Dr6 = DRx.Dr6;
ContextRecord
->Dr7 = DRx.Dr7;
}

//printf(”Eip: 0x%08X // SEH Handler : 0x%08X”,ContextRecord->Eip, Handler);
/*
asm {
push dword ptr ss:[ebp+0xC]
push FckHandler
push dword ptr fs:[0]
mov dword ptr fs:[0],esp
}
*/

Handler(ExceptionRecord, EstablisherFrame, ContextRecord, DispatcherContext);
/*
asm {
mov esp,dword ptr fs:[0]
pop dword ptr fs:[0]
}
if((ContextRecord->Dr7 == 0x155) && (if changed == 0)) {
DRx.Dr0 = ContextRecord->Dr0;
DRx.Dr1 = ContextRecord->Dr1;
DRx.Dr2 = ContextRecord->Dr2;
DRx.Dr3 = ContextRecord->Dr3;
DRx.Dr6 = ContextRecord->Dr6;
DRx.Dr7 = ContextRecord->Dr7;
ContextRecord->Dr0 = DRx.Dr0;
ContextRecord->Dr1 = DRx.Dr1;
ContextRecord->Dr2 = DRx.Dr2;
ContextRecord->Dr3 = DRx.Dr3;
ContextRecord->Dr6 = DRx.Dr6;
ContextRecord->Dr7 = DRx.Dr7;
if changed = 1;
} else if (if changed) {
ContextRecord->Dr0 = DRx.Dr0;
ContextRecord->Dr1 = DRx.Dr1;
ContextRecord->Dr2 = DRx.Dr2;
ContextRecord->Dr3 = DRx.Dr3;
ContextRecord->Dr6 = DRx.Dr6;
ContextRecord->Dr7 = DRx.Dr7;
} /* else {
if(ContextRecord->Dr0 != DRx.Dr0)
//olly add to list(0, ERROR ,”==> The Dr0 have been changed!”);
if (ContextRecord->Dr1 != DRx.Dr1)
//olly add to list(0, ERROR ,”==> The Dr1 have been changed!”);
if (ContextRecord->Dr2 != DRx.Dr2)
//olly add to list(0, ERROR ,”==> The Dr2 have been changed!”);
if (ContextRecord->Dr3 != DRx.Dr3)
//olly add to list(0, ERROR ,”==> The Dr3 have been changed!”);
if (ContextRecord->Dr6 != DRx.Dr6)
//olly add to list(0, ERROR ,”==> The Dr6 have been changed!”);
if (ContextRecord->Dr7 != DRx.Dr7)
//olly add to list(0, ERROR ,”==> The Dr7 have been changed!”);
*/

return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章