Following the above article, continue to discuss the anti debugging technology of Linux.
7: Check the running status of the process
The debugger can debug by attach ing to an existing process. In this case, the parent process of the debugged process is not a debugger.
In this case, we can directly check the running state of the process to determine whether it is debugged. The / proc file system is still used here. Specifically, we check the / proc/self/status file.
When changing from non debugging state to debugging state, the process state changes from sleeping to tracing stop, and TracerPid also changes from 0 to non-0, that is, the PID of the debugger. Thus, we can check the value of TracerPid in the status file to determine whether there is being debugged.
It is worth noting that the / proc directory contains a lot of information about the process. Here we are reading the status file. In addition, we can also obtain process related information, including running status, through the / proc/self/stat file.
#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> #include <dirent.h> #include <sys/ptrace.h> #include <sys/types.h> #include <sys/stat.h> void UseProcStatus() { const char *needle = "TracerPid:"; size_t nl = strlen(needle); char buf1[512]; FILE* fin; int tpid; memset(buf1, 0, 512); fin = fopen("/proc/self/status", "r"); while(fgets(buf1, 512, fin)) { if(!strncmp(buf1, needle, nl)) { sscanf(buf1, "TracerPid: %d", &tpid); if(tpid != 0) { printf("Debugger detected!\n"); fclose(fin); exit(0); } } } fclose(fin); printf("All good\n"); } int main(int argc, char *argv[]) { UseProcStatus(); }
8: Set the maximum time for the program to run
This method is often seen in CTF competitions. Due to the breakpoints, checking and modifying memory and other operations during debugging, the running time of the program is often much longer than the normal running time. Therefore, once the program runs too long, it may be due to being debugged.
Specifically, when the program starts, set the timing through alarm, and when it arrives, stop the program.
However, it should be noted that this method can be easily bypassed. We can set the processing method of gdb for signal. If we choose to ignore SIGALRM instead of passing it to the program, the alarmHandler will not be executed.
#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> #include <dirent.h> #include <sys/ptrace.h> #include <sys/types.h> #include <sys/stat.h> void alarmHandler(int sig) { printf("Debugger detected"); exit(0); } void UseAlarm() { signal(SIGALRM, alarmHandler); alarm(2); } int main(int argc, char *argv[]) { UseAlarm(); }
9: Check the filedescriptor that the process opens
If the debugged process is started by gdb < target >, it is obtained by the gdb process fork. When fork is called, the fd(file descriptor) owned by the parent process will be inherited by the child process. Because gdb often opens multiple FDS, if a process has more FDS, it may inherit from gdb, that is, the process is being debugged.
Specifically, the fd owned by the process will be listed under / proc/self/fd /.
It should be noted that this method has a probability that it cannot be checked.
#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> #include <dirent.h> #include <sys/ptrace.h> #include <sys/types.h> #include <sys/stat.h> void UseFdNum() { struct dirent *dir; DIR *d = opendir("/proc/self/fd"); while(dir = readdir(d)) { if(!strcmp(dir->d_name, "5")) { printf("Debugger detected!"); closedir(d); exit(0); } } closedir(d); printf("All good!\n"); } int main(int argc, char *argv[]) { UseFdNum(); }