컴퓨터의 구성 : 컴퓨터는 CPU와 메모리로 구성.
메모리 오염 : 공격자가 메모리를 악의적으로 조작하면 CPU가 잘못된 동작을 가능
이를 유발하는 취약점을 메모리 오염 취약점(Memory Corruption)이라고 함.
리눅스의 메모리는 5가지의 세그먼트(segment)로 구분.
코드 세그먼트, 데이터 세그먼트, BSS 세그먼트, 힙 세그먼트, 스텍 세그먼트로 구분함.
메모리를 나누는 이유 : 각 용도에 맞게 적절한 권한 부여 가능. 권한에는 읽기, 쓰기, 실행이 있음.
실행 가능한 기계코드가 위치하는 영역. 텍스트 세그먼트(Text Segment)라고도 함.
int main() { return 31337; }
//main 함수 컴파일 시 정수가 기계코드로 변환되는데, 이는 코드 세그먼트에 위치하게 됨.
컴파일 시점에 값이 정해진 전역 변수 및 전역 상수가 위치하는 영역.
int data_num = 31337; //data
char data_rwstr[] = "writable_data"; //data
const char data_rostr[] = "readonly_data"; //rodata
char *str_ptr = "readonly"; //str_ptr은 data, 문자열은 rodata
int main() {}
컴파일 시점에 값이 정해지지 않은 전역 변수가 위치하는 영역. (ex. 초기화 하지 않은 전역변수)
프로그램이 시작될 때 모두 0으로 값이 초기화.
int bss_data;
int main() {
printf("%d\n",bss_data); //0
return 0;
프로세스의 스택이 위치하는 영역.
함수의 인자나 지역 변수와 같은 임시 변수들 저장.
void func() {
int choice = 0; //지역 변수 choice가 스택에 저장
scanf("%d",&choice);
if (choice) //유저가 입력한 choice에 따라 결과 다름
call_true();
else
call_false();
return 0;
}
힙 데이터가 위치하는 영역.
실행 중에 동적으로 할당 가능하며 스택 세그먼트와 반대 방향으로 자람.
-> 힙과 스택이 메모리를 최대한 자유롭게 사용하고 충돌 문제에서도 비교적 자유로워 짐.
int main() {
int *heap_data_ptr =
malloc(sizeo(*heap_data_ptr)); //동적 할당한 힙 영역의 주소
*heap_data_ptr = 31337; //힙 영역에 값을 씀
printf("%d\n",*heap_data_ptr); //힙 영역의 값을 사용함
return 0;
}
세그먼트 | 역할 | 일반적인 권한 | 사용 예 |
---|---|---|---|
코드 세그먼트 | 실행 가능한 코드가 저장된 영역 | 읽기, 실행 | main() 등의 함수코드 |
데이터 세그먼트 | 초기화 된 전역 변수 또는 상수가 위치하는 영역 | 읽기와 쓰기 또는 읽기 전용 | 초기화 된 전역 변수, 전역 상수 |
BSS 세그먼트 | 초기화되지 않은 데이터가 위치하는 영역 | 읽기, 쓰기 | 초기화되지 않은 전역 변수 |
스택 세그먼트 | 임시 변수가 저장되는 영역 | 읽기, 쓰기 | 지역 변수, 함수의 인자 등 |
힙 세그먼트 | 실행 중에 동적으로 사용되는 영역 | 읽기, 쓰기 | malloc(), calloc() 등으로 할당받은 메모리 |
컴퓨터가 효율적으로 작동할 수 있도록 하드웨어 및 소프트웨어의 기능을 고안하고 이들을 구성하는 방법.
컴퓨터의 기능 구조에 대한 설계, 명령어 집합구조, 마이크로 아키텍처, 기타 하드웨어 및 컴퓨팅 방법에 대한 설계 등 포함.
폰 노이만은 컴퓨터에 연산, 제어, 저장의 세 가지 핵심 기능이 필요하다고 생각함.
이를 위해 근대의 컴퓨터는 중앙처리장치, 기억장치, 버스 사용.
프로그램의 연산 처리, 시스템을 관리하는 컴퓨터의 두뇌.
프로세스의 코드를 불러오고, 실행하고, 결과를 저장하는 과정이 일어남.
컴퓨터가 동작하는데 필요한 여러 데이터를 저장하기 위해 사용.
컴퓨터 부품과 부품 사이 또는 컴퓨터와 컴퓨터 사이에 신호를 전송하는 통로.
데이터 버스, 주소 버스, 제어 버스 등 있음
- 데이터 버스(Data Bus) : 데이터가 이동
- 주소 버스(Address Bus) : 주소를 지정
- 제어 버스(Control Bus) : 읽기/쓰기를 제어
랜선, 데이터 전송 소프트웨어, 프로토콜 등도 버스라고 불림
CPU가 해석하는 명령어의 집합.
프로그램은 기계어로 이루어져 있고, 실행하면 이 명령어들을 CPU가 읽고 처리.
컴퓨터의 연산 능력 수준과 컴퓨팅 환경이 다양하기 때문에 ISA도 IA-32, x86-65(x64), MIPS, AVR 등 다양하게 존재.
인텔의 64비트 CPU 아키텍처.
인텔의 32비트 CPU 아키텍처인 IA-32를 64비트 환경에서 사용할 수 있도록 확장.
CPU가 데이터를 빠르게 저장하고 사용할 때 이용하는 보관소.
산술 연산에 필요한 데이터를 저장하거나 주소를 저장하고 참조하는 등 다양한 용도로 사용.
이름 | 주용도 |
---|---|
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) | 스택의 바닥을 가리키는 포인터 |
플래그 | 의미 |
---|---|
CF(Carry Flag) | 부호 없는 수의 연산 결과가 비트의 범위를 넘을 경우 설정 |
ZF(Zero Flag) | 연산의 결과가 0일 경우 설정 |
SF(Sign Flag) | 연산의 결과가 음수일 경우 설정 |
OF(Overflow Flag) | 부호 있는 수의 연산 결과가 비트 범위를 넘을 경우 설정 |
x86-64 아키텍처는 IA-32의 64비트 확장 아키텍처이며 호환 가능.
IA-32에서 CPU의 레지스터들은 32비트 크기이며 각각의 명칭은 eax, ebx, ecx, edx, esi, edi, esp, ebp임.
명령어(Operation Code, Opcode)와 피연산자(Operand)로 구성.
mov
, lea
inc
, dec
, add
, sub
and
, or
, xor
, not
cmp
, test
jmp
, je
, jg
push
, pop
call
, ret
, leave
syscall
피연산자에는 상수(Immediate Value), 레지스터(Register), 메모리(Memory)가 있음.
rip
를 이동시켜 실행 흐름 바꿈특정 기능을 수행하는 코드 조각으로 반복되는 연산을 프로시저 호출로 대체할 수 있어 전체 코드의 크기 축소 가능하며 기능별로 코드 조각에 이름을 붙일 수 있어 가독성 향상
프로시저를 부르는 행위를 호출(Call), 프로시저에서 돌아오는 것을 반환(Return)이라고 하며 call 다음의 명령어 주소를 스택에 저장하고 프로시저로 rip를 이동
- call addr : addr에 위치한 프로시저 호출
- leav : 스택프레임 정리
- ret : return address로 반환
운영체제는 커널 모드와 유저 모드로 권한이 나뉘는데 시스템 콜은 유저 모드에서 커널 모드의 시스템 소프트웨어에게 어떤 동작을 요청하기 위해 사용
유저 모드의 소프트웨어가 도움을 요청 -> 커널이 요청한 동작 수행 -> 유저에게 결과 반환
- syscall 명령어