For a long time Apple has had a debugger-detection function on their developer site. It’s not very hard to defeat with a debugger, but since this technique is sometimes used by malware to try to prevent debugging, I decided to document how to defeat it.
NOTE: These examples assume compiling to x86_64, the current default.
An minimal program using Apple’s function goes something like this:
intmain(int argc, char *argv[]) { int debugger = AmIBeingDebugged(); if (debugger) { printf("FAILURE\n"); return1; }
printf("SUCCESS\n"); return0; }
If we run it with and without a debugger, we get 2 different results:
1 2 3 4 5 6 7 8 9 10 11 12 13
$ ./main SUCCESS $ echo $? 0
$ lldb main (lldb) target create "main" rCurrent executable set to 'main' (x86_64). (lldb) r Process 17532 launched: '.../main' (x86_64) FAILURE Process 17532 exited with status = 1 (0x00000001) (lldb)
Let’s use a debugger to change that!
As we can see, sysctl is populating a struct with data, and info.kp_proc.p_flag ends up with a P_TRACED flag set if the progress is being traced (debugged).
From that we learn that info.kp_proc.p_flag has a type of int, and we can calculate the offset, but an even easier way to calculate the offset of the value, and the value of P_TRACED is to just compile and run a little C program.
Now it’s just a simple matter of setting a breakpoint on sysctl to read the structure address from the rdx register, finish the function call, and use an LLDB expression to XOR out the P_TRACED flag for each sysctl call.
The LLDB expression might looks something like this, substituting the address for the one read at function start.
Success! We have fooled the debugger detection code!
Obviously if you are doing this to avoid debugger detection in a piece of software, you will probably want to script this for your use-case. Fortunately, LLDB has python scripting capabilities, which I recommend you take advantage of.
Comments