gcc 컴파일
GCC (GNU Compiler Collection)
- GNU 프로젝트의 일환으로 개발된 컴파일러
- C, C++, Java, Objective-C, Fortran, Ada, Pascal 등 언어 지원
기본 컴파일 방법
- gcc <컴파일 할 파일명> 명령어 입력 시, 해당 파일을 컴파일
메모리 보호 기법 설정 옵션
- gcc 명령어를 사용해 컴파일 메모리 보호 기법 설정하거나 해제할 수 있는 옵션이 존재
- NX 해제 : gcc -z execstack
- SSP 해제 : gcc -fno-stack-protector
- PIE 해제 : gcc -no-pie
- RELRO 해제 : gcc -z norelro
gcc 컴파일
- gcc를 사용해 C로 작성한 소스코드를 실행 가능한 바이너리 파일로 생성하는 과정

디버깅 Tool
Linux
GDB (GNU Debugger)
- 리눅스에서 기본적으로 제공하는 디버깅 도구
- gdb <실행 파일 명> 입력 시, 해당 파일을 gdb로 디버깅 가능
PEDA (Python Exploit Development Assistance for GDB)
- binary 분석 및 Exploit을 도와주는 도구로 gdb의 발전된 버전
- 여러 디버거들 중에서 비교적 오래된 도구
- heap 영역을 보기에 까다로운 부분이 있어 Peda-heap 플러그인을 사용해 기능을 추가하기도 함
- 동적 분석 시 레지스터 스택 코드 부분을 보기 쉽게 출력
- 설치 방법 : git clone https://github.com/logld/peda.git
- echo “source ~/peda/peda.py” >> ~/.gdbinit
GEF (GDB Enhanced Features)
- Peda와 같이 gdb에 추가적인 기능들을 실행할 수 있도록 하는 툴
- gdb에서 지원하는 모든 아키텍처(x86, arm, mips, powerpc, sparc 등)를 지원함
- GEF 디버거의 사용법을 설명해주는 자료가 많음
- 설치 방법 : git clone https://github.com/hugsy/gef.git
Pwndbg
- heap 관련 문제를 풀 때 유용함
- 레지스터, 스택, 실행 중인 코드의 정보에 대한 가독성이 좋음
- 실제 레지스터와 메모리 구조가 한눈에 보기 쉬워 프로그램을 따라가며 이해하기 편리함
- 메모리를 관리하는 명령어가 만들어져 있음
- 설치 방법 : git clone https://github.com/pwndbg/pwndbg/archive/refs/tags/2021.06.22.tar.gz
- cd pwndbg
- ./setup.sh
.gdbinit
- gdb가 실행될 때 로딩하는 환경설정 파일로 보통 /etc/gdb/gdbinit에 위치
- 파일이 존재하지 않는다면, 직접 ~/.gdbinit을 생성해도 무방
- gdb 명령어 하나에 다양한 디버거들로 전환하며 사용할 수 있음
기본 디버거 설정
- 디버거 설치 후 .gdbinit 파일이 존재하는 위치로 이동한 뒤, sudo vim .gdbinit으로 파일 열기
- 사용자가 기본 설정하려는 디버거의 .py의 위치를 확인하고, “source 경로/.py”를 입력 후 저장
- .gdbinit에 기본 디버거 설정 값을 입력한 뒤 사용할 디버거 외 다른 설정은 주석(#) 처리
.gdbinit 설정 변경 후
- gdb <실행 파일 명> 명령어 입력 시, 해당 파일을 pwndbg로 디버깅 가능
Windows
IDA (Interactive Disassembler)
- Hex-Rays에서 직접 개발한 컴퓨터 소프트웨어용 디스어셈블러
- IDA Pro 개꿀
Ghidra
- NSA에서 제작한 리버스 엔지니어링 툴로 오픈소스로 공개함
- 디컴파일, 디스어셈블리, 프로젝트 공유, 디버깅 등 기능 존재
Ollydbg
- 용량이 가볍고 빠르며, 다양한 기능이 있어 사용이 편리함
- 초보자도 쉽게 배울 수 있음
Windbg
- Microsoft에서 직접 만든 디버거로 커널 드라이버 개발과 유지보수에 사용됨
- 유저 모드 디버깅보다 커널 모드 디버깅에 유용함
Exploit Tool
Pwntools
- 리눅스 환경에서 exploit을 쉽게 할 수 있게 하는 기능들이 있는 Python Module
- 설치 후 다른 python 모듈처럼 import 해서 사용 (from pwn import *)

Pwntools 설치 방법
sudo apt-get update sudo apt-get install python3 python3-pip python3-dev git libssl-dev libffi-deb build-essential python3 -m pip install --upgrade pip python3 -m pip install --upgrade pwntools
Pwntools 명령어
- 접속 함수 : 사용자의 로컬 바이너리 파일이나 원격 환경에 접속하기 위해 사용하는 함수
- process : 사용자의 로컬 바이너리 파일을 실행할 때 사용
- remote : 원격 서버를 대상으로 접속할 때 사용
- ssh : 원격 서버 대상을 ssh로 접속할 때 사용
- 페이로드 전송 함수 : 작성한 페이로드를 접속한 환경의 바이너리 파일에 전송하기 위해 사용하는 함수
- send : 파일에 문자열 ‘str1’ 전송
- sendline : 파일에 문자열 ‘str1\n’ 전송
- sendafter : 파일이 ‘str0’ 출력 시, 문자열 ‘srt1’ 전송
- sendlineafter : 파일이 ‘str0’ 출력 시, 문자열 ‘str1\n’ 전송
- 출력 데이터 저장 함수 : 접속한 환경의 바이너리 파일에서 출력하는 데이터를 받아오기 위해 사용하는 함수
- recv : 출력하는 데이터 중 최대 (int)바이트 데이터 받기
- recvline : 출력하는 데이터 중 개행 문자가 나올 때까지 받기
- recvn : 출력하는 데이터 중 정확하게 바이트만 받기
- recvuntil : 출력하는 데이터 중 문자열 ‘str0’ 뒤 부터 받기
- recvall : 연결이 끊어지거나 프로세스가 종료될 때까지 받기
- 패킹 / 언패킹 함수 : 패킹함수는 정수를 인자로 받아 패킹한 후 문자열 형태로 리턴
- 언패킹 함수는 문자열을 인자로 받아 언패킹한 후 정수 형태로 리턴
- 디버깅 함수 : exploit을 진행하는 중에 gdb를 실행하고자 할 때 사용하는 함수
- 쉘 함수 : 쉘을 획득하고자 하거나, 쉘을 획득한 후에 사용하는 함수
Shellcode 작성
Shellcode
- 시스템에서 특정 명령을 실행할 수 있도록 내부에 삽입하는 OP Code 조각
- 일반적으로 명령 Shell을 실행시켜 대상 PC의 시스템을 제어하기 위해서 사용
Pwnable 시 Shellcode 작성 순서
- 문제가 요구하는 쉘 코드의 조건 / 기준 분석
- C 기반 쉘 실행 코드 작성
- Disassembling
- system call에 필요한 부분만 어셈블리어로 구현 후 컴파일
- 컴파일 후 objdump로 OP Code 출력
- Null Byte(0x00) 제거
- 최종 OP Code를 하나의 문자열로 연결
NASM (Netwide Assembler)
- 인텔 x86 아키텍처용 어셈블러이자 역어셈블러
- 어셈블리 파일(.s)을 컴파일하는 프로그램
어셈블리 파일 컴파일 명령어
- nasm -f [출력 파일 포맷] -o [오브젝트 파일] [어셈블리 파일]
설치 방법
- sudo apt-get install nasm
objdump
- 바이너리 분석 프로그램으로 라이브러리, 컴파일된 오브젝트 모듈, 공유 오브젝트 파일, 독립 실행파일 등의 바이너리 파일들의 정보를 볼 때 쓰임
- objdump로 수행 가능한 작업은 크게 바이너리 덤프와 ELF 파일 디스어셈블로 나눌 수 있음
주요 Flag Options
- objdump -f <filename> : 파일 포맷, 아키텍처, 플래그, 시작 주소에 대한 정보 출력
- objdump -d<filename> : 파이르이 실행 영역을 디스어셈블해서 출력
- objdump -D <filename> : 파일의 모든 영역을 디스어셈블해서 출력
- objdump -h <filename> : 섹션 헤더의 정보를 출력
- objdump -t <filename> : 파일의 심볼 테이블을 출력
- C 기반 쉘 실행 코드 작성
- C의 execve 함수를 이용해 쉘을 여는 간단한 실행 코드 작성 후 64비트로 컴파일


2. 디스어셈블링
- 컴파일 후 생성된 바이너리 파일을 디버거로 열고, main 함수 분석


3. system call에 필요한 부분만 어셈블리어로 구현
- 디버거로 분석한 내용을 바탕으로 execve(”/bin/sh”, 0, 0)를 호출할 어셈블리어 코드 작성

4. 컴파일
- 작성한 어셈블리어 코드를 실행 파일로 생성 후 실행

5. objdump로 OP Code 출력
- objdump로 바이너리 파일의 OP Code 출력

6. Null Byte(0x00) 제거
- objdump로 OP Code(기계어) 확인 결과, Null Byte가 존재하는 것을 확인

7. Null Byte(0x00)제거
- string Null Byte를 제거하기 위해서 코듣 변경 ; /bin/sh → /bin/sh

- 변경된 코드로 실행 파일을 생성해, objdump로 확인해 보면 Null Byte가 존재하지 않음

8. 최종 OP Code를 하나의 문자열로 연결
- OP Code를 하나의 문자열로 연결하고, 실행하기 위한 테스트 코드 작성

- 작성한 코드를 컴파일 해 쉘이 열리는 것을 확인

'Pwnable' 카테고리의 다른 글
| Integer Overflow (0) | 2025.11.19 |
|---|---|
| Format String Bug (0) | 2025.11.19 |
| Buffer Overflow (0) | 2025.11.19 |
| Pwnable 방법론 (0) | 2025.11.19 |
| 리눅스 & 어셈블리어 (0) | 2025.11.19 |