Vault7: CIA Hacking Tools Revealed
Navigation: » Latest version
Debug Print Debugger Detection
Overview
OutputDebugString, and other functions like it, effectively causes an exception to be raised (DBG_PRINTEXCEPTION_C or 0x40010006) which Windows catches. The arguments for the exception are the debug message string, and the length of the string.
When a debugger is attached, Windows will eat the exception and let the debugger know. If no debugger is attached, the exception is passed to the program (as a continuable error). If the program
does not have a handler for the exception, then life goes on. If the program does, then that exception handler is called. The former is most likely in normal programs, since there's usually no reason
for a program to handle the event when OutputDebugString has no debugger to print to.
In the case of anti-debugging, if a program uses RaiseException to throw DBG_PRINTEXCEPTION_C, and have an exception handler ready to handle the exception, you can detect a debugger's presence by checking to see if your handler was called. This
can be something as simple as follows:
BOOL IsThereADebugger()
{
__try
{
RaiseException(DBG_PRINTEXCEPTION_C, 0, 0, 0);
}
__except(GetExceptionCode() == DBG_PRINTEXCEPTION_C)
{
return FALSE;
}
return TRUE;
}
******
In a commercial program I looked at, they use the same method in a more 'advanced' way:
typedef struct _ARGS{
DWORD_PTR one;
DWORD_PTR two;
} ARGS, *PARGS;
BOOL IsThereADebugger()
{
BOOL bReturn;
__try
{
bReturn = StackAdjust(10);
}
__except(GetExceptionCode() == DBG_PRINTEXCEPTION_C)
{
LPEXCEPTION_POINTERS pepException = GetExceptionInformation();
pepException->ExceptionRecord->ExceptionInformation[1] = 0x10;
<other context-record specific stuff>
return 0xFFFFFFFF;
}
return bReturn;
}
//Argument is placed in ECX, not on stack
//I believe the purpose of this function was to decrement ESP far enough so as to not overwrite anything important, though that doesn't make a whole lot of sense to me either...
//The value 0x2c was ~ how much ESP was decremented prior to the first call to StackAdjust in IsThereADebugger().
BOOL StackAdjust(int iterations)
{
__asm{
sub esp, 0x2c
dec ecx
jle RaiseExcept
call StackAdjust
Unwind:
add esp, 0x2c
ret
RaiseExcept:
call RaiseException
jmp Unwind
}
}
BOOL RaiseException()
{
DWORD dwFlag = 0;
DWORD dwTemp; //not important
ARGS args;
args.one = &dwTemp;
args.two = &dwFlag;
//Program only used 1 argument in the exception handler, but sends 2 because that's what is expected with this type of exception
RaiseException(DBG_PRINTEXCEPTION_C, 0, 2, &args);
if (dwFlag == 0)
return TRUE; //Debugger was present since our exception handler was not called
else
return FALSE; //Our exception handler was called, so no debugger is present
}
I'm not sure whether or not this more complex example is better than the simplier one.