Technology sharing: Anti-debugging with exceptions
0 × 01 Introduction
Some articles have introduced how to combat debuggers by detecting exceptions. The idea is simple: depending on the design intent, the debugger will handle specific exceptions. If an exception is wrapped in a try block, the exception handler will be executed only when a debugger is not attached. Therefore, we can conclude that the program is being debugged by a debugger as long as the exception block is not executed.
0 × 02 Interrupt 3 Interrupt (0xCC) of one byte)
Among all the exceptions that will be processed by the debugger, interrupt 3 is interrupted and a single-byte breakpoint is generated.
BOOL IsDebuggerPresent_Int3(){ __try { __asm int 3 } __except(1) { return FALSE; } return TRUE;}
0 × 03 Interrupt 3 Interrupt of two bytes (0xCD 0 × 03)
You can use the _ emit pseudo command of the Visual C ++ inline assembler to generate a two-byte interrupt 3 command. Only the OnllyDbg debugger recognizes this exception in all debuggers tested.
BOOL IsDebuggerPresent_Int3_2Byte(){ __try { __asm { __emit 0xCD __emit 0x03 } } __except(1) { return FALSE; } return TRUE;}
0 × 04 Interrupt 0x2C interrupted
Interrupt 0x2C causes a debugging asserted exception. WinDbg will respond to this exception, but other debuggers will not process it. This command can be generated using _ int 2c. This interrupt is only valid on Vista and later systems. Because of this, you need to dynamically check the windows version. if the version number is smaller than 6, false is returned.
BOOL IsDebuggerPresent_Int2c(){ OSVERSIONINFO osvi; ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osvi); if (osvi.dwMajorVersion 6) return FALSE; __try { __int2c(); } __except(1) { return FALSE; } return TRUE;}
0 × 05 Interrupt 0x2D
If interrupt 0x2D is executed, Windows throws a breakpoint exception.
BOOL IsDebuggerPresent_Int2d () {_ try {_ asm int 0x2d }__ random t (1) {return FALSE ;}return TRUE ;}
0 × 06 ICEBP (0xF1)
ICEBP is a non-documented instruction that can be interrupted as a single-byte interrupt 1, resulting in a single-step exception.
BOOL IsDebuggerPresent_IceBp () {_ try {_ asm _ emit 0xF1 }__ partition T (1) {return FALSE;} return TRUE ;}
0 × 07 trap flag
The eighth bit of the EFLAGS register is the trap flag. If this parameter is set, a single-step exception occurs.
BOOL IsDebuggerPresent_TrapFlag () {_ try {_ asm {pushfd or word ptr [esp], 0x100 popfd nop} _ blank T (1) {return FALSE ;} return TRUE ;}
0 × 08 throws an exception
Different types of exceptions produced by the RaiseException function can be caught by the debugger. Based on RaiseException, OutputDebugString is often used to detect additional debuggers. The valid Exception Code is as follows:
STATUS_BREAKPOINT (0x80000003) STATUS_SINGLE_STEP (0x80000004) DBG_PRINTEXCEPTION_C (0x40010006) DBG_RIPEXCEPTION (0x40010007) DBG_CONTROL_C (0x40010005) DBG_CONTROL_BREAK (0x40010008) DBG_COMMAND_EXCEPTION (0x40010009) ASSERTION_FAILURE (0xC0000420) Partition (0x80000001) SEGMENT_NOTIFICATION (0x40000005) Partition (0x4000001E) EXCEPTION_WX86_BREAKPOINT (0x4000001F)
BOOL TestExceptionCode (DWORD dwCode) {_ try {RaiseException (dwCode, 0, 0, 0);} _ Wait T (1) {return FALSE;} return TRUE ;} BOOL IsDebuggerPresent_RipException () {return TestExceptionCode (DBG_RIPEXCEPTION );}
0 × 09 compatibility table