System

[Dreamhack/System Hacking] Stage1~2

오호츠크해 기단 2023. 1. 17. 16:18
728x90

Stage 1

환경 구축하기

VMware에 Ubuntu를 설치한다.

우분투 설치 중...^^

 

 

Stage 2

새그먼트

리눅스에서는 프로세스의 메모리를 크게 5가지의 세그먼트(Segment)로 구분한다. 여기서 세그먼트란 적재되는 데이터의 용도별로 메모리의 구획을 나눈 것인데, 크게 코드 세그먼트, 데이터 세그먼트, BSS 세그먼트, 힙 세그먼트, 그리고 스택 세그먼트로 구분된다.

코드 세그먼트(Code Segment)

: 실행 가능한 기계 코드가 저장된 영역

읽기+실행 권한 부여 -> 프로그램이 동작하려면 코드를 실행할 수 있어야 하므로

ex) main() 등의 함수 코드

 

 

데이터 세그먼트(Data Segment)

: 컴파일 시점에 값이 정해진 전역 변수 및 전역 상수들이 위치

읽기 권한 부여 -> CPU가 이 세그먼트의 데이터를 읽을 수 있어야 하므로

ex) 초기화된 전역 변수, 전역 상수

쓰기 가능 <data 세그먼트> 쓰기 불가능 <rodata(read-only data) 세그먼트>
전역 변수와 같이 프로그램이 실행되면서 값이 변할 수 있는 데이터들이 위치 전역으로 선언된 상수 등 값이 변하면 안되는 데이터들이 위치

 

 

BSS 세그먼트(BSS Segment, Block Started By Symbol Segment)

: 컴파일 시점에 값이 정해지지 않은 전역 변수가 위치

읽기+쓰기 권한 부여

ex) 초기화되지 않은 전역 변수

 

개발자가 선언만 하고 초기화하지 않은 전역변수 등이 포함

 

 

스택 세그먼트(Stack Segment)

: 프로세스의 스택이 위치하는 영역

읽기+쓰기 권한 부여 -> CPU가 자유롭게 값을 읽고 쓸 수 있어야 하므로

ex) 지역 변수, 함수의 인자 등

 

함수의 인자나 지역 변수와 같은 임시 변수들이 실행중에 저장됨

스택 프레임(Stack Frame)이라는 단위로 사용되며 스택 프레임은 함수가 호출될 때 생성되고, 반환될 때 해제됨

 

 

힙 세그먼트(Heap Segment)

: 힙 데이터가 위치

읽기+쓰기 권한 부여

ex) malloc(), calloc() 등으로 할당 받은 메모리

 

스택과 마찬가지로 실행중에 동적으로 할당될 수 있으며, 리눅스에서는 스택 세그먼트와 반대 방향으로 자람

 

 

 

폰 노이만 구조

근대의 컴퓨터는 연산과 제어를 위해 중앙처리장치(Central Processing Unit, CPU)를, 저장을 위해 기억장치(memory)를 사용

중앙처리장치🧠

CPU는 프로그램의 연산을 처리하고 시스템을 관리하는 컴퓨터의 두뇌이다. 프로세스의 코드를 불러오고, 실행하고, 결과를 저장하는 일련의 모든 과정이 CPU에서 일어난다. CPU는 산술/논리 연산을 처리하는 산술논리장치(Arithmetic Logic Unit, ALU)와 CPU를 제어하는 제어장치(Control Unit), CPU에 필요한 데이터를 저장하는 레지스터(Register) 등으로 구성된다.

 

기억장치💾

기억장치는 컴퓨터가 동작하는데 필요한 여러 데이터를 저장하기 위해 사용되며, 용도에 따라 주기억장치와 보조기억장치로 분류된다.

 

버스🚌

버스는 컴퓨터 부품과 부품 사이 또는 컴퓨터와 컴퓨터 사이에 신호를 전송하는 통로를 말한다.

 

