본문 바로가기

3. Pwnable (포너블)

[2024.09.28]PWN PWN 해 2주차 활동

2주차는 드림핵의 System Hacking 로드맵에서 System Hacking Introduction, Background - Computer Science, Tool Installation 강의를 공부했다.

https://dreamhack.io/lecture/roadmaps/2

 

System Hacking

시스템 해킹을 공부하기 위한 로드맵입니다.

dreamhack.io

 


 

Background: Computer Architecture

컴퓨터 구조

컴퓨터가 효율적으로 작동할 수 있도록 하드웨어 및 소프트웨어의 기능을 고안하고, 이들을 구성하는 방법

대표적으로 폰 노이만 구조, 하버드 구조, 수정된 하버드 구조가 있음

명령어 집합구조: CPU의 명령어에 대한 설계

마이크로 아키텍처: CPU의 하드웨어적 설계

 

폰 노이만 구조

연산, 제어, 저장의 세 가지 핵심 기능 (중앙처리장치, 기억장치, 버스)

중앙처리장치: 연산 처리, 시스템 관리하는 컴퓨터의 두뇌역할. 산술/논리 연산을 처리하는 산술논리장치(ALU) CPU를 제어하는 제어장치, CPU에 필요한 데이터를 저장하는 레지스터 등으로 구성.

기억장치: 주기억장치와 보조기억장치로 분류. 실행과정에서 임시로 저장하는 주기억장치 대표적으로 램(RAM). 장기간 보관하는 보조기억장치 대표적으로 하드 드라이브(HDD), SSD.

버스: 신호를 전송하는 통로. 데이터가 이동하는 데이터 버스, 주소를 지정하는 주소 버스, 읽기/쓰기를 제어하는 제어 버스.

 

명령어 집합 구조(ISA)

CPU가 해석하는 명령어의 집합. IA-32, x86-64(x64), MIPS, AVR 등 다양하게 존재하지만 x86기반 CPU의 점유율이 압도적이기 때문에 가장 범용적인 x64아키텍처를 대상.

 

x86-64(amd64) 아키텍처

n 비트 아키텍처: 여기서 nCPU가 한번에 처리할 수 있는 데이터의 크기. CPU가 이해할 수 있는 데이터의 단위라는 의미에서 WORD라고 부름.

 

레지스터

 - 범용 레지스터: 주용도는 있으나, 그 외의 다양한 용도로 사용될 수 있는 레지스터. 8바이트를 저장, 부호 없는 정수를 기준으로 2641까지의 수를 나타낼 수 있음.

 

 - 세그먼트 레지스터: cs, ss, ds, es, fs, gs 6가지, 크기는 16비트. cs, ds, ss는 코드 영역과 데이터, 스택 메모리 영역을 가리킬 때 사용, 나머지 레지스터는 운영체제 별로 용도를 결정할 수 있도록 범용적인 용도.

 명령어 포인터 레지스터: CPU가 어느 부분의 코드를 실행할지 가리키는 역할. 명령어 레지스터는 rip이며, 크기는 8바이트.

 

 - 플래그 레지스터: 프로세서의 현재 상태를 저장하고 있는 레지스터.

 

※ 레지스터 호환: 32비트, 16 비트에서 호환성을 위해 비트를 나누어 사용가능.

 

 

Background: Linux Memory Layout

<메모리 오염: 조작된 메모리 값에 의해 CPU도 잘못된 동작을 하게 됨.>

 

세그먼트

코드 세그먼트, 데이터 세그먼트, BSS 세그먼트, 힙 세그먼트, 스택 세그먼트로 구분.

 

코드 세그먼트(텍스트 세그먼트)

실행 가능한 기계 코드가 위치하는 영역, 읽기권한과 실행권한이 부여.

데이터 세그먼트

컴파일 시점에 값이 정해진 전역변수 및 전역상수들이 위치, 읽기권한 부여.

data 세그먼트: 값이 변할 수 있는 데이터 ex) 전역 변수

rodata(read-only data) 세그먼트: 값이 변하면 안되는 데이터 ex) 전역으로 선언된 상수

BSS 세그먼트

컴파일 시점에 값이 정해지지 않은 전역 변수가 위치, 선언만 하고 초기화하지 않은 전역변수 등이 포함. 프로그램이 시작될 때, 모두 0으로 값이 초기화, 읽기 권한 및 쓰기 권한이 부여.

스택 세그먼트

프로세스의 스택이 위치, 함수의 인자나 지역 변수와 같은 임시 변수들이 실행중에 여기에 저장. 스택 프레임(Stack Frame) 이라는 단위로 사용.

