Ptrace Anti-debugging
The debuggers like gdb utilize the ptrace() function to attach to a process at runtime. Since only one process is allowed to do this at a time, having a call to ptrace() in your code can be used as an anti-debugging technique.
For the code example:
1 |
|
If we execute the program we can get the normal output every second, but if we debug it with strace the process will exit abnormally.
1 | strace ./a.out |
To bypass it, we can patch the ptrace part with NOP. And also, this anti-debugging method can be bypassed by using the LD_PRELOAD environment variable, which allows us to control the loading path of a shared library.
Create the following file:
1 | long ptrace(int request, int pid, void *addr, void *data) { |
Compile it as a shared library:
1 | gcc -shared -fPIC ptrace.c -o ptrace.so |
Set the LD_PRELOAD environment variable:
In the shell:
1 | export LD_PRELOAD=./ptrace.so |
In gdb:
1 | set environment LD_PRELOAD=./ptrace.so |
The output will be like this:
1 | (gdb) file a.out |
Monitoring TracerPid
TracerPid is a value which can reflect the status of the process at runtime. If the process is being attached to a debugger, the value of TracerPid won’t be 0 or the PID of the target process. Instead, the TracerPid indicates the PID of the debugger process.
We have the following code:
1 |
|
Execute the program and let’s take a look at the process status:
1 | cat /proc/31589/status |
We can find that the TracerPid is 0 which means the program itself is not being debugged.
Debug the program with gdb:
1 | (gdb) file ./a.out |
Check the TracerPid:
1 | cat /proc/31652/status |
Then let’s trace the PID to which the TracerPid indicates:
1 | ps aux | grep 31649 |
Detecting the Ports of Debuggers
Detecting the Names of Debuggers
Debuggers like android_server, gdb, gdbserver can be retrieved by enumerating the processes or checking the installation directories. For example:
1 | cat /proc/33555/cmdline |
The output shows that the IDA remote debugger is running. And we can also check the debugger installation directories such as /data/local/tmp which stores the IDA android_server binary.
Detecting the Debugging Breakpoints
Detecting the software breakpoints is a common anti-debugging approach which can be implemented by looping through the execution segments to detect the software breakpoint instructions.
The breakpoint instructions of the related instruction sets:
Instruction Set | Instruction |
---|---|
arm_linux_arm_le | { 0x01, 0x00, 0x9f, 0xef } |
arm_linux_arm_be | { 0xef, 0x9f, 0x00, 0x01 } |
eabi_linux_arm_le | { 0xf0, 0x01, 0xf0, 0xe7 } |
eabi_linux_arm_be | { 0xe7, 0xf0, 0x01, 0xf0 } |
arm_linux_thumb_le | { 0x01, 0xde } |
arm_linux_thumb_be | { 0xde, 0x01 } |
arm_linux_thumb2_le | { 0xf0, 0xf7, 0x00, 0xa0 } |
arm_linux_thumb2_be | { 0xf7, 0xf0, 0xa0, 0x00 } |
Inotify Detection
Under Linux, inotify can monitor file system events (open, read, delete, delete, etc.). The hardening scheme can monitor some files of apk itself through inotify. Some memory dump technologies pass /proc/pid/maps, /proc/pid/mem to achieve memory dump, so monitoring the reading and writing of these files can also play a certain anti-debugging effect.
Detecting the Time Interval or Latency of Code Execution
By calculating the execution time difference of a certain part of the code to determine whether it is debugged, under the Linux kernel, the current time can be obtained by time, gettimeofday, or directly by sys call. In addition, you can also determine whether the program runs timeout by customizing the SIGALRM signal.
References
https://android.googlesource.com/toolchain/gdb/+/refs/heads/master/gdb-9.2/gdb/arm-linux-tdep.c