명령어 집합 구조(Instruction Set Architecture, ISA)

CPU가 해석하는 명령어의 집합을 말한다.

 

 

 

 

x86-64 아키텍처: 레지스터

* x64 아키텍처는 인텔의 64비트 CPU 아키텍처

x64 아키텍처에는 범용 레지스터(General Register)세그먼트 레지스터(Segment Register)명령어 포인터 레지스터(Instruction Pointer Register, IP)플래그 레지스터(Flag Register)가 존재한다.

레지스터는 CPU가 데이터를 빠르게 저장하고 사용할 때 이용하는 보관소이며, 산술 연산에 필요한 데이터를 저장하거나 주소를 저장하고 참조하는 등 다양한 용도로 사용된다.

 

 

 

 

Stage 3

어셈블리 언어

 

기본 구조🧱

문장은 동사에 해당하는 명령어(Operation Code, Opcode)와 목적어에 해당하는 피연산자(Operand)로 구성된다.

x86-64 어셈블리어 문법 구조

 

 

명령어🔫

데이터 이동(Data Transfer) mov, lea
산술 연산(Arithmetic) inc, dec, add, sub
논리 연산(Logical) and, or, xor, not
비교(Comparison) cmp, test
분기(Branch) jmp, je, jg
스택(Stack) push, pop
프로시져(Procedure) call, ret, leave
시스템 콜(System call) syscall

 

 

피연산자🎯

피연산자에는 올 수 있는  3가지

  • 상수(Immediate Value)
  • 레지스터(Register)
  • 메모리(Memory)

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

Ex) QWORD PTR [0x8048000] -> 0x8048000의 데이터를 8바이트만큼 참조

 

 

 

데이터 이동🚚

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

mov rdi, rsi rsi의 값을 rdi에 대입
mov QWORD PTR[rdi], rsi rsi의 값을 rdi가 가리키는 주소에 대입
mov QWORD PTR[rdi+8*rcx], rsi rsi의 값을 rdi+8*rcx가 가리키는 주소에 대입

 

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

lea rsi, [rbx+8*rcx] rbx+8*rcx 를 rsi에 대입

 

 

 

산술 연산❌

add dst, src: dst에 src의 값을 더한다.

add eax, 3 eax += 3
add ax, WORD PTR[rdi] ax += *(WORD *)rdi

sub dst, src: dst에서 src의 값을 뺀다.

sub eax, 3 eax -= 3
sub ax, WORD PTR[rdi] ax -= *(WORD *)rdi

inc op: op의 값을 1 증가시킨다.

inc eax eax += 1

dec op: op의 값을 1 감소 시킨다.

dec eax eax -= 1

 

 

논리 연산🤔 - and & or

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

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

논리연산🤔 - xor & not

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

not op: op의 비트 전부 반전

 

 

비교⚖️

비교 명령어는 두 피연산자의 값을 비교하고, 플래그를 설정한다.

 

cmp op1, op2: op1과 op2를 비교

test op1, op2: op1과 op2를 비교

 

 

분기🔀

분기 명령어는 rip를 이동시켜 실행 흐름을 바꾼다.

 

jmp addr: addr로 rip를 이동

1: xor rax, rax
2: jmp 1 ; jump to 1

je addr: 직전에 비교한 두 피연산자가 같으면 addr로 rip이동(jump if equal)

1: mov rax, 0xcafebabe
2: mov rbx, 0xcafebabe
3: cmp rax, rbx ; rax == rbx
4: je 1 ; jump to 1

jg addr: 직전에 비교한 두 연산자 중 전자가 더 크면 addr로 rip이동 (jump if greater)

1: mov rax, 0x31337
2: mov rbx, 0x13337
3: cmp rax, rbx ; rax > rbx
4: jg 1  ; jump to 1

 

 

