Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect if the current process is being run by GDB

Tags:

c

linux

posix

gdb

The standard way would be the following:

if (ptrace(PTRACE_TRACEME, 0, NULL, 0) == -1)   printf("traced!\n"); 

In this case, ptrace returns an error if the current process is traced (e.g., running it with GDB or attaching to it).

But there is a serious problem with this: if the call returns successfully, GDB may not attach to it later. Which is a problem since I'm not trying to implement anti-debug stuff. My purpose is to emit an 'int 3' when a condition is met (e.g., an assert fails) and GDB is running (otherwise I get a SIGTRAP which stops the application).

Disabling SIGTRAP and emitting an 'int 3' every time is not a good solution because the application I'm testing might be using SIGTRAP for some other purpose (in which case I'm still screwed, so it wouldn't matter, but it's the principle of the thing :))

like image 221
terminus Avatar asked Aug 29 '10 22:08

terminus


People also ask

How do I run a process in GDB?

Use the run command to start your program under GDB. You must first specify the program name (except on VxWorks) with an argument to GDB (see section Getting In and Out of GDB), or by using the file or exec-file command (see section Commands to specify files).

How do I stop a program from running in GDB?

To stop your program while it is running, type "(ctrl) + c" (hold down the ctrl key and press c). gdb will stop your program at whatever line it has just executed. From here you can examine variables and move through your program. To specify other places where gdb should stop, see the section on breakpoints below.

Which system call does GDB use?

GDB controls the program that we are debugging (the inferior) through a system call named ptrace().

Does GDB slow down?

GDB does software watchpointing by single-stepping your program and testing the variable's value each time, which is hundreds of times slower than normal execution. (But this may still be worth it, to catch errors where you have no clue what part of your program is the culprit.)


2 Answers

On Windows there is an API, IsDebuggerPresent, to check if process is under debugging. At Linux, we can check this with another way (not so efficient).

Check "/proc/self/status" for "TracerPid" attribute.

Example code:

#include <sys/stat.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <ctype.h>  bool debuggerIsAttached() {     char buf[4096];      const int status_fd = ::open("/proc/self/status", O_RDONLY);     if (status_fd == -1)         return false;      const ssize_t num_read = ::read(status_fd, buf, sizeof(buf) - 1);     ::close(status_fd);      if (num_read <= 0)         return false;      buf[num_read] = '\0';     constexpr char tracerPidString[] = "TracerPid:";     const auto tracer_pid_ptr = ::strstr(buf, tracerPidString);     if (!tracer_pid_ptr)         return false;      for (const char* characterPtr = tracer_pid_ptr + sizeof(tracerPidString) - 1; characterPtr <= buf + num_read; ++characterPtr)     {         if (::isspace(*characterPtr))             continue;         else             return ::isdigit(*characterPtr) != 0 && *characterPtr != '0';     }      return false; } 
like image 114
Sam Liao Avatar answered Sep 21 '22 11:09

Sam Liao


The code I ended up using was the following:

int gdb_check() {   int pid = fork();   int status;   int res;    if (pid == -1)   {     perror("fork");     return -1;   }    if (pid == 0)   {     int ppid = getppid();      /* Child */     if (ptrace(PTRACE_ATTACH, ppid, NULL, NULL) == 0)     {       /* Wait for the parent to stop and continue it */       waitpid(ppid, NULL, 0);       ptrace(PTRACE_CONT, NULL, NULL);        /* Detach */       ptrace(PTRACE_DETACH, getppid(), NULL, NULL);        /* We were the tracers, so gdb is not present */       res = 0;     }     else     {       /* Trace failed so GDB is present */       res = 1;     }     exit(res);   }   else   {     waitpid(pid, &status, 0);     res = WEXITSTATUS(status);   }   return res; } 

A few things:

  • When ptrace(PTRACE_ATTACH, ...) is successful, the traced process will stop and has to be continued.
  • This also works when GDB is attaching later.
  • A drawback is that when used frequently, it will cause a serious slowdown.
  • Also, this solution is only confirmed to work on Linux. As the comments mentioned, it won't work on BSD.
like image 22
terminus Avatar answered Sep 20 '22 11:09

terminus