Step 216. x64dbg 심화: 메모리 브레이크포인트, 패치 — 실행 중인 프로그램을 수술한다

Step 216. x64dbg 심화: 메모리 브레이크포인트, 패치 — 실행 중인 프로그램을 수술한다

Level 3 — Reversing 트랙 | 난이도 ★★★☆☆ | 예상 소요 시간 5시간

전제: Step 178(리버싱 맛보기), Step 215(Ghidra 심화)를 마쳤다. 조건 분기(je/jne)와 비교(cmp/test)를 어셈블리에서 읽을 수 있다.

⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다. 패치는 "내가 만든 연습용 바이너리"에만 합니다 — 남의 소프트웨어를 패치해 배포하면 저작권·라이선스 위반이 될 수 있습니다.

  • 준비물: x64dbg는 Windows GUI 도구라 이 Linux 환경에는 없습니다 — 화면은 예시로 안내하고, 패치와 메모리 감시의 원리는 파이썬과 gdb로 실측합니다 (실측: Ubuntu 24.04, gdb 15.1, Python 3.12).
  • 주의: 오늘의 핵심 두 기술(메모리 브레이크포인트, 패치)은 악성코드 분석에서도 매일 쓰는 정상적 기법입니다. 다만 패치 결과물을 배포하는 것은 별개의 법적 문제임을 기억하세요.

Ghidra가 "읽고 정리하는 책상"이라면, x64dbg는 "실행 중인 프로그램의 조종실"입니다. 오늘 배우는 두 기술은 리버싱의 가장 강력한 수법입니다. 하나는 메모리 브레이크포인트 — "이 값이 대체 어디서 읽히는 거야?"라는 질문에, 값이 읽히는 바로 그 순간에 실행을 멈춰 답해 주는 기능. 다른 하나는 패치 — 프로그램의 명령을 바꿔치기해 인증을 뒤집는 기술. 둘 다 원리는 단순한데, 오늘 그 원리를 파일 바이트 수준까지 내려가 직접 확인합니다.


1. 학습 목표

이 챕터를 끝내면 다음을 할 수 있습니다:

  • x64dbg에서 문자열 검색으로 비교 코드를 역추적하는 루틴을 수행한다
  • 코드 브레이크포인트와 메모리 브레이크포인트의 차이를 설명하고 상황에 맞게 고른다
  • 조건 분기 명령(je/jne)의 기계어 바이트를 식별한다
  • 패치(메모리상 수정 → 파일 저장)의 전 과정을 수행한다
  • 패치의 원리를 파이썬 바이트 조작으로 직접 재현한다

2. 배경 지식 — 오늘의 도구와 개념

오늘의 도구 한눈에 보기

구분 내용
언어·환경 x64dbg는 화면 예시(Windows GUI) / 원리 실측: WSL 우분투의 gdb 15.1 + Python 3.12 (tmp_test 스크립트)
오늘의 명령어·키 x64dbg: 우클릭 → Search for → All referenced strings, F2(BP), 우클릭 → Breakpoint → Memory, 더블클릭으로 명령 수정, Ctrl+P(Patches) / gdb: watch, rwatch / 파이썬: 바이너리 바이트 읽기·쓰기
필요한 개념 코드 BP vs 메모리 BP(하드웨어 BP), 조건 분기의 기계어(0x74/0x75), 가상 주소와 파일 오프셋의 변환
오늘의 산출물 인증이 뒤집힌 패치 바이너리 1개 + 패치 전후 비교 기록

2-1. 코드 브레이크포인트 vs 메모리 브레이크포인트

지금까지 쓴 브레이크포인트는 코드 BP — "이 명령에 도달하면 멈춰라"입니다. 주소를 알아야 겁니다. 그런데 리버싱에서 더 자주 드는 질문은 반대입니다: "이 이 어디서 쓰이는지 모르겠다."

메모리 브레이크포인트(memory breakpoint)는 주소가 아니라 데이터에 겁니다 — "이 메모리가 읽힐 때(또는 쓰일 때) 멈춰라". 내 입력 버퍼에 걸어 두면, 프로그램이 내 입력을 검사하러 읽는 순간에 멈춥니다. 비교 코드를 몰라도 비교 코드로 끌려가는 셈입니다. x64dbg에서는 덤프 창에서 주소 우클릭 → Breakpoint → Memory, on access로 겁니다.

