현재 프로세스가 GDB에서 실행 중인지 탐지하는 방법
표준 방법은 다음과 같다.
if (ptrace(PTRACE_TRACEME, 0, NULL, 0) == -1)
printf("traced!\n");
이 경우 ptrace는 현재 프로세스를 추적할 경우(예: GDB로 실행하거나 ptrace에 부착) 오류를 반환한다.
그러나 여기에는 심각한 문제가 있다: 만약 통화가 성공적으로 돌아온다면, GDB는 나중에 그것에 연결되지 않을 수도 있다.그것은 내가 반디버그적인 것을 실행하려고 하지 않기 때문에 문제다.내 목적은 조건이 충족되고(예: 어설션 실패) GDB가 실행 중일 때(그렇지 않으면 응용 프로그램을 중지하는 SIGTRAP를 얻음) 'int 3'을 방출하는 것이다.
테스트 중인 응용 프로그램이 다른 목적으로 SIGTRAP를 사용할 수 있기 때문에 매번 SIGTRAP를 비활성화하고 'int 3'을 방출하는 것은 좋은 해결책이 아니다(이 경우 나는 여전히 망쳐져 있으므로 문제가 되지 않지만 사물의 원리 :).
Windows에는 프로세스가 디버깅 중인지 확인하기 위한 API IsDebuggerPresent가 있다.리눅스에서는 이것을 다른 방법(효율적이지 않음)으로 확인할 수 있다.
"/proc/self/status"에서 "TracerPid" 특성을 확인하십시오.
예제 코드:
#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;
}
나도 비슷한 욕구를 가지고 있었고, 다음과 같은 대안을 생각해 냈다.
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);
}
}
호출될 경우 디버그_break 기능은 디버거가 연결된 경우에만 중단된다.
x86에서 실행 중이고 호출자(상승하지 않음)에서 인터럽트되는 중단점을 원하는 경우 다음 헤더를 포함하고 debug_break 매크로를 사용하십시오.
#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
이는 종단부의 대답과 유사하지만, 통신을 위해 파이프를 사용한다.
#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;
}
}
OS X에 따라 원래 코드를 시도하다 보니 waitfid (부모의 경우)가 항상 EINTR(시스템 호출 중단)으로 -1을 반환한다는 것을 알았다.이것은 팻택이 부모에게 달라붙어 통화를 방해했기 때문에 생긴 것이다.
그냥 미지근하다고 다시 전화하는 것이 안전한지 확실하지 않아서(어떤 상황에서는 잘못 행동할지도 모르는 것 같았다) 나는 대신 파이프를 사용하여 통신을 했다.그것은 약간의 추가 코드지만, 아마도 더 많은 플랫폼에서 안정적으로 작동할 것이다.
이 코드는 OS X v10.9.3 (Mavericks), Ubuntu 14.04 (Trusty Tahr) (3.13.0-24-generic) 및 FreeB에서 테스트되었다.SD 10.0.
프로세스 기능을 구현하는 Linux의 경우 이 방법은 프로세스에 다음이 있을 경우에만 작동된다.CAP_SYS_PTRACE
일반적으로 프로세스가 루트로 실행될 때 설정되는 기능.
기타 유틸리티(gdb
그리고lldb
로 이 또한 파일 시스템 메타데이터의 일부로 이 기능 세트를 가지고 있다.
공정이 효과적인지 여부를 탐지할 수 있다.CAP_SYS_PTRACE
東京都와 -lcap
#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;
}
당신은 아이를 갈퀴로 갈 수 있다.PTRACE_ATTACH
부모(그리고 필요한 경우 분리)를 수행하고 결과를 다시 전달한다.하지만 그것은 약간 무례하게 보인다.
네가 언급했듯이, 이것은 꽤 비용이 많이 든다.주장이 불규칙적으로 실패해도 그리 나쁘지 않은 것 같아.아마도 오랫동안 단 한 명의 아이가 이것을 하기 위해 주변에 두는 것이 가치 있는 일일 것이다 - 부모와 자식 사이에 두 개의 파이프를 공유하고, 아이가 바이트를 읽을 때 체크하고, 그리고 나서 그 상태와 함께 바이트를 다시 보낸다.
내가 사용하게 된 코드는 다음과 같았다.
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가 부착될 때도 작동한다.
- 단점은 자주 사용하면 심각한 둔화를 유발한다는 점이다.
- 또한 이 솔루션은 리눅스에서만 작동하는 것으로 확인된다.코멘트가 언급했듯이 BSD에서는 효과가 없을 것이다.
애플리케이션이 디버깅을 위해 GDB에서 실행되는지 여부를 알고자 한다면 Linux에서 가장 간단한 솔루션은readlink("/proc/<ppid>/exe")
, 그리고 결과를 검색한다."gdb"
.
나는 실비아에 의해 기술되고 xorl에 의해 블로그된 파일 설명자 "hack"의 수정된 버전이 나에게 잘 작동한다는 것을 발견했다.
이것은 내가 사용하는 수정된 코드다.
#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;
}
Sam Liao의 대답 C++ 버전(리눅스에만 해당):
// Detect if the application is running inside a debugger.
bool being_traced()
{
std::ifstream sf("/proc/self/status");
std::string s;
while (sf >> s)
{
if (s == "TracerPid:")
{
int pid;
sf >> pid;
return pid != 0;
}
std::getline(sf, s);
}
return false;
}
참조URL: https://stackoverflow.com/questions/3596781/how-to-detect-if-the-current-process-is-being-run-by-gdb
'programing' 카테고리의 다른 글
Vuex 돌연변이 대상이 주에서 찾는 대신 페이로드로 통과됨 (0) | 2022.05.20 |
---|---|
서로 다른 것을 참조하는 마우스 호버 기능서로 다른 것을 참조하는 마우스 호버 기능같은 Vue.js 함수에서. (0) | 2022.05.20 |
객체 일련화란? (0) | 2022.05.20 |
수업에서 개인 필드의 수정을 방지하려면 어떻게 해야 하는가? (0) | 2022.05.20 |
Vue js 동적으로 구성 요소 가져오기 (0) | 2022.05.20 |