Linux Memory Layout - Segment

신우빈·2022년 6월 29일
0

Dreamhack

목록 보기
2/7
post-thumbnail

Intro.

컴퓨터 = CPU + Memory

CPU

  • 실행할 명령어와 명령어 처리에 필요한 데이터를 메모리에서 읽기
  • Instruction Set Architecture(ISA)에 따라 처리
  • 연산 결과 메모리에 적재

CPU의 동작과 메모리 사이에 밀접한 연관이 있음을 알 수 있다.

즉, 공격자가 메모리를 악의적으로 조작할 수 있다면, 조작된 메모리 값에 의해 CPU도 잘못된 동작을 할 수 있다.

위의 경우를 메모리가 오염됐다고 표현하며, 이를 유발하는 취약점을 메모리 오염 취약점이라고 함.
시스템 해킹에서 많은 공격기법이 메모리 오염을 기반으로 함.
그리고, 이를 이해하기 위한 배경으로 리눅스 메모리 구조에 대해 오늘 배운다.

목표
1. 프로세스 가상메모리의 각 구역이 어떤 정보를 담고 있는지 이해
2. 1을 통해 프로세스 메모리의 전체 구조 알기

Segment

Segment : 적재되는 데이터의 용도별로 메모리의 구획을 나눈 것(
( 리눅스에서는 프로세스의 메모리를 크게 5가지의 Segment로 구분 )

간단히 말해서, Segment = 메모리 영역

종류

  • 코드 세그먼트
  • 데이터 세그먼트
  • BSS 세그먼트
  • 힙 세그먼트
  • 스택 세그먼트

장점
운영체제가 메모리를 용도별로 나누면, 각 용도에 맞게 적절한 권한을 부여할 수 있다.
권한 : 읽기, 쓰기, 실행
CPU는 메모리에 대한 권한이 부여된 행위만 가능

'실행 중인 프로세스의 메모리가 5개의 영역으로 구분된다.'


Code Segment

실행 가능한 기계 코드가 위치하는 영역(=Text Segment)

권한
읽기 및 실행 => 프로그램이 동작하려면 코드를 실행할 수 있어야하기 때문

쓰기 권한이 있다면?
공격자가 악의적인 코드 삽입하기 쉬움.
그래서 대부분의 현대 운영체제는 Code Segment에 쓰기 권한 부여 X

예시

int main() { return 31337; }

main함수가 컴파일 되면 기계 코드로 변환. 이 기계 코드가 Code Segment에 위치


Data Segment

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

권한
읽기 => CPU가 이 Segment의 Data를 읽을 수 있어야하기 때문
쓰기

  • 가능 : 전역 변수와 같이 프로그램이 실행되면서 값이 변할 수 있는 데이터들이 위치 (Data Segment)
  • 불가능 : 프로그램이 실행되면서 값이 변하면 안되는 데이터들이 위치 (read-only data = rodata)
    o 전역으로 선언된 상수가 여기에 포함.

예시

int data_num = 31337; // data
char dta_rwstr[] = "writable_data"; // data
const char data_rostt[] = "readonly_data"; // rodata
char *str_ptr = "readonly"; // str_ptr은 data, 문자열은 rodata

int main(){ ... }

str_ptr : "readonly" 라는 문자열을 가리킴. 이 문자열은 상수 문자열 로 취급

따라서 문자열 "readonly" 는 rodata에 위치하며,
이를 가리키는 str_ptr 은 전역 변수로서 data에 위치


BSS Segment

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

개발자가 선언만 하고 초기화하지 않은 전역변수 등이 포함됨.
BSS Segment 는 프로그램이 시작될 때, 모두 0으로 값이 초기화 됨.

권한
읽기 및 쓰기

int bss_data;

int main() {
	printf("%d\n", bss_data);
    return 0;
 }

bss_data는 초기화되지 않은 전역 변수이기 때문에 0이며, bss segment에 위치


Stack Segment

포르세스의 스택이 위치하는 영역

함수의 인자나 지역 변수와 같은 임시 변수들이 실행중에 이곳에 저장.
단위
Stack Frame : 함수가 호출될 때 생성되고, 반환될 때 해제

프로그램의 전체 실행 흐름은 사용자의 입력을 비롯한 여러 요인에 영향을 받음.

따라서, 어떤 프로세스가 실행될 때, 이 프로세스가 얼만큼의 Stack Frame을 사용하게 될지 예상하는 것은 일반적으로 불가능하다.
( 아래의 예시에서 choice의 값에 따라 call_true 혹은 call_false가 결정됨 )
그래서 운영체제는 프로세스 시작 시 작은 크기의 Stack Segment 를 먼저 할당.
부족해질 때 마다 이를 확장

Stack은 확장될 때, 기존 주소보다 낮은 주소로 확장

권한
읽기와 쓰기

예시

void func() {
	int choice = 0;
    
    scanf("%d", &choice);
    if(choice)
    	call_true();
    else
    	call_false();
        
    return 0;
    }

위 코드에서 choice가 스택에 저장.


Heap Segment

Heap Data 가 위치하는 Segment. Stack 과 마찬가지로 실행중에 동적으로 할당될 수 있음

C언어에서 malloc(), calloc() 등을 호출해서 할당받는 메모리 = Heap Segment에 위치.

권한
읽기와 쓰기

int main(){
	int *heap_data_ptr = malloc(sizeof(*heap_data_ptr)); // 동적 할당한 힙 영역의 주소를 가리킴
    *heap_data_ptr = 31337; // 힙 영역에 값을 씀
    printf("%d\n", *heap_data_ptr); // 힙 영역의 값을 사용함
    return 0;
    }
  • heap_data_ptr에 malloc()으로 동적 할당한 영역의 주소를 대입하고 이 영역에 값을 씀.
  • heap_data_ptr은 지역변수 -> stack에 위치. malloc으로 할당받은 heap segment의 주소를 가리킴.

Heap Segment 와 Stack Segment 가 반대 방향으로 자라는 이유는?

  • Heap 과 Stack 은 메모리를 최대한 자유롭게 사용할 수 있으며, 충돌 문제로부터 비교적 자유로움.

정리

Segment역할일반적인 권한Example비고
Code실행 가능한 코드가 저장된 영역읽기, 실행main()등의 함수 코드
Data초기화 o 전역 변수 or 상수가 위치읽기와 쓰기 or 읽기 전용초기화된 전역 변수, 전역 상수Data & rodata
BSS초기화 x 데이터가 위치읽기, 쓰기초기화되지 않은 전역 변수
Stack임시 변수가 저장읽기, 쓰기지역 변수, 함수의 인자 등
Heap실행 중 동적으로 사용되는 영역읽기, 쓰기malloc(), calloc()로 할당받은 메모리

0개의 댓글