컴퓨터 == CPU + memory 로 구성 되어있다.
실행할 명령어와 명령어 처리에 필요한 데이터를 메모리에서 읽고, Instruction Set Architecture(ISA) 에 따라 이를 처리한다.
연산의 결과는 메모리에 다시 적재한다.
공격자의 악의적인 메모리 조작 값에 의해 CPU 도 잘못된 동작을 하는 것
메모리 오염과 관련된 취약점인
Stack Buffer Overflow, Off by one, Format String Bug, Double Free Bug, Use After Free 에 대해서 탐구하는 것이 해당 커리큘럼의 목표이다.
적재되는 데이터의 용도별로 메모리의 구획을 나눈것
리눅스는 프로세스의 메모리를 크게 5가지의 세그먼트로 구분
-> code, data, BSS, Heap, stack
Segment 의 이점
각 용도에 맞게 적절한 권한 (R, W, X) 부여 가능
쓰기권한이 없는 이유 : 공격자가 악의적인 코드를 삽입하기가 쉬워지기 때문
CPU 가 이 세그먼트의 데이터를 읽을수 있어야 하기 때문
프로그램이 실행되면서 값이 변할 수 있는 데이터가 위치 (ex 전역변수)
프로그램이 실행되면서 값이 변하면 안되는 데이터들이 위치
(상수 문자열은 rodata)
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, string == rodata
int main() { ... }
문자형 포인터 변수 str_ptr 이 가리키는 "readonly" 문자열은 상수문자열로 취급되어, rodata 에 위치한다!
초기화 되지 않은 전역변수가 위치 BSS segment 에 위치!
함수의 인자(argument) 나 지역변수 (local variable) 와 같은 임시 변수들이 실행중에 저장
함수가 호출될 때 생성, 반환될 때 해제
스택이 확장될 때, 기존 주소보다 낮은 주소로 확장된다.
CPU 가 자유롭게 값을 읽고 쓸 수 있어야 하기 때문
void func()
{
int choice = 0;
scanf("%d", &choice);
if(choice)
call_true();
else
call_false();
return 0;
}
지역변수 choice 가 스택에 저장된다.
스택과 마찬가지로 실행중에 동적으로 할당될 수 있다
리눅스의 경우 스택 세그먼트와 반대방향으로 자란다
int main()
{
int *head_data_pttr = malloc(sizeof(*head_data_ptr));
*head_data_ptr = 31337;
printf("%d\n", *head_data_ptr);
return 0;
}
힙 세그먼트 모두 사용 후 확장할 때 스택 세그먼트와 충돌하는 것을 방지하기 위해서이다.