反调试之去除硬件断点

首先介绍几个X86下调试寄存器D0…D7,已经没有D4,D5了在wiki上说是被D6,D7取代了。

D0…D3


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.

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 x86 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.


D6


The debug status register permits the debugger to determine which debug conditions have occurred. When the processor detects an enabled debug exception, it sets the low-order bits of this register (0,1,2,3) before entering the debug exception handler.

Note that the bits of DR6 are never cleared by the processor. To avoid any confusion in identifying the next debug exception, the debug handler should move zeros to DR6 immediately before returning.


D7


The low-order eight bits of DR7 (0,2,4,6 and 1,3,5,7) selectively enable the four address breakpoint conditions. There are two levels of enabling: the local (0,2,4,6) and global (1,3,5,7) 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.

Bits 16-17 (DR0), 20-21 (DR1), 24-25 (DR2), 28-29 (DR3), define when breakpoints trigger. Each breakpoint has a two-bit entry that specifies whether they break on execution (00b), data write (01b), data read or write (11b). 10b is defined to mean break on IO read or write but no hardware supports it.[citation needed] Bits 18-19 (DR0), 22-23 (DR1), 26-27 (DR2), 30-31 (DR3), define how large an area of memory is watched by breakpoints. Again each breakpoint has a two-bit entry that specifies whether they watch one (00b), two (01b), eight (10b)[1] or four (11b) bytes.[2]


D7有点难看懂,所以我自己尝试总结一下

;******************************************
;coded by Hume,2K+
;《加密与解密(第四版)》
;(c)  看雪学院 www.kanxue.com 2000-2018
;******************************************
;例子1.演示在SEH回调函数中清空Dr寄存器以达到反调试的目的
;******************************************
include ..\asminc.h
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

	.DATA
Text           db "SEH程序没有运行!",0
TextSEH        db "Hello,SEH!",0
Caption        db "SEH",0
	.DATA?
	
.code
_start:
assume fs:nothing
;------------------------------------------------
        push	offset _except_handler
        push	fs:[0]       
        mov	fs:[0],esp  
;---------------------------------------------------
	    mov	esi,0
	    mov	eax,[esi]		
 WouldBeOmit:                                  
   	    invoke  MessageBox,0,addr Text,addr Caption,MB_OK  ; 这一句永远无法被执行
;---------------------------------------------------
ExecuteHere:                           
   	    invoke   MessageBox,0,addr TextSEH,addr Caption,MB_OK
;--------------------------------------------------
    	    pop     fs:[0]                      
    	    add     esp,4 
    	    invoke  ExitProcess,NULL        
;-------------------------------------------------
_except_handler proc uses ebx pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
        mov  	  eax,pContext 
        Assume  eax:ptr CONTEXT
        lea      ebx, ExecuteHere  	
        mov     [eax].regEip,ebx  	
        xor      ebx,ebx
        mov     [eax].iDr0,ebx    	
        mov     [eax].iDr1,ebx
        mov     [eax].iDr2,ebx
        mov     [eax].iDr3,ebx
        mov     [eax].iDr7,341     
        mov     eax,0            	
        ret                     
_except_handler endp
end _start

ba e1 401000
ba r2 40107e

DR0…DR7 Values
DR0 401000
DR1 40107e
DR2 0
DR3 0
DR6 ffff4ff0
DR7 700105

Dr7 |----------------|----------------|

7 0 0 1 0 5h = 111 0000 0000 0001 0000 0101b

Dr7 (high)|0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0|0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 1|(low)

16-17bits —>DR0—>00—>execution
18-19bits —>DR0—>00—>one bytes

20-21bits —>DR1—>11—>data read or write
22-23bits —>DR1—>01—>two bytes

0:000> bp ntdll!KiUserExceptionDispatcher
0:000> gn
Breakpoint 2 hit
eax=0019ffcc ebx=00364000 ecx=00401000 edx=00401000 esi=00401000 edi=00401000
eip=772242b0 esp=0019fa34 ebp=0019ff80 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ntdll!KiUserExceptionDispatcher:
0:000> dd esp
0:000> dt _CONTEXT 0019fa8c
ntdll!_CONTEXT
   +0x000 ContextFlags     : 0x1007f
   +0x004 Dr0              : 0x401000
   +0x008 Dr1              : 0x40107e
   +0x00c Dr2              : 0
   +0x010 Dr3              : 0
   +0x014 Dr6              : 0xffff4ff0
   +0x018 Dr7              : 0x700105
   +0x01c FloatSave        : _FLOATING_SAVE_AREA
   +0x08c SegGs            : 0x2b
   +0x090 SegFs            : 0x53
   +0x094 SegEs            : 0x2b
   +0x098 SegDs            : 0x2b
   +0x09c Edi              : 0x401000
   +0x0a0 Esi              : 0x401000
   +0x0a4 Ebx              : 0x364000
   +0x0a8 Edx              : 0x401000
   +0x0ac Ecx              : 0x401000
   +0x0b0 Eax              : 0x19ffcc
   +0x0b4 Ebp              : 0x19ff80
   +0x0b8 Eip              : 0x401013
   +0x0bc SegCs            : 0x23
   +0x0c0 EFlags           : 0x246
   +0x0c4 Esp              : 0x19ff6c
   +0x0c8 SegSs            : 0x2b
   +0x0cc ExtendedRegisters : [512]  "???"

从中可见context已经封装了dr0…dr7

0:000> bp 401080

我在handler的返回的地方下了一个断点,看看seh执行完是否修改了调试寄存器

0:000> g
Breakpoint 3 hit
eax=00000000 ebx=00000000 ecx=00401051 edx=77238e90 esi=00000000 edi=00000000
eip=00401080 esp=0019f938 ebp=0019f958 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ClearDr+0x1080:
00401080 c21000          ret     10h
0:000> dt _CONTEXT 0019fa8c
ntdll!_CONTEXT
   +0x000 ContextFlags     : 0x1007f
   +0x004 Dr0              : 0
   +0x008 Dr1              : 0
   +0x00c Dr2              : 0
   +0x010 Dr3              : 0
   +0x014 Dr6              : 0xffff4ff0
   +0x018 Dr7              : 0x155
   +0x01c FloatSave        : _FLOATING_SAVE_AREA
   +0x08c SegGs            : 0x2b
   +0x090 SegFs            : 0x53
   +0x094 SegEs            : 0x2b
   +0x098 SegDs            : 0x2b
   +0x09c Edi              : 0x401000
   +0x0a0 Esi              : 0x401000
   +0x0a4 Ebx              : 0x364000
   +0x0a8 Edx              : 0x401000
   +0x0ac Ecx              : 0x401000
   +0x0b0 Eax              : 0x19ffcc
   +0x0b4 Ebp              : 0x19ff80
   +0x0b8 Eip              : 0x40102d
   +0x0bc SegCs            : 0x23
   +0x0c0 EFlags           : 0x246
   +0x0c4 Esp              : 0x19ff6c
   +0x0c8 SegSs            : 0x2b
   +0x0cc ExtendedRegisters : [512]  "???"

context中封装的调试寄存器和eip被改了,如果恢复执行的话,在上级函数的帮助下这些修改就会成真,所以这操作挺骚的。不过也显而易见有缺陷。


第八章的异常处理勉强看完,有点爽。

mark:VEH HOOK,SEH HOOK,各种HOOK。