Q1.

  end로 점프하면 프로그램이 종료된다고 가정하자. 프로그램이 종료됐을 때, 0x400000 부터 0x400019까지의 데이터를 대응되는 아스키 문자로 변환하면 어느 문자열이 나오는가?

1: mov dl, BYTE PTR[rsi+rcx]

: rsi+rcx의 주소를 참조한 값을 dl 레지스터에 복사

  *dl : RDX(Extended Data Register), DX(Data Register)의 하위 8bit 레지스터

 

dl = 0x67 0x55 0x5c 0x53 0x5f 0x5d 0x55 0x10

 

2: xor dl, 0x30

: dl 레지스터 값과 0x30 값을 xor 연산하여 dl에 저장

 

dl = 0x57 0x65 0x6C 0x63 0x6F 0x6D 0x65 0x20

 

3: mov BYTE PTR[rsi+rcx], dl

: dl 레지스터 값을 rsi+rcx 주소 참조 값에 복사

 

dl = 0x400000 <= 0x57 0x65 0x6C 0x63 0x6F 0x6D 0x65 0x20

 

4: inc rcx

1증가

rcx = 0x1

5: cmp rcx, 0x19

rcx 값과 0x19 크기 비교

6: jg end

: rcx 값이 0x19보다 크면 end로 jump

 

Result : 0x1 > 0x19 (False)

7: jmp 1

1로 jump

 

[Before Memory]
0x400000 | 0x67 0x55 0x5c 0x53 0x5f 0x5d 0x55 0x10
0x400008 | 0x44 0x5f 0x10 0x51 0x43 0x43 0x55 0x5d 
0x400010 | 0x52 0x5c 0x49 0x10 0x47 0x5f 0x42 0x5c
0x400018 | 0x54 0x11 0x00 0x00 0x00 0x00 0x00 0x00

[After Memory]
0x400000 | 0x57 0x65 0x6C 0x63 0x6F 0x6D 0x65 0x20 : Welcome
0x400008 | 0x74 0x6F 0x20 0x61 0x73 0x73 0x65 0x6D : to assem
0x400010 | 0x62 0x6C 0x79 0x20 0x77 0x6F 0x72 0x6C : bly worl
0x400018 | 0x64 0x21 0x30 0x30 0x30 0x30 0x30 0x30 : d!000000

 

Answer: Welcome to assembly world!000000

 

 

 

Opcode: 스택🧱

push val : rsp를 8만큼 빼고, 스택의 최상단에 val을 쌓음

연산
rsp -= 8
[rsp] = val

pop reg : 스택 최상단의 값을 reg에 넣고, rsp를 8만큼 더함

연산
rsp += 8
reg = [rsp-8]

 

Opcode: 프로시저📜

프로시저(Procedure)란?

: 특정 기능을 수행하는 코드 조각

 

호출(Call): 프로시저를 부르는 행위

반환(Return): 프로시저에서 돌아오는 것

프로시저를 호출할 때는 프로시저를 실행하고 나서 원래의 실행 흐름으로 돌아와야 하므로, call 다음의 명령어 주소(return address, 반환 주소)를 스택에 저장하고 프로시저로 rip를 이동시킨다.

 

 

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

연산
push return_address
jmp addr

ret: return address로 반환

연산
pop rip

 

 

Opcode: 시스템 콜📟

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

유저 모드: 운영체제가 사용자에게 부여하는 권한

 

시스템 콜(system call, syscall)은 유저 모드에서 커널 모드의 시스템 소프트웨어에게 어떤 동작을 요청하기 위해 사용된다. x64아키텍쳐에서는 시스템콜을 위해 syscall 명령어가 있다.

syscall

커널에게 필요한 동작을 요청한다.

요청: rax
인자 순서: rdi → rsi → rdx → rcx → r8 → r9 → stack

 

Q2.

  다음 어셈블리 코드를 실행했을 때 출력되는 결과로 올바른 것은?

r3ady 70 d3bu6?