구현은 CPU의 디버그 레지스터(하드웨어 BP)를 쓰므로 개수 제한(보통 4개)이 있습니다. gdb에도 같은 기능이 watch(쓰일 때), rwatch(읽힐 때)로 있습니다 — 오늘 실측해 봅니다.

2-2. 조건 분기의 기계어 — 0x74와 0x75

if (strcmp(a,b) == 0) 같은 인증은 기계어로 test/cmp 뒤의 조건 점프 한 개로 귀결됩니다. 짧은 점프(short jump)의 기계어는:

  • 0x74 = JE(같으면 점프)
  • 0x75 = JNE(다르면 점프)

뒤따르는 한 바이트는 점프 거리입니다. 즉 인증 로직의 운명이 한 바이트에 달렸습니다 — 0x75를 0x74로 바꾸면 "다를 때 통과"가 "같을 때 통과"로 뒤집힙니다. 이것이 패치의 본질입니다.

2-3. 패치의 두 단계 — 메모리와 파일

x64dbg에서 명령을 더블클릭해 고치면 일단 메모리상의 프로그램만 바뀝니다. 지금 실행에서 즉시 효과를 보지만, 파일은 그대로입니다. 바꾼 내용을 파일에 반영하려면 Ctrl+P(Patches 창)에서 Patch file을 해야 합니다.

파일에 쓰려면 한 가지 변환이 필요합니다. 디버거 화면의 주소는 가상 주소(실행 중 메모리 위치)이고, 패치는 파일 오프셋(디스크에서의 위치)에 써야 하니까요. 둘의 변환은 섹션 표가 알려 줍니다 — 오늘 파이썬 실측에서 이 변환을 손으로 해 봅니다.

2-4. 문자열에서 역추적 — x64dbg의 표준 오프닝

crackme를 받으면 x64dbg에서의 첫 동작은 정해져 있습니다. 우클릭 → Search for → Current region → String references(또는 All referenced strings)로 문자열 목록을 열고, "Correct"/"Wrong" 같은 판정 메시지를 찾아 더블클릭합니다. 그러면 그 문자열을 참조하는 코드 — 즉 인증 분기 근처 — 로 점프합니다. 메시지에서 거꾸로 비교 코드를 찾아가는, 사실상 모든 입문 crackme 공략의 첫 수입니다.

2-5. 조건부 브레이크포인트

반복문 안의 비교처럼 "수백 번 지나가는데 그중 한 번만 보고 싶은" 상황에는 조건부 BP를 씁니다. BP에 우클릭 → Edit에서 조건식(예: rax==0x1337)을 달면 조건이 참일 때만 멈춥니다. Step 218의 다단계 검증 분석에서 빛을 발하는 기능입니다.


3. 따라 하기

3-1. 실험실 — 패치당할 프로그램

x64dbg의 무대는 Windows지만, 패치의 원리는 운영체제와 무관하게 같습니다. 리눅스 바이너리로 원리를 실측하고, 화면 조작은 예시로 배웁니다.

입력 (patchme.c)

#include <stdio.h>
#include <string.h>

int main(void) {
    char pw[32];
    printf("password: ");
    scanf("%31s", pw);
    if (strcmp(pw, "open-sesame") == 0)
        puts("correct!");
    else
        puts("wrong.");
    return 0;
}
cd ~/lab214_218
gcc -O0 -no-pie -o patchme patchme.c
echo wrongpw | ./patchme
password: wrong.

(2026-09-09 실측. -O0로 컴파일해 분기 구조를 교과서적으로 남겼습니다.)

3-2. 수술 부위 찾기 — 인증 분기의 주소

objdump -d patchme | sed -n "/<main>:/,/ret/p" | grep -E "call|test|jne|j"
  401211:	e8 9a fe ff ff       	call   4010b0 <strcmp@plt>
  401216:	85 c0                	test   %eax,%eax
  401218:	75 11                	jne    40122b <main+0x75>
  401224:	e8 57 fe ff ff       	call   401080 <puts@plt>      ← correct! 출력
  401229:	eb 0f                	jmp    40123a <main+0x84>
  401235:	e8 46 fe ff ff       	call   401080 <puts@plt>      ← wrong. 출력

(2026-09-09 실측. 출력은 일부만 골라 배치했습니다.)

출력 읽는 법: strcmp의 반환값(eax)을 test하고, 다르면(jne) 0x40122b — wrong 쪽 — 으로 점프합니다. 수술 부위는 40121875 11 두 바이트. 첫 바이트 0x75가 JNE라는 뜻이고 0x11이 점프 거리입니다. 이 한 바이트를 0x74(JE)로 바꾸면 인증이 뒤집힙니다.

