如何检测当前进程是否由GDB运行?
标准的方法如下:
if (ptrace(PTRACE_TRACEME, 0, NULL, 0) == -1) printf("traced!\n");
在这种情况下,如果当前进程被跟踪(即使用gdb运行或附加到它),ptrace会返回一个错误。
但是这样做存在一个严重的问题:如果调用成功返回,那么gdb可能以后不会附加到它。 这是一个问题,因为我没有试图实施反debugging的东西。 我的目的是当一个接触(即一个断言失败)和gdb正在运行(否则我得到一个SIGTRAP,停止应用程序)发射一个'诠释3'。
禁用SIGTRAP并每次发射“int 3”并不是一个好的解决scheme,因为我testing的应用程序可能使用SIGTRAP来达到其他目的(在这种情况下,我还是搞砸了,所以没关系,事物的原则:))
谢谢
以前作为一个评论:你可以派生一个孩子,试图PTRACE_ATTACH
其父(然后分离,如果有必要),并传达结果。 这似乎有点不雅观虽然。
正如你所说,这是相当昂贵的。 如果断言失败,我想这也不算太坏。 也许值得让一个长期运行的孩子来做这件事情 – 在父母和孩子之间共享两个pipe道,孩子在读取一个字节时进行检查,然后用状态发回一个字节。
在Windows上有一个API IsDebuggerPresent来检查进程是否正在debugging。 在linux上,我们可以用另一种方式来检查(不是很有效率)。
检查“ TracerPid ”属性的“ / proc / self / status ”。
示例代码:
#include <sys/stat.h> #include <string.h> #include <fcntl.h> int IsDebuggerPresent(void) { char buf[1024]; int debugger_present = 0; int status_fd = open("/proc/self/status", O_RDONLY); if (status_fd == -1) return 0; ssize_t num_read = read(status_fd, buf, sizeof(buf)-1); if (num_read > 0) { static const char TracerPid[] = "TracerPid:"; char *tracer_pid; buf[num_read] = 0; tracer_pid = strstr(buf, TracerPid); if (tracer_pid) debugger_present = !!atoi(tracer_pid + sizeof(TracerPid) - 1); } return debugger_present; }
我最终使用的代码如下:
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; }
一些东西:
- 当ptrace(PTRACE_ATTACH,…)成功时,被跟踪的进程将停止并且必须继续。
- 这也适用于gdb稍后的连接。
- 缺点是经常使用会造成严重的放缓。
- 而且,这个解决scheme只能在Linux上运行。 正如所提到的意见,它不会在BSD上工作。
无论如何,谢谢你的答案。
我也有类似的需求,并提出了以下的select
static int _debugger_present = -1; static void _sigtrap_handler(int signum) { _debugger_present = 0; signal(SIGTRAP, SIG_DFL); } void debug_break(void) { if (-1 == _debugger_present) { _debugger_present = 1; signal(SIGTRAP, _sigtrap_handler); raise(SIGTRAP); } }
如果调用,debug_break函数只会在debugging器连接时中断。
如果你正在x86上运行,并且需要一个在调用者中断的断点(而不是raise ),只需包含下面的头文件,然后使用debug_breakmacros:
#ifndef BREAK_H #define BREAK_H #include <stdio.h> #include <stdlib.h> #include <signal.h> int _debugger_present = -1; static void _sigtrap_handler(int signum) { _debugger_present = 0; signal(SIGTRAP, SIG_DFL); } #define debug_break() \ do { \ if (-1 == _debugger_present) { \ _debugger_present = 1; \ signal(SIGTRAP, _sigtrap_handler); \ __asm__("int3"); \ } \ } while(0) #endif
我发现Silvioces描述的文件描述符“hack”的修改版本和xorl的 博客工作对我很好。
这是我使用的修改后的代码:
#include <stdio.h> #include <unistd.h> // gdb apparently opens FD(s) 3,4,5 (whereas a typical prog uses only stdin=0, stdout=1,stderr=2) int detect_gdb(void) { int rc = 0; FILE *fd = fopen("/tmp", "r"); if (fileno(fd) > 5) { rc = 1; } fclose(fd); return rc; }
如果您只是想知道应用程序是否在gdb
下运行以进行debugging,Linux上最简单的解决方法是readlink("/proc/<ppid>/exe")
,然后search"gdb"
的结果。
这类似于总站的答案,但使用pipe道进行通信:
#include <unistd.h> #include <stdint.h> #include <sys/ptrace.h> #include <sys/wait.h> #if !defined(PTRACE_ATTACH) && defined(PT_ATTACH) # define PTRACE_ATTACH PT_ATTACH #endif #if !defined(PTRACE_DETACH) && defined(PT_DETACH) # define PTRACE_DETACH PT_DETACH #endif #ifdef __linux__ # define _PTRACE(_x, _y) ptrace(_x, _y, NULL, NULL) #else # define _PTRACE(_x, _y) ptrace(_x, _y, NULL, 0) #endif /** Determine if we're running under a debugger by attempting to attach using pattach * * @return 0 if we're not, 1 if we are, -1 if we can't tell. */ static int debugger_attached(void) { int pid; int from_child[2] = {-1, -1}; if (pipe(from_child) < 0) { fprintf(stderr, "Debugger check failed: Error opening internal pipe: %s", syserror(errno)); return -1; } pid = fork(); if (pid == -1) { fprintf(stderr, "Debugger check failed: Error forking: %s", syserror(errno)); return -1; } /* Child */ if (pid == 0) { uint8_t ret = 0; int ppid = getppid(); /* Close parent's side */ close(from_child[0]); if (_PTRACE(PTRACE_ATTACH, ppid) == 0) { /* Wait for the parent to stop */ waitpid(ppid, NULL, 0); /* Tell the parent what happened */ write(from_child[1], &ret, sizeof(ret)); /* Detach */ _PTRACE(PTRACE_DETACH, ppid); exit(0); } ret = 1; /* Tell the parent what happened */ write(from_child[1], &ret, sizeof(ret)); exit(0); /* Parent */ } else { uint8_t ret = -1; /* * The child writes a 1 if pattach failed else 0. * * This read may be interrupted by pattach, * which is why we need the loop. */ while ((read(from_child[0], &ret, sizeof(ret)) < 0) && (errno == EINTR)); /* Ret not updated */ if (ret < 0) { fprintf(stderr, "Debugger check failed: Error getting status from child: %s", syserror(errno)); } /* Close the pipes here, to avoid races with pattach (if we did it above) */ close(from_child[1]); close(from_child[0]); /* Collect the status of the child */ waitpid(pid, NULL, 0); return ret; } }
尝试在OSX下的原始代码,我发现waitpid(在父)将始终返回-1与EINTR(系统调用中断)。 这是由pattach,附加到父母并打断电话。
现在还不清楚是否再次调用waitpid是安全的(在某些情况下,它似乎可能performance不正确),所以我只是用pipe道来进行通信。 这是一些额外的代码,但可能在更多平台上可靠地工作。
该代码已经在OSX 10.9.3,Ubuntu 14.04(3.13.0-24-generic)和FreeBSD 10.0上testing过。
对于实现进程function的linux,只有在进程具有CAP_SYS_PTRACE
function的情况下,该方法才会起作用,当进程以root身份运行时,通常会设置CAP_SYS_PTRACE
function。
其他实用程序( gdb
和lldb
)也将此function设置为其文件系统元数据的一部分。
您可以通过链接-lcap
来检测进程是否具有有效的CAP_SYS_PTRACE
,
#include <sys/capability.h> cap_flag_value_t value; cap_t current; /* * If we're running under linux, we first need to check if we have * permission to to ptrace. We do that using the capabilities * functions. */ current = cap_get_proc(); if (!current) { fprintf(stderr, "Failed getting process capabilities: %s\n", syserror(errno)); return -1; } if (cap_get_flag(current, CAP_SYS_PTRACE, CAP_PERMITTED, &value) < 0) { fprintf(stderr, "Failed getting permitted ptrace capability state: %s\n", syserror(errno)); cap_free(current); return -1; } if ((value == CAP_SET) && (cap_get_flag(current, CAP_SYS_PTRACE, CAP_EFFECTIVE, &value) < 0)) { fprintf(stderr, "Failed getting effective ptrace capability state: %s\n", syserror(errno)); cap_free(current); return -1; }