← 목록으로

메모리 구조 기초: 스택, 힙, 레지스터부터 이해하기

[Pwn] Pwnable · 작성: 2026-07-19 19:28:48 · 조회 2

Pwnable(바이너리 익스플로잇)을 시작하기 전에 반드시 먼저 알아야 하는 게 프로그램이 메모리를 어떻게 쓰는지다. 이 개념 없이 익스플로잇 코드를 따라 치기만 하면 뭘 하고 있는지 이해가 안 된다. 이 글은 그 전제 지식을 정리한다.

프로세스 메모리 레이아웃

프로그램이 실행되면 메모리는 대략 이렇게 나뉜다 (주소가 낮은 곳 → 높은 곳 순).

낮은 주소
+------------------+
|  Code (Text)     |  실행할 명령어들 — 보통 읽기 전용
+------------------+
|  Data / BSS      |  전역/정적 변수
+------------------+
|  Heap             |  malloc()으로 동적 할당한 메모리, 낮은 주소 → 높은 주소로 자람
|      ↓            |
|      ↑            |
|  Stack            |  함수 호출마다 쌓이는 지역 변수/리턴 주소, 높은 주소 → 낮은 주소로 자람
+------------------+
높은 주소

힙과 스택이 서로를 향해 자라는 구조라서, 옛날 방식으로는 이 둘이 충돌하는 것도 하나의 공격 아이디어였다 (지금은 여러 보호 기법으로 막혀있다).

레지스터 (x86-64 기준)

레지스터는 CPU 안에 있는 아주 빠른 저장 공간이다. 익스플로잇에서 자주 보게 되는 것들만 추려보면:

레지스터 역할
RIP 다음에 실행할 명령어의 주소 (Instruction Pointer). 이걸 공격자가 원하는 값으로 바꾸는 게 대부분의 익스플로잇의 최종 목표다.
RSP 스택의 맨 위(top) 주소 (Stack Pointer)
RBP 현재 함수의 스택 프레임 시작 주소 (Base Pointer)
RDI, RSI, RDX, RCX 함수 호출 시 첫 4개 인자를 순서대로 담는 레지스터 (x86-64 System V 호출 규약)
RAX 함수의 리턴값, 시스템콜 번호

"RDI에 원하는 문자열 주소를 넣고 system 함수를 호출한다" 같은 표현이 이후 글들에 계속 나오는데, 이게 바로 함수 호출 규약(calling convention)을 이용해서 함수에 원하는 인자를 넘겨주는 방법이다.

함수 호출과 스택 프레임

함수가 호출되면 스택에 다음이 순서대로 쌓인다.

[ 지역 변수들 (buf 등) ]   <- 낮은 주소
[ saved RBP ]
[ 리턴 주소 ]              <- 함수가 끝나면 RIP가 여기로 복원됨
[ 인자들 (일부는 레지스터로) ]  <- 높은 주소

함수가 ret 명령어로 끝날 때, CPU는 스택에 저장된 "리턴 주소"를 읽어서 그 주소로 실행 흐름을 옮긴다. 이 리턴 주소가 담긴 스택 영역을 덮어쓸 수 있다면, 함수가 끝나는 순간 실행 흐름을 원하는 곳으로 보낼 수 있다 — 이게 스택 버퍼 오버플로우의 핵심 아이디어이고, 다음 글에서 바로 다룬다.

실습 환경 준비

pip install pwntools
checksec ./binary

다음 글부터는 이 개념들을 바탕으로 실제 취약점 하나씩 들어간다.

Pwnable 카테고리의 글 (1/14)

  1. 메모리 구조 기초: 스택, 힙, 레지스터부터 이해하기
  2. 스택 버퍼 오버플로우: 가장 기본적인 메모리 손상 공격
  3. Format String 취약점: printf가 위험해지는 순간
  4. Stack Canary: 원리와 우회 방법
  5. NX/DEP 우회와 Return-to-libc
  6. ASLR과 정보 유출(Leak)
  7. ROP (Return-Oriented Programming) 완전 정복
  8. 힙 익스플로잇 기초: Use-After-Free와 Double Free
  9. pwntools 실전 활용법
  10. UDF(User-Defined Function) 기반 DB 권한 상승: 원리부터 실전까지
  11. Pass-the-Hash / Pass-the-Ticket: 원리와 실전
  12. eBPF 기반 지속성(Persistence) 기법
  13. Full RELRO 환경에서의 GOT Overwrite와 우회
  14. ret2dlresolve: 심볼 정보 없이 임의 함수 호출하기
스택 버퍼 오버플로우: 가장 기본적인 메모리 손상 공격 →