3-3. 가상 주소 → 파일 오프셋 변환

디버거가 보여 주는 0x401218은 가상 주소입니다. 파일에서의 위치는 섹션 표로 변환합니다:

readelf -S patchme | grep -A1 "\.text"
  [15] .text  PROGBITS  00000000004010d0  000010d0
       0000000000000185 ...

(2026-09-09 실측.)

읽는 법: .text 섹션은 가상 주소 0x4010d0에서 시작하고, 파일 오프셋 0x10d0에서 시작합니다. 차이가 0x400000 — 즉 이 프로그램은 "가상 주소 = 파일 오프셋 + 0x400000". 수술 부위의 파일 오프셋은 0x401218 − 0x400000 = 0x1218입니다. x64dbg의 Patches 창이 자동으로 해 주는 계산을, 우리는 손으로 한 것입니다.

3-4. 파이썬으로 한 바이트 수술 (실측)

패치 스크립트를 만듭니다. 파일을 통째로 읽고, 한 바이트를 고치고, 새 파일로 저장합니다:

입력 (tmp_test/patch216.py)

# ELF 바이너리의 조건 분기 한 바이트 패치
UNC = r"\\wsl$\Codex-Security-Lab\root\lab214_218\patchme"   # 실습 랩 경로
DST = r"\\wsl$\Codex-Security-Lab\root\lab214_218\patchme_patched"

JNE_VADDR = 0x401218          # objdump에서 찾은 jne의 가상 주소
TEXT_VADDR = 0x4010D0         # .text 가상 주소
TEXT_OFF   = 0x10D0           # .text 파일 오프셋
file_off = JNE_VADDR - (TEXT_VADDR - TEXT_OFF)
print(f"가상주소 0x{JNE_VADDR:x} -> 파일 오프셋 0x{file_off:x}")

data = bytearray(open(UNC, "rb").read())
print(f"패치 전 바이트: 0x{data[file_off]:02x} (0x75=JNE 여야 함)")
assert data[file_off] == 0x75, "예상한 JNE 바이트가 아님"

data[file_off] = 0x74         # JNE -> JE
open(DST, "wb").write(data)
print(f"패치 후 바이트: 0x{data[file_off]:02x} (0x74=JE)")

실행 (Git Bash에서):

가상주소 0x401218 -> 파일 오프셋 0x1218
패치 전 바이트: 0x75 (0x75=JNE 여야 함)
패치 후 바이트: 0x74 (0x74=JE)

(2026-09-09 실측.)

3-5. 수술 결과 검증 — 인증이 뒤집혔다

chmod +x patchme_patched
echo wrongpw | ./patchme            # 원본 + 오답
echo open-sesame | ./patchme        # 원본 + 정답
echo wrongpw | ./patchme_patched    # 패치본 + 오답
echo open-sesame | ./patchme_patched # 패치본 + 정답
password: wrong.      ← 원본: 오답은 거부
password: correct!    ← 원본: 정답만 통과
password: correct!    ← 패치본: 오답이 통과!
password: wrong.      ← 패치본: 정답이 거부됨

(2026-09-09 실측.)

읽는 법: 한 바이트(0x75→0x74)가 인증 로직 전체를 정확히 뒤집었습니다. 오답은 통과하고 정답은 거부됩니다. 이것이 "패치"의 실체 — 디버거의 화려한 UI 아래에서 일어나는 일은 이것이 전부입니다.

3-6. 같은 일을 x64dbg로 — 화면 예시

Windows에서 x64dbg로 같은 수술을 하는 절차입니다:

# 화면 예시 — x64dbg 패치 절차
1. crackme.exe를 x64dbg에 드래그해 로드
2. CPU 창에서 우클릭 → Search for → Current region → String references
3. "wrong" / "correct" 문자열을 찾아 더블클릭 → 참조 코드(인증 분기)로 이동
4. 분기 근처의 jne 명령에서 F2로 BP → F9로 실행 → 아무 비밀번호 입력 → BP에서 멈춤
5. jne 명령을 더블클릭 → "jne"를 "je"로 고쳐 확인
   → 이 시점에 메모리상 프로그램이 바뀜. F9로 계속 실행하면 오답인데 통과
6. Ctrl+P (Patches 창) → Patch file → 바뀐 내용이 파일에 저장됨

