Debugger Detection By Nick Cano This tutorial is intended for detection of debuggers attached to 32-bit processes. I'm not sure about compatibility with 64-bit binaries for some of the methods. Introduction: Many times, we as programmers have been faced with the task of securing our programs. Whether we are masking malicious intent, hiding intellectual property or thwarting crackers, obfuscation is everywhere. One very effective ways of defending your program is to check for attached debuggers and exit the program if one is detected. (Or, better yet, NULL every single byte in memory!) Whatever your program, your intent and your preference on what to do when a debugger is detected, the methods I will describe will help you obscure your code-flow and effectively leave people trying to analyze your software at a loss. In this tutorial, I will cover 7 methods of detecting debuggers. 1. Calling the CheckRemoteDebuggerPresent() API method 2. Checking for Interrupt Breakpoints 3. Using debug prefixes to skip execution of breakpoints 4. Looking to see if any hardware breakpoints are present 5. Reading the PEB BeingDebugged member 6. Detecting a 0 return from GetLastError() after calling OutputDebugString() 7. Testing to see if exception 0x40010007 gets handled Method 1: Calling the CheckRemoteDebuggerPresent() API method This is probably the simplest way of detecting a debugger. Unlike all of the other methods, this one doesn't really need to be explained much, so I'll just show the code. bool IsRemoteDebuggerPresent() { BOOL dbg = false; CheckRemoteDebuggerPresent(GetCurrentProcess(), &dbg); return dbg; }

Method 2: Checking for Interrupt Breakpoints This method, like many of the ones later in this tutorial, will test for debuggers using a try{} block. There are many exceptions which debuggers will automatically handle, and this can be exploited to also detect them. This method, particularly, executes Interrupt operations (INT3 and 0x2D, to be specific). It then returns false if it gets the exception – this means the exception wasn't handled by a debugger. If a debugger does, however, handle the exception, the procedure returns true.

inline bool Has2DBreakpointHandler() { __try { __asm int 0x2d } __except (EXCEPTION_EXECUTE_HANDLER){ return false; } return true; } inline bool HasINT3BreakpointHandler() { __try { __asm int 3 } __except (EXCEPTION_EXECUTE_HANDLER){ return false; } return true; }

Method 3: Using debug prefixes to skip execution of breakpoints Many debuggers use what are known as “prefixes.” Prefixes are opcodes which have no real meaning in execution and are solely there for the debuggers use (as far as I know). Thus, executing one outside of a debugger replicates a NOP and the opcode following it is executed as normal. However, when executed within a debugger, the debugger will completely ignore the prefix and the following OP, effectively treating the whole statement as three NOPs. To test for debug prefixes, we will use the 0xF3 0x64 prefix, followed by an INT2D. If there is a debugger, the interrupt will be skipped over. If there isn't we will catch the exception. inline bool IsDebugPrefixHandled() { __try { __asm { __emit 0xF3 __emit 0x64 int 0x2d } } __except(EXCEPTION_EXECUTE_HANDLER){ return false; } return true; }

Method 4: Looking to see if any hardware breakpoints are present One thing debuggers like to take advantage of are the 4 available hardware breakpoints your processor has. If a debugger is attached and has any hardware breakpoints set, we can grab the thread context for the current thread and see where those breakpoints are.

bool HasHardwareBreakpoints() { CONTEXT ctx = {0}; ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; HANDLE hThread = GetCurrentThread(); if(GetThreadContext(hThread, &ctx) == 0) return false; return (ctx.Dr0 != 0 || ctx.Dr1 != 0 || ctx.Dr2 != 0 || ctx.Dr3 != 0); }

Method 5: Reading the PEB BeingDebugged member Another very simple way to detect if a debugger is present is to read the PEB (Process Environment Block) and see if its BeingDebugged member is true. inline bool DoesPEBShowDebugger() { DWORD result = 0; __asm { MOV EAX, FS:[30h] ADD EAX, 2 MOV EAX, [EAX] AND EAX, 0x000000FF TEST EAX, EAX JNE isDebugged JMP exit isDebugged: MOV result, 1 exit: } return (result == 1); }

Method 6: Detecting a 0 return from GetLastError() after calling OutputDebugString() Besides our first method, this is probably the easiest one of all. It is also, however, one of the most overlooked methods. The logic behind this is simple: if OutputDebugString() succeeds, a debugger must be present. If the return is erroneous, it's probably because we don't have a debugger to output too. inline bool CanCallOutputDebugString() { SetLastError(0); OutputDebugStringA("test"); return (GetLastError() == 0); }

Method 7: Testing to see if exception 0x40010007 gets handled This is another simple exception handler check. Nearly every debugger handles the 0x40010007 exception, so detecting it is a matter of seeing if we handle it or not when artificially triggered.

inline bool hasRIPExceptionHandler() { __try { RaiseException(0x40010007, 0, 0, 0); } __except(EXCEPTION_EXECUTE_HANDLER){ return false; } return true; }

Conclusion: Though these are only a few of many methods to detect debuggers, they are very effective and a good start to protecting your software. These methods combined with obfuscation and a few more advanced techniques can surely make your program much harder to analyze. A few other anti-reversing techniques you can pair with these would be zeroing all the function names and DLL names in the import table after forking, zeroing the beginning of the PE header, and even zeroing all executable memory when a debugger is detected. Furthermore, there are many exploits out there which can completely crash many different debuggers. You should be able to find the full project, along with further tutorials, on the same host as this tutorial.

Debugger Detection.pdf

debugger must be present. If the return is erroneous, it's probably because we don't have a. debugger to output too. bool HasHardwareBreakpoints(). {. CONTEXT ctx = {0};. ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;. HANDLE hThread = GetCurrentThread();. if(GetThreadContext(hThread, &ctx) == 0). return false;.

87KB Sizes 4 Downloads 46 Views

Recommend Documents

The AndBug Debugger - GitHub
Many small tools wrapped by "Android" and Eclipse. Includes a ... or emulator. Key dependency of ANY Android debugger. ... AndBug is 90% Python, 10% C.

TensorFlow Debugger: Debugging Dataflow Graphs for Machine ...
Debuggability is important in the development of machine-learning (ML) systems. Several widely-used ML libraries, such as TensorFlow and Theano, are based on ... Each node in the original graph is watched by a pair of nodes, a Copy (C).