프로세스를 시작할 때 작은 크기의 스택 세그먼트를 먼저 할당해주고, 부족해질 때마다 이를 기존 주소보다 낮은 주소로 확장, 읽기 와 쓰기 권한 부여.

힙 세그먼트

힙 데이터가 위치, 스택 세그먼트와 반대 방향으로 자람. 읽기와 쓰기 권한이 부여.

 

x86 Assembly: Essential Part(1)

 

x64 어셈블리 언어

동사에 해당하는 명령어와 목적어에 해당하는 피연산자로 구성.

명령어

 

피연산자

상수, 레지스터, 메모리의 3가지 종류

메모리 []으로 둘러싸인 것으로 표현, 앞에 크기 지정자(Size Directive) TYPE PTR이 추가될 수 있음. BYTE, WORD, DWORD, QWORD가 올 수 있으며, 각각 1바이트, 2바이트, 4바이트, 8바이트의 크기를 지정.

 

데이터 이동

어떤 값을 레지스터나 메모리에 옮기도록 지시

mov dst, src : src에 들어있는 값을 dst에 대입.

lea dst, src : src의 유효 주소(EA) dst에 저장.

 

산술 연산

add dst, src : dst src의 값을 더함.

sub dst, src: dst에서 src의 값을 뺌.

inc op: op의 값을 1 증가.

dec op: op의 값을 1 감소.

 

논리 연산(비트 연산)

and dst, src: dst src의 비트가 모두 1이면 1, 아니면 0

or dst, src: dst src의 비트 중 하나라도 1이면 1, 아니면 0

xor dst, src: dst src의 비트가 서로 다르면 1, 같으면 0

not op: op의 비트 전부 반전

 

비교 (두 피연산자의 값을 비교하고, 플래그를 설정)

cmp op1, op2: op1 op2를 비교

test op1, op2: op1 op2를 비교 (AND 비트연산을 취함. 결과를 op1에 대입하지 않음.)

분기

jmp addr: addr rip를 이동. (rip: 동적 라우팅 알고리즘)

je addr: 직전에 비교한 두 피연산자가 같으면 점프 (jump if equal)

jg addr: 직전에 비교한 두 연산자 중 전자가 더 크면 점프 (jump if greater)

 

x86 Assembly: Essential Part(2)

스택

push val : val을 스택 최상단에 쌓음

pop reg : 스택 최상단의 값을 꺼내서 reg에 대입

 

프로시저

특정 기능을 수행하는 코드 조각, 부르는 행위를 호출(Call), 돌아오는 것을 반환(Return).

call 다음의 명령어 주소(Return Address, 반환 주소)를 스택에 저장하고 프로시저로 rip를 이동.

call addr : addr에 위치한 프로시져 호출.

leave: 스택프레임 정리.

ret : return address로 반환.

 

시스템 콜

커널 모드: 전체 시스템을 제어하기 위해 시스템 소프트웨어에 부여하는 권한.

유저 모드: 사용자에게 부여하는 권한.

시스템 콜: 유저 모드에서 커널 모드의 시스템 소프트웨어에게 어떤 동작을 요청하기 위해 사용. x64아키텍처에서는 시스템콜을 위해 syscall 명령어가 있음.

 

Tool Installation

Tool: gdb

           - gdb: 리눅스의 대표적인 디버거, Ubuntu에 기본적으로 설치되어 있음

           - pwngdb: 블로그 찾아가며 설치

entry: 진입점에 중단점을 설정한 후 실행

break(b): 중단점 설정

continue(c): 계속 실행

disassemble: 디스어셈블 결과 출력

u, nearpc, pd: 디스어셈블 결과 가독성 좋게 출력

x: 메모리 조회

run(r): 프로그램 처음부터 실행

context: 레지스터, 코드, 스택, 백트레이스의 상태 출력

nexti(ni): 명령어 실행, 함수 내부로는 들어가지 않음

stepi(si): 명령어 실행, 함수 내부로 들어감

telescope(tele): 메모리 조회, 메모리값이 포인터일 경우 재귀적으로 따라가며 모든 메모리값 출력

vmmap: 메모리 레이아웃 출력

 

Tool: pwntools

프로세스 및 원격: 모형 작업 또는 원격 서버의 서비스를 대상으로 플로잇 수행

send & recv: 데이터 길이

packing & unpacking: 정수를 정수 배열로, 또는 정수를 정수로 변경

Interactive: 프로세스 또는 서버와 터미널로 직접 통신

ELF: ELF헤더의 매우 중요한 정보 수집

context.log_level: 출력플로잇 과정에서 출력할 정보의 중요도

shellcraft: 다양한 셸 코드를 제공

asm: 어셈블리 코드를 기계로 어셈블