[System Hacking] 컴퓨터 구조, 리눅스 메모리 구조
가상머신
컴퓨터를 에뮬레이팅 한 것
가상머신을 작동시키는 컴퓨터(host) / 가상 머신 안에서 작동하는 컴퓨터(guest)
컴퓨터 구조(Computer Architecture)
컴퓨터에 대한 기본 설계. 이에 맞추워 여러 하드웨아가 개발되고 이를 조립해 컴퓨터가 완성됨.
컴퓨터가 효율적으로 작동할 수 있도록 하드웨어 및 소프트웨어의 기능을 고안하고, 이들을 구성하는 방법,
기능 구조의 설계
컴퓨터가 연산을 효율적으로 하기 위해 어떤 기능들이 컴퓨터에 필요한지 고민,설계
(ex) 폰 노이만 구조, 하버드 구조, 수정된 하버드 구조
명령어 집합구조(Instruction Set Architecture)
컴퓨터 구조 중 CPU가 사용하는 명령어와 관련된 설계
(ex) x86-64 아키텍처, ARM, MIPS, AVR
마이크로 아키텍처(Micro Architexture)
정의된 명령어 집합을 효율적으로 처리할 수 있도록, CPU의 회로를 설계하는 분야
(ex) 캐시설계, 파이프라이닝, 슈퍼 스칼라, 분기 예측, 비순차적 명령어 처리
하드웨어 및 컴퓨팅 방법론
(ex) 직접 메모리 접근
폰 노이만 구조
연산,제어,저장의 세가지 핵심 기능 필요 강조
중앙처리장치(Central Processing Unit, CPU)
프로그램의 연산을 처리하고 시스템을 관리하는 컴퓨터의 두뇌.
프로세스의 코드를 불러오소, 실행하고, 결과를 저장하는 과정 모두 이 안에서 일어남.
산술논리장치(Arithmetic Logic Unit, ALU), 제어장치(Control Unit), 레지스터(Register) 등으로 구성
기억장치(memory)
컴퓨터가 동작하는데 필요한 여러 데이터를 저장하기 위해 사용
- 주기억장치
- 실행과정에서 필요한 데이터들을 임시로 저장하기 위해 사용 (ex, RAM)
- 보조기억장치
- 운영체제, 프로그램 등과 같은 데이터를 장기간 보관하고자 할 때 사용 (ex, HDD, SSD)
버스
컴퓨터 부품과 부품 사이 또는 컴퓨터와 컴퓨터 사이에 신호를 전송하는 통로.\
- 데이터 버스(Data Bus) : 데이터 이동
- 주소 버스(Address Bus) : 주소 지정
- 제어 버스(Control Bus) : 읽기/쓰기 제어
- 랜선이나 데이터 전송 소프트웨어, 프로토콜 등도 속함
명령어 집합구조(Instruction Set Architecture,ISA)
CPU가 해석하는 명령어의 집합.
x86-64 아키텍처
인텔의 64비트 CPU 아키텍처. 인텔의 32비트 cpu 아키텍처인 IA-32를 64비트 환경에서 사용할 수 있도록 확장한 것.
N비트 아키텍처
N은 CPU가 한번에 처리할 수 있는 데이터의 크기.
= WORD : CPU가 이해할 수 있는 데이터의 단위
WORD가 클수록 가상메모리 자원이 크기에 유리함.
레지스터
CPU가 데이터를 빠르게 저장하고 사용할 때 이용하는 보관소.
산술 연산에 필요한 데이터를 저장하거나 주소를 저장하고 참조하는 등 다양한 용도로 사용.
범용 레지스터(general register)
주용도는 있으나 그 외 다양한 용도로 사용될 수 있는 레지스터.
rax (accumulator register) | 함수의 반환 값 |
rbx (base register) | x64에서는 주된 용도 없음 |
rcx (counter register) | 반복문의 반복 횟수, 각종 연산의 시행 횟수 |
rdx (data register) | x64에서는 주된 용도 없음 |
rsi (source index) | 데이터를 옮길 때 원본을 가리키는 포인터 |
rdi (destination index) | 데이터를 옮길 때 목적지를 가리키는 포인터 |
rsp (stack pointer) | 사용중인 스택의 위치를 가리키는 포인터 |
rbp (stack base pointer) | 스택의 바닥을 가리키는 포인터 |
세그먼트 레지스터(segment register)
아키텍처가 확장되면서 용도에 큰 변화가 생긴 레지스터. (각 크기 16비트)
cs, ds, ss 레지스터는 코드,데이터, 스택메모리 영역을 가리킬때 사용, es,fs,gs는 운영체제 별로 용도를 결정.
명령어 포인터 레지스터(Instruction Pointer Register,IP)
CPU가 프로그램의 어느 부분의 코드를 실행할지 가리키는 역할.
rip, 8바이트.
플래그 레지스터(Flag Register)
프로세서의 현재 상태를 저장하고 있는 레지스터.
RFLAGS라는 64비트 플래그 레지스터가 존재. 여러 비트들로 CPU 상태 표현.
CF(Carry Flag) | 부호 없는 수의 연산 결과가 비트의 범위를 넘을 경우 설정. |
ZF(Zero Flag) | 연산의 결과가 0일 경우 설정. |
SF(Sign Flag) | 연산의 결과가 음수일 경우 설정. |
OF(Overflow Flag) | 부호 있는 수의 연산 결과가 비트 범위를 넘을 경우 설정. |
레지스터 호환
IA-16(ax,bx,cx,dx,si,di,sp,bp) -> IA-32 (eax,ehx,ecx,edx,esi,edi,esp,ebp) -> x86-64(rax,rbx,rcx,rdx,rsi,rdi,rsp,rbp)
퀴즈
1. rax = 0x0123456789abcdef , ah =? : 0xcd
2. rax - rbs -> ZF : rax == rbx
3. rax = 0x0123456789abcdef, eax =? : 0x89abcdef
4. rax = 0x0123456789abcdef, ax =? : 0xcdef
5. rax = 0x0123456789abcdef, al =? : 0xef
메모리 구조(Memory Layout)
메모리 오염(Memory Corruption) 취약점
공격자가 메모리를 조작하여 조작된 메모리 값에 의해 CPU가 잘못된 동작을 함
세그먼트(Segment)
적재되는 데이터의 용도별로 메모리의 구획을 나눈 것.
리눅스에서 프로세스의 메모리를 크게 5개의 세그먼트로 나눔.
운영체제가 메모리를 용도 별로 나누면 읽기,쓰기, 실행 권한을 용도에 맞게 부여함.
코드 세그먼트(Code Segment)
실행 가능한 기계 코드가 위치하는 영역, 텍스트 세그먼트라고도 불림.
읽기/실행 권한 부여.
데이터 세그먼트(Data Segment)
컴파일 시점에 값이 정해진 전역 변수 및 전역 상수들이 위치.
읽기 권한 부여.
- data 세그먼트 :전역 변수와 같이 프로그램이 실행되며 값이 변할 수 있는 데이터들이 위치. 쓰기권한 부여.
- rodata(read-only data) 세그먼트 : 값이 변하면 안되는 데이터들이 위치. 쓰기 불가능.
BSS 세그먼트(Block Started By Symbol Segment)
컴파일 시점에 값이 정해지지 않은 전역 변수가 위치하는 메모리 영역.
이 세그먼트의 메모리 영역은 프로그램이 시작될 때 모두 0으로 값이 초기화
스택 세그먼트(Stack Segment)
프로세스의 스택이 위치하는 영역. (임시변수, 지역변수 등이 실행될 떄 저장)
스택 프레임(Stack Frame) 단위로 사용, 이는 함수가 호출될 때 생성, 반환될 떄 해제.
읽기/쓰기 권한 부여
힙 세그먼트(Heap Segment)
힙 데이터가 위치하는 세그먼트. 스택과 마찬가지로 실행중에 동적 할당 가능.
리눅스에서 스택 세그먼트와 반대 방향으로 자람.(충돌 방지)
c에서 malloc,calloc등을 호출해서 할당받는 메모리가 이에 위치.
읽기/쓰기 권한 부여
세그먼트 | 역할 | 일반적 권한 | 사용 예 |
코드 세그먼트 | 실행 가능한 코드가 저장된 영역 | 읽기, 실행 | main() 등의 함수 코드 |
데이터 세그먼트 | 초기화된 전역 변수 또는 상수가 위치하는 영역 | 읽기와 쓰기 또는 읽기 전용 | 초기화된 전역 변수, 전역 상수 |
BSS 세그먼트 | 초기화되지 않은 데이터가 위치하는 영역 | 읽기, 쓰기 | 초기화되지 않은 전역 변수 |
스택 세그먼트 | 임시 변수가 저장되는 영역 | 읽기, 쓰기 | 지역 변수, 함수의 인자 등 |
힙 세그먼트 | 실행중에 동적으로 사용되는 영역 | 읽기, 쓰기 | malloc(), calloc() 등으로 할당 받은 메모리 |
퀴즈
1. d가 위치하는 세그먼트 : 스택 (d는 지역변수)
2. d_str가 위치하는 세그먼트: 읽기 전용 데이터(const 전역 변수 이므로)
3.c가 위치하는 세그먼트 : bss (초기화되지 않은 전역변수)
4. b가 위치하는 세그먼트:읽기 전용데이터(const 전역 변수)
5. e는 어느 세그먼트의 데이터인지 : 힙 (malloc)
6. a가 위치하는 세그먼트 : 데이터 (초기화된 전역변수)
7. foo가 위치하는 세그먼트 : 코드 (함수)
x86-64 어셈블리
x64 어셈블리 언어의 기본 구조
명령어(Operatuib Code, Opcode) + 피연산자(Operand)
명령어
명령어 | 역할 | 내용 |
데이터 이동(Data Transfer) | 어떤 값을 레지스터나 메모리에 옮기도록 지시 | mov(대입), lea(주소 저장) |
산술 연산(Arithmetic) | 덧셈,뺼셈,곱셈,나눗셈 연산 지시 | inc(+1), dec(-1), add(+), sub(-) |
논리 연산(Logical) | AND,OR,XOR,NEG 등 비트연산 지시 | and, or, xor, not |
비교(Comparison) | 두 피연산자의 값을 비교, 플래그 설정 | cmp(빼서 대소비교), test(AND 연산 비교) |
분기(Branch) | rip를 이동시켜 실행흐름을 바꿈 | jmp(주소로 이동), je(직전 비교가 같으면 점프), jg(직전 비교 중 전자가 더 크면 점프) |
스택(Stack) | - | push(최상단에 쌓기), pop(최상단 값을 꺼내 대입) |
프로시져(Procedure) | 프로시저:특정 기능을 수행하는 코드 조각 호출(call) - 프로시저를 부르는 행위 반환(return)- 프로시저에서 돌아오는 것 |
call(프로시저 호출), ret(return address로 반환), leave(스택프레임 정리) |
시스템 콜(System call) | 커널모드 - os가 전체 시스템 제어 위해 시스템 소프트웨어에 부여하는 권한 유저모드 - os가 사용자에게 부여하는 권한 시스템콜 - 유저모드에서 커널모드의 시스템 소프트웨어에게 동작을 요청할 때 사용 |
syscall* |
*
syscall | rax | arg0(rdi) | arg1(rsi) | arg2(rdx) |
read | 0x00 | unsigned int fd | char *buf | size_t count |
write | 0x01 | unsigned int fd | const char *buf | size_t count |
open | 0x02 | const char *filename | int flags | umode_t mode |
close | 0x03 | unsigned int fd | ||
mprotect | 0x0a | unsigned long start | size_t len | unsigned long prot |
connect | 0x2a | int sockfd | struct sockaddr * addr | int addrlen |
execve | 0x3b | const char *filename | const char *const *argv | const char *const *envp |
피연산자
- 상수(Immediate Value)
- 래지스터(Register)
- 메모리(Memory)
메모리 피연산자는 []로 표현
크기 지정자 TYPE PTR 추가 가능. BYTE(1),WORD(2),DWORD(4),QWORD(8)
퀴즈
1. Welcome to assembly world!
2. r34dy 70 d3bu6?