← 목록으로

안티 디버깅 / 안티 리버싱 기법과 우회

[Rev] 리버싱 · 작성: 2026-07-19 19:32:50 · 수정: 2026-07-21 23:27:57 · 조회 15

안티 디버깅을 "디버거 탐지 함수 몇 개"로 알면, 각 기법이 정확히 어떤 커널/CPU 메커니즘의 특성을 되짚어 이용하는지를 놓치게 된다. 동적 분석 글에서 다룬 ptrace 원리를 알아야 왜 각 안티 디버깅 기법이 성립하는지 이해된다.

1) ptrace(PTRACE_TRACEME) 셀프 트레이스 — 가장 흔한 리눅스 기법

if (ptrace(PTRACE_TRACEME, 0, 0, 0) == -1) {
    // 이미 누군가 이 프로세스를 추적 중 — 즉 디버거가 붙어있다는 뜻
    exit(1);
}

리눅스 커널은 한 프로세스에 대해 동시에 하나의 tracer만 허용한다. 프로그램이 시작하자마자 스스로 자신을 TRACEME로 선언해버리면, 커널 입장에서는 "이미 tracer가 있다"고 기록되므로 이후 gdb가 PTRACE_ATTACH를 시도해도 커널이 EPERM(권한 거부)을 반환하며 실패한다 — 디버거를 "탐지"하는 게 아니라, 디버거가 애초에 붙을 수 있는 자리를 먼저 차지해버리는 방식이다.

우회: ptrace 시스템 콜 자체를 후킹(LD_PRELOAD로 ptrace 함수를 항상 성공(0)을 반환하도록 가로채기)하거나, strace/Frida처럼 ptrace에 의존하지 않는 다른 방식의 계측 도구로 전환한다.

2) /proc/self/statusTracerPid 확인

FILE *f = fopen("/proc/self/status", "r");
// "TracerPid:" 라인의 값이 0이 아니면 누군가 이 프로세스를 추적 중

커널이 /proc 가상 파일시스템에 현재 자신을 추적 중인 프로세스의 PID를 그대로 노출해준다는 점을 이용한 탐지다. 우회: 이 파일을 여는 open/read 시스템 콜을 후킹해서 TracerPid: 0으로 조작된 내용을 반환하게 만들거나, 커널 모듈 레벨에서 이 값을 숨긴다.

3) 타이밍 기반 탐지 — rdtsc 명령어

uint64_t t1 = __rdtsc();
// 디버거가 여기서 브레이크포인트로 멈추면 실제 경과 시간이 비정상적으로 길어짐
uint64_t t2 = __rdtsc();
if (t2 - t1 > THRESHOLD) exit(1);

rdtsc는 CPU 자체의 사이클 카운터를 직접 읽는 명령어라 OS를 거치지 않고 하드웨어에서 직접 값을 가져온다. 디버거가 브레이크포인트로 실행을 멈추면, 그 사이의 실제 경과 시간이 명령어 몇 개를 실행하는 정상적인 시간보다 훨씬 길어진다는 걸 이용한 탐지다. 우회: 디버거 스크립트로 rdtsc 명령어 실행 시점마다 값을 조작해 항상 짧은 간격처럼 보이게 만들거나(가상화/에뮬레이션 계층에서 rdtsc를 가로챔), 브레이크포인트 대신 로깅만 하는 비침습적 계측(Frida 등)으로 전환한다.

4) 하드웨어 브레이크포인트 탐지 — 디버그 레지스터(DR0-DR7)

x86 CPU는 소프트웨어 int3 없이도, **디버그 레지스터(DR0~DR3에 감시할 주소, DR7에 활성화 플래그)**를 이용해 특정 메모리 접근 시 트랩을 거는 하드웨어 브레이크포인트를 지원한다. 프로그램이 자신의 디버그 레지스터 값을 직접 읽어(GetThreadContext/ptrace(PEEKUSER)) 0이 아니면 하드웨어 브레이크포인트가 걸려있다고 판단할 수 있다.

5) 코드 무결성 자체 검사 (Self-Checksum)

디버거로 int3(0xCC)를 심는 소프트웨어 브레이크포인트는 코드 섹션의 바이트를 실제로 변경한다. 프로그램이 실행 중 자기 코드 섹션의 체크섬/해시를 계산해서 원본과 비교하면, 브레이크포인트가 걸린 위치의 바이트 변화(0xCC)를 감지할 수 있다.

여기서 끝나지 않는다: 실전에서의 접근

안티 디버깅을 "디버거를 막는 기술"이 아니라 **"디버거가 의존하는 커널/CPU 메커니즘(ptrace 단일 tracer 제약, /proc 노출 정보, rdtsc, 디버그 레지스터, 코드 무결성)의 부산물을 역으로 관찰하는 기법들의 모음"**으로 이해하면, 왜 우회 방법도 결국 "그 부산물이 관찰되는 지점 자체를 조작"하는 방향으로 수렴하는지가 같은 논리로 설명된다.

리버싱 카테고리의 글 (4/11)

  1. 리버싱이란 무엇인가: 정적 분석 기초
  2. 어셈블리어 읽기 기초 (x86-64)
  3. 동적 분석: 디버거로 실행 흐름 추적하기
  4. 안티 디버깅 / 안티 리버싱 기법과 우회
  5. 패킹과 언패킹: 실행 파일을 압축/암호화해서 숨기기
  6. Frida로 동적 계측/후킹하기
  7. 안드로이드 앱 리버싱: APK 분석 기초
  8. iOS 앱 리버싱 기초
  9. 크랙미(Crackme) 실습 방법론
  10. PE 포맷 내부 구조: Windows 실행 파일 뜯어보기
  11. 코드 난독화 해제: Control Flow Flattening 분석하기
← 동적 분석: 디버거로 실행 흐름 추적하기 패킹과 언패킹: 실행 파일을 압축/암호화해서 숨기기 →