← 목록으로

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

[Rev] 리버싱 · 작성: 2026-07-19 19:32:50 · 조회 5

분석당하기 싫은 프로그램(악성코드, 상용 소프트웨어의 라이선스 체크 로직, 게임 치트 방지 등)은 디버거가 붙어있는지 스스로 감지하거나, 분석을 어렵게 만드는 장치를 넣어둔다. 이 글은 자주 나오는 패턴과 우회 방법을 정리한다.

디버거 감지 기법

API 기반 감지 (Windows)

if (IsDebuggerPresent()) { exit(1); }

가장 단순한 방식. CheckRemoteDebuggerPresent, PEB(Process Environment Block)의 BeingDebugged 플래그를 직접 읽는 변종도 있다.

ptrace 기반 감지 (Linux)

if (ptrace(PTRACE_TRACEME, 0, 0, 0) == -1) { exit(1); }

리눅스는 한 프로세스에 디버거를 하나만 붙일 수 있다는 점을 이용한다. 자기 자신을 먼저 PTRACE_TRACEME로 추적 시도해서, 이미 gdb 같은 디버거가 붙어있으면 이 호출이 실패하는 걸로 감지한다.

타이밍 기반 감지

start = rdtsc();
// 아무 동작
end = rdtsc();
if (end - start > THRESHOLD) { /* 디버거가 브레이크포인트로 멈췄다 판단 */ }

브레이크포인트에 걸려 실행이 잠깐 멈추면 명령어 사이 시간 간격이 비정상적으로 커진다는 걸 이용한다.

하드웨어 브레이크포인트 감지

디버그 레지스터(DR0-DR7)에 값이 설정되어 있는지 직접 읽어서, 하드웨어 브레이크포인트가 걸려있는지 확인한다.

우회 방법

  1. 체크 함수 자체를 패치: 디스어셈블러/디버거로 감지 로직을 찾아서, 해당 분기를 항상 "감지 안 됨" 쪽으로 가도록 바이너리를 직접 수정한다 (예: jnenop으로).
  2. API 후킹: IsDebuggerPresent 같은 함수의 리턴값을 Frida나 API 후킹 도구로 강제로 0(False)으로 바꿔치기한다. 바이너리 자체는 건드리지 않아서 재분석할 때마다 패치를 다시 할 필요가 없다.
  3. 커널 레벨 디버거 사용: 유저 레벨에서 감지되는 방식(IsDebuggerPresent 등)은 커널 레벨 디버거(예: Windows의 WinDbg 커널 모드)로는 애초에 감지되지 않는 경우가 있다.
  4. PEB 직접 패치: 메모리 상의 PEB 구조체에서 BeingDebugged 바이트를 디버거로 직접 0으로 바꿔버린다.

난독화(Obfuscation)도 함께 나오는 경우가 많다

안티 디버깅과 별개로, 분석 자체를 어렵게 만드는 기법들도 같이 쓰인다.

실전 팁

안티 디버깅 우회는 "감지 로직을 찾는 것" 자체가 리버싱 실력을 요구하는 역설적인 상황이다. 처음엔 strings로 관련 API 이름이나 ptrace 같은 특징적인 함수 호출을 먼저 검색해서 감지 코드의 위치부터 좁히는 걸 권한다.

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

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