3-4의 파이썬 스크립트가 한 일(주소 변환 → 바이트 확인 → 한 바이트 교체 → 파일 저장)을 x64dbg는 마우스 다섯 번으로 해 줍니다. 도구가 달라도 수술의 해부학은 같습니다.

3-7. 메모리 브레이크포인트 — gdb에서 원리 실측

"내 입력을 누가 읽는가"를 리눅스에서 재현합니다. gdb의 watch는 값이 쓰일 때 멈춥니다:

(gdb) break main
(gdb) run
(gdb) watch *(char*)($rbp-0x40)      ← 입력 버퍼 pw에 감시
(gdb) continue
Watchpoint 2: *(char*)($rbp-0x40)
Old value = 0 '\000'
New value = 2 '\002'
0x0000000000401059 in ?? ()

(2026-09-09 실측. echo AAAA | gdb -batch ...로 실행. 멈춘 곳은 scanf 내부 — 버퍼에 입력을 쓰는 순간입니다.)

읽는 법: 코드 주소를 몰라도, 데이터에 감시를 걸면 그 데이터를 건드리는 코드에서 멈춥니다. x64dbg의 Memory BP(on access)와 같은 원리입니다. 다만 이 WSL 환경에서는 읽기 감시 rwatchExpression cannot be implemented with read/access watchpoint. 오류로 거부됐습니다 (2026-09-09 실측) — 가상화 환경에서 하드웨어 읽기 감시가 지원되지 않는 제약입니다. 벽 3에서 다룹니다.


4. 미션과 연습문제

미션 — "수술 없는 우회"와 "수술 있는 우회" 비교 보고서

  1. patchme를 gdb로 열어, 파일을 고치지 않고 실행 중에만 인증을 우회해 보세요 (힌트: jne에서 멈춘 뒤 레지스터나 rip를 손대는 방법이 있습니다 — set $rip = ...)
  2. 3-4의 패치로 만든 patchme_patched와 비교합니다: 두 우회의 차이(영속성, 필요한 도구)를 표로 정리하세요
  3. patchme.c의 비밀번호를 바꿔 다시 컴파일하고, objdump부터 패치까지 전 과정을 혼자 반복하세요 — 새 바이너리에서 jne의 주소와 파일 오프셋이 달라짐을 확인하세요

연습문제

문제 1. 코드 브레이크포인트와 메모리 브레이크포인트의 차이를, "무엇을 알고 있을 때 무엇을 쓰는가"로 설명하세요.

문제 2. 0x75(JNE)를 0x90×2(NOP 두 개)로 고치는 패치도 가능합니다. 이 패치는 JE로 뒤집는 것과 결과가 어떻게 다른가요? (힌트: NOP이면 흐름이 어디로 가나요?)

문제 3. 가상 주소 0x401218의 명령을 파일에서 고치려면 왜 0x400000을 뺐나요? .text 섹션 표의 두 숫자가 무엇을 뜻하는지와 함께 설명하세요.

문제 4. 패치가 "메모리상 즉시 반영, 파일은 별도 저장"인 이유를, 디버거가 보는 것과 디스크의 파일이 다른 존재라는 관점에서 설명하세요.


5. 모범 답안과 완료 기준

미션 모범 답안

실행 중 우회 (메모리상 패치의 수동 버전):

(gdb) break *0x401218        ← jne에 BP
(gdb) run                    ← 아무 비밀번호 입력
(gdb) set $rip = 0x401224    ← correct! 쪽 call로 강제 점프
(gdb) continue
correct!

(출력 예시 — 절차는 2026-09-09 실측과 동일하게 동작합니다. 주소는 환경에 따라 다릅니다.)

비교 표:

구분 실행 중 우회 (gdb set) 파일 패치 (바이트 교체)
영속성 그 실행에서만 파일을 바꾸니 영구적
필요한 도구 디버거 아무 바이너리 에디터/스크립트
탐지 세션마다 다시 해야 함 파일 해시가 바뀜 — 무결성 검사에 걸릴 수 있음
용도 분석 중 시험 분석 확정 후 결과물 고정

재수술 확인 사항: 비밀번호 문자열을 바꾸면 바이너리가 달라지므로 jne의 주소·파일 오프셋이 바뀝니다. 절차(objdump로 분기 찾기 → readelf로 변환 → 한 바이트 교체 → 검증)는 동일하게 작동해야 합니다.

연습문제 해답

