← 목록으로

동적 분석: 디버거로 실행 흐름 추적하기

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

정적 분석만으로는 "이 변수가 실제로 어떤 값을 갖는지", "이 분기가 실제로 어느 쪽으로 가는지"를 확신하기 어려울 때가 많다. 디버거로 실제 실행시키면서 관찰하면 훨씬 빠르게 답이 나온다.

플랫폼별 도구

플랫폼 도구
Linux gdb (+ pwndbg 또는 gef 플러그인으로 UI 개선)
Windows x64dbg, WinDbg
macOS lldb
크로스플랫폼 후킹 Frida (다음 글에서 별도로 다룸)

기본 gdb는 화면이 텍스트뿐이라 레지스터/스택 상태를 보기 불편해서, 실무에서는 거의 항상 pwndbg나 gef 같은 플러그인을 얹어서 쓴다.

기본 워크플로우

gdb ./target
break *0x401234      # 특정 주소에 브레이크포인트
break check_password  # 함수 이름으로 브레이크포인트 (심볼이 있을 때)
run                    # 실행 시작
next / n               # 한 줄 실행 (함수 호출은 건너뜀)
step / s                # 한 줄 실행 (함수 호출 안으로 들어감)
continue / c             # 다음 브레이크포인트까지 실행

실행 중 상태 확인

info registers        # 전체 레지스터 값
x/10xg $rsp            # 스택 상위 10개(8바이트씩)를 16진수로
x/s $rdi                # rdi가 가리키는 문자열 출력
disassemble             # 현재 함수 어셈블리 보기

pwndbg/gef를 쓰면 브레이크포인트에 걸릴 때마다 레지스터·스택·현재 명령어를 한 화면에 자동으로 정리해서 보여줘서, 위 명령어들을 매번 칠 필요가 줄어든다.

정답 체크 로직 찾는 실전 흐름 (crackme 예시)

  1. strings로 "Wrong password" 같은 실패 메시지 문자열을 찾는다
  2. 그 문자열을 참조하는 코드 위치를 디스어셈블러에서 역추적한다 (Ghidra의 "References" 기능 등)
  3. 그 근처의 cmp/je/jne 분기 지점에 브레이크포인트를 건다
  4. 아무 값이나 입력해서 실행시키고, 브레이크포인트에서 비교 대상 레지스터/메모리 값을 확인한다
  5. 그 값이 바로 정답이거나, 정답을 계산하는 함수의 결과다

트레이싱: 흐름만 빠르게 훑기

브레이크포인트를 하나씩 거는 대신, 어떤 시스템 콜/라이브러리 함수를 호출하는지 순서대로 쭉 보고 싶을 때는 트레이싱 도구가 더 빠르다.

strace ./target    # 시스템 콜 추적 (open, read, connect 등)
ltrace ./target     # 라이브러리 함수 호출 추적 (strcmp, malloc 등)

암호화되지 않은 비교라면 ltracestrcmp("입력값", "실제정답") 형태로 그냈로 정답이 찍히는 경우도 흔하다 — 정적 분석보다 훨씬 빠른 지름길이 될 때가 있다.

조건부 브레이크포인트

특정 조건일 때만 멈추게 하면 반복문 안에서 원하는 순간만 잡을 수 있다.

break *0x401234 if $rax == 5

정적 분석으로 "어디를 봐야 할지" 가설을 세우고, 동적 분석으로 그 가설을 실제 값으로 검증하는 반복이 리버싱의 기본 사이클이다.

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

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