카테고리 없음

리버싱 입문 Day 10 - CTF 입문 및 BOF 기본 개념 정리

baa1983 2026. 4. 21. 18:09

지금까지 기드라를 이용한 정적 분석을 계속 진행해왔다.

처음에는 어셈블리어를 전부 이해하려 하기보다는
중요해 보이는 흐름 위주로 따라가는 방식으로 접근했는데,
실제로 분석을 계속하다 보니 포인터의 이동이나
메모리에서 값을 넣고 빼는 부분들이 생각보다 쉽게 눈에 들어오지 않았다.

특히 단순한 비교문이 아니라
중간에 값이 변형되는 구조(XOR, ADD 등)가 섞이기 시작하면
흐름을 한 번에 이어서 보는 게 쉽지 않았다.

그래도 기드라 정적 분석 자체는 계속 이어갈 생각이지만,
조금 더 빠르게 CTF에 적응하기 위해서는
기초적인 패턴을 먼저 정리할 필요가 있다고 판단했다.

어차피 실제 문제를 풀게 되면 지금처럼 막히는 구간은 계속 나오겠지만,
직접 문제를 풀고 성취감을 쌓으면서 접근하는 것이
오히려 정적 분석 실력을 더 빠르게 올릴 수 있을 것 같아서
CTF의 리버싱과 시스템 해킹(Pwn) 기본 공부를 시작하게 되었다.


1. CTF 리버싱 기본 구조

지금까지 여러 문제를 풀면서 느낀 건,
대부분의 문자열 검증 문제는 거의 같은 구조를 가진다는 점이다.

입력 → 배열 저장 → 반복문 → 값 변형 → 비교 → 성공/실패
 

겉으로는 복잡해 보이지만,
결국은 이 구조 안에서 움직인다.

핵심은 “무슨 계산을 했는지”보다

어디서 비교하는지, 어떤 형태로 비교하는지

이걸 먼저 찾는 것이다.


2. 역산 (리버싱의 핵심)

리버싱에서 중요한 건 결국 하나다.

조건을 보고 입력값을 역으로 계산하는 것

예를 들어:

 
input[i] + 2 == key[i]
 

이렇게 되어 있으면 그대로 따라가는 게 아니라

 
input[i] = key[i] - 2
 

이렇게 역으로 생각해야 한다.

초반에는 이 부분이 가장 헷갈렸는데,
문제를 반복하다 보니

조건 → 수식 → 역산

이 흐름이 점점 익숙해지는 느낌이다.


3. 자주 나오는 패턴

1) 단순 연산

  • +1 / -1
  • +n / -n

이건 거의 기본이다.


2) XOR

 
input[i] ^ 0x20
 

이건 정말 자주 나온다.

특징은:

  • 대소문자 변환
  • 두 번 XOR 하면 원래 값으로 돌아옴

3) 인덱스(i) 활용

 
input[i] + i
 

단순 연산보다 한 단계 더 어려워지는 구간이다.


4) 결합 연산

 
(input[i] ^ 0x20) + 3
 

여기서 중요한 건 하나다.

역산은 반드시 뒤에서부터 한다


4. Ghidra 기준 해석

실제 분석에서는 변수 이름이 의미 있게 나오지 않는다.

 
param_1 입력
local_9 key
local_10 i
 

이름이 아니라

“이 변수가 어떤 역할을 하는지”

이걸 찾는 게 더 중요하다.


5. 리버싱에서 느낀 점

이번에 여러 문제를 풀면서 느낀 건,

  • 단순 비교보다 “변형 후 비교”가 훨씬 많다
  • 문자열 길이 조건이 같이 붙는 경우가 많다
  • 완벽히 해석하려 하기보다 흐름을 먼저 보는 게 중요하다

아직 부족한 부분이 많지만,
그래도 처음보다 구조가 훨씬 빨리 보이기 시작했다.


6. BOF (Buffer Overflow) 기초

리버싱만 하다가 처음으로 시스템 해킹(Pwn) 개념을 접해봤다.

처음에는 완전히 다른 영역처럼 느껴졌는데,
결국 프로그램 흐름을 이해한다는 점에서는 이어지는 부분도 있었다.


1) 취약점 발생

 
char buf[16];
gets(buf);
 

여기서 문제는 명확하다.

  • buf는 16바이트
  • gets는 길이 제한 없음

→ 입력이 넘치면 메모리를 침범한다


2) 스택 구조

[ return address ]
[ saved rbp ]
[ buffer ]
 

처음에는 return address 바로 위에 buffer가 있다고 생각했는데,
중간에 saved rbp가 하나 더 끼어 있다는 걸 이해하는 게 중요했다.


3) 핵심 개념

64비트 기준으로 정리하면:

  • overflow 시작 = buf + 1
  • return address 도달 = buf + 8
  • 전체 payload = buf + 16

여기서 계속 나오는 +8이 바로 saved rbp 때문이다.


4) payload

처음에는 payload를 그냥 “주소”라고 생각했는데,
정확히는

공격을 위해 넣는 전체 입력 데이터

이다.

구조는 단순하다.

[ padding ] + [ return address ]
 

5) 리틀엔디언

이 부분도 처음에는 꽤 헷갈렸다.

주소를 그대로 넣는 게 아니라

거꾸로 넣어야 한다

예:

0x4011a6 → a6 11 40 00 00 00 00 00
 

6) BOF 흐름 정리

지금까지 이해한 걸 한 줄로 정리하면:

버퍼를 넘쳐서 return address까지 도달한 뒤, 그 값을 원하는 함수 주소로 바꿔서 실행 흐름을 변경한다


7. 전체 정리

이번에 공부하면서 느낀 건,

리버싱과 시스템 해킹은 완전히 다른 영역이라기보다는

  • 리버싱 → 프로그램을 이해
  • Pwn → 프로그램을 조작

이렇게 이어지는 느낌이었다.


현재 상태

  • 문자열 기반 리버싱 가능
  • 간단한 BOF 공격 이해
  • payload 구조 이해

아직 완전히 자유롭게 다루는 수준은 아니지만,
기본적인 틀은 잡힌 상태라고 생각한다.


앞으로

  • CTF 문제 반복 풀이
  • 실제 바이너리 분석
  • Pwn 심화 (보호기법 등)

이쪽으로 계속 진행할 예정


마무리

아직은 코드가 완전히 읽히는 단계는 아니지만,
그래도 흐름이 조금씩 눈에 들어오기 시작했다.

다음에는 실제 문제를 조금 더 많이 풀어보면서
지금 정리한 개념들이 얼마나 적용되는지 확인해볼 생각이다.