문제 1 해답. 코드 BP는 "멈추고 싶은 명령의 주소를 아는" 경우에 씁니다 — 문자열 역추적으로 비교 코드를 찾은 뒤처럼. 메모리 BP는 반대로 "코드는 모르지만 데이터는 아는" 경우에 씁니다 — 내 입력 버퍼에 걸어 두면 그것을 읽는 (어디에 있는지 몰랐던) 검사 코드에서 멈춥니다. 정리하면: 아는 것이 주소면 코드 BP, 아는 것이 데이터면 메모리 BP.

문제 2 해답. JE로 뒤집으면 "정답이 거부되고 오답이 통과"로 반전됩니다. 반면 NOP으로 지우면 점프 자체가 사라져 test 결과와 무관하게 항상 다음 명령(correct! 출력)으로 직진합니다 — 정답이든 오답이든 무조건 통과합니다. 실전 crackme 풀이에서는 "무조건 통과" 쪽이 더 편한 경우가 많지만, 프로그램에 정답 경로에서만 채워지는 상태가 있다면 반전 패치가 필요한 경우도 있습니다.

문제 3 해답. 실행 파일은 메모리에 올라갈 때 각 섹션이 정해진 가상 주소에 배치됩니다. readelf 출력에서 0x4010d0은 .text가 메모리에서 시작되는 가상 주소이고, 0x10d0은 같은 내용이 파일에서 시작되는 오프셋입니다. 둘의 차(0x400000)만큼 밀려 있으므로, 가상 주소에서 이 차이를 빼면 파일 안의 위치가 나옵니다. 디버거 화면의 주소로는 디스크의 파일을 직접 가리킬 수 없기 때문에 이 변환이 필요합니다.

문제 4 해답. 실행 중인 프로그램은 파일의 사본이 메모리에 올라간 것이고, 디버거가 멈춰서 보여 주고 고치는 것은 그 사본입니다. 사본을 고쳐도 원본 파일은 그대로이므로, 프로그램을 닫으면 수정은 사라집니다. 파일을 바꾸려면 "메모리의 어느 위치가 파일의 어느 바이트에 대응하는가"를 계산해 원본에 다시 써야 하고, 그것이 x64dbg의 Patches 창(Ctrl+P)과 우리의 파이썬 스크립트가 한 일입니다.

완료 기준 체크리스트

  • [ ] 코드 BP와 메모리 BP의 차이와 선택 기준을 말할 수 있다
  • [ ] 문자열 참조 검색으로 인증 분기를 역추적하는 절차를 안다
  • [ ] objdump 출력에서 조건 점프의 기계어 바이트(0x74/0x75)를 식별했다
  • [ ] 가상 주소 → 파일 오프셋 변환을 readelf 섹션 표로 계산했다
  • [ ] 파이썬으로 한 바이트를 패치해 인증이 뒤집히는 것을 확인했다
  • [ ] gdb watch로 데이터 감시의 원리를 실측했다
  • [ ] 미션: 실행 중 우회와 파일 패치를 비교하는 표를 완성했다

6. 흔한 실수와 해결

벽 1. 패치했는데 파일이 안 바뀐다 (x64dbg)

증상: je로 고치고 실행하면 통과되는데, 프로그램을 다시 열면 원래대로입니다.
원인: 명령을 더블클릭해 고친 것은 메모리상 수정입니다. 파일은 건드리지 않았습니다.
해결: Ctrl+P로 Patches 창을 열어 Patch file을 눌러야 파일에 저장됩니다. Patches 창에는 이번 세션에 고친 모든 위치가 목록으로 뜨니, 저장 전에 의도한 패치만 있는지 확인하세요.

벽 2. 패치할 바이트를 찾았는데 0x75가 아니다

증상: 파이썬 스크립트의 assert data[file_off] == 0x75에서 실패합니다.
원인: 주소 변환이 틀렸거나, 컴파일 옵션이 달라 분기 형태가 바뀐 것입니다. -O1 이상으로 컴파일하면 컴파일러가 분기를 재배치해 je+점프 조합이 되는 등 모습이 달라집니다 (오늘 실습이 -O0인 이유).
해결: objdump에서 분기 명령의 주소와 기계어를 다시 확인하세요. readelf의 섹션 표에서 .text의 두 숫자(가상 주소, 파일 오프셋)로 변환을 다시 계산하면 됩니다. 추측로 패치하지 말고, 패치 전 바이트가 기대와 같은지 반드시 검사하세요 — 오늘 스크립트의 assert가 그 안전장치입니다.

벽 3. gdb에서 rwatch가 안 된다

증상: rwatch *(char*)($rbp-0x40)Expression cannot be implemented with read/access watchpoint. 오류가 납니다.
원인: 읽기 감시는 CPU의 하드웨어 디버그 레지스터가 필요한데, WSL 같은 가상화 환경에서는 지원되지 않는 경우가 있습니다 (2026-09-09 WSL2 실측).
해결: 쓰기 감시 watch는 동작하니(실측 확인) 우선 그것으로 관찰하세요. "읽는 순간"을 꼭 봐야 한다면, 비교 함수(strcmp 등)의 호출 주소에 코드 BP를 거는 방법으로 우회합니다 — Step 178에서 한 것이 바로 그 우회입니다.

벽 4. x64dbg에서 문자열 검색에 "correct"가 안 나온다

증상: String references에 판정 메시지가 없습니다.
원인: 문자열이 다른 메모리 영역에 있거나(Current region만 검색했을 때), 난독화로 실행 중에 복원되는 경우입니다.
해결: Search for → All regions로 범위를 넓혀 보세요. 그래도 없으면 실행 중 복원형 — Step 178의 XOR crackme처럼 동적 분석으로 전환할 신호입니다. 이때 메모리 BP가 빛을 발합니다: 입력 버퍼에 on access BP를 걸어 검사 코드를 역으로 찾습니다.

벽 5. 패치 후 실행하니 세그폴트/비정상 종료

증상: 한 바이트를 고쳤는데 프로그램이 죽습니다.
원인: 명령의 첫 바이트가 아닌 곳을 고쳤거나(명령 중간을 깨면 CPU가 엉뚱한 명령으로 해석), 점프 거리 바이트를 건드린 경우입니다. x86 명령은 가변 길이라 한 바이트 어긋나면 그 뒤 전체가 깨집니다.
해결: 패치는 반드시 명령의 시작 바이트에서, 같은 길이의 명령으로만 교체하세요 (0x75→0x74는 둘 다 2바이트 short jump라 안전). NOP으로 지울 때도 바이트 수를 정확히 맞춰야 합니다 — 2바이트 명령에는 0x90 두 개.


7. 정리

오늘의 개념

개념 한 줄 설명
메모리 브레이크포인트 코드 주소가 아니라 데이터의 읽기/쓰기에 거는 감시 — 검사 코드를 역추적
조건부 BP 조건식이 참일 때만 멈추는 BP — 반복문 분석의 필수품
패치 프로그램의 명령 바이트를 바꿔 동작을 변경하는 기술
0x74 / 0x75 JE / JNE short jump — 인증의 운명을 쥔 한 바이트
가상 주소 ↔ 파일 오프셋 섹션 표로 변환하는 두 좌표계 — 파일 패치의 핵심 계산
Patches 창 (Ctrl+P) 메모리상 수정을 파일에 반영하는 x64dbg의 저장소

오늘의 명령어·키

명령/키 하는 일
x64dbg: 우클릭 → Search for → String references 판정 메시지에서 비교 코드로 역추적
x64dbg: 덤프 우클릭 → Breakpoint → Memory, on access 메모리 BP 걸기
x64dbg: 명령 더블클릭 → 수정 / Ctrl+P 메모리 패치 / 파일에 저장
objdump -d | grep jne 패치 후보(조건 점프) 찾기
readelf -S 가상 주소 → 파일 오프셋 변환 재료
gdb watch / rwatch 쓰기 / 읽기 데이터 감시 (rwatch는 환경 제약 주의)

명령어보다 중요한 감각

오늘 배운 두 기술은 같은 질문의 양면입니다. 메모리 BP는 "이 값을 누가 만지는가"를 묻고, 패치는 "만지는 곳을 알았으니 바꾼다"고 답합니다. 분석(관찰)과 조작(개입) — 동적 리버싱의 바퀴 두 개입니다.

그리고 패치의 겸손함을 기억하세요. 수술은 정확히 한 바이트, 명령의 경계를 지켜서. 한 바이트가 프로그램의 정체성을 뒤집을 수 있다는 것은, 반대로 한 바이트의 오차가 전부를 깬다는 뜻이기도 합니다. 패치 전에 기대 바이트를 확인하는 assert 한 줄 — 그것이 분석가와 파괴자를 가르는 습관입니다.


전부 체크되면 Step 216 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.