[Pwnable] 1. 리눅스 메모리 구조(Linux Memory Layout)

Wonder_Land🛕·2022년 10월 18일
0

[Pwnable]

목록 보기
1/21
post-thumbnail

[Reference] : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.


  1. 서론
  2. 리눅스 프로세스의 메모리 구조
  3. Q&A
  4. 마치며

1. 서론

컴퓨터는 크게 CPU와 메모리로 구성되어 있습니다.

CPU에서는 실행할 명령어와 명령어 처리에 필요한 데이터를 읽고, ISA에 따라 이를 처리합니다.
그리고 연산의 결과를 다시 메모리에 적재합니다.

즉, CPU와 메모리 사이에는 밀접한 연관이 있습니다.

많은 시스템 해킹 공격기법은 메모리 오염 기법을 통해 CPU가 잘못된 동작을 수행하게 합니다.

이러한 공격기법을 배우기 전,
배경 지식으로 '리눅스 메모리 구조(Linux Memory Layout)'을 배우겠습니다.


2. 리눅스 프로세스의 메모리 구조

1) 세그먼트(Segment)

  • 세그먼트(Segment)
    : 적재되는 데이터의 용도별로 메모리의 구획을 나눈 것

리눅스에서는 프로세스의 메모리를 5가지 세그먼트로 구분합니다.

  1. 코드 세그먼트

  2. 데이터 세그먼트

  3. BSS 세그먼트

  4. 스택 세그먼트

  5. 힙 세그먼트

로 구분됩니다.

이렇게 용도별로 메모리가 구분되면,
각 용도에 맞게 적절한 권한을 부여할 수 있는 장접이 있습니다.

권한으로는 '읽기, 쓰기, 실행'이 존재하며,
CPU는 메모리에 부여된 권한에 맞는 행위만 할 수 있습니다.

(1) 코드 세그먼트(Code Segment)

  • 코드 세그먼트(Code Segment)
    : 실행 가능한 기계 코드가 위치하는 영역
    : 텍스트 세그먼트(Text Segment)라고도 함
    : 읽기, 실행 권한

프로그램이 동작하려면 코드를 실행할 수 있어야 하기 때문에,
읽기 및 실행 권한이 부여됩니다.

만약 쓰기 권한이 있다면, 공격자가 악의적인 코드를 삽입할 수 있으므로,
대부분의 운영체제는 코드 세그먼트에 쓰기 권한을 제한합니다.

int main() { return 31337; }

위의 코드가 컴파일 되면, 554889e5b8697a00005dc3라는 기계 코드로 변환되고,
이 기계 코드가 코드 세그먼트에 위치합니다.


(2) 데이터 세그먼트(Data Segment)

  • 데이터 세그먼트(Data Segment)
    : 컴파일 시점에 값이 정해진 전역 변수 및 전역 상수가 위치하는 영역
    : 읽기 권한

CPU가 이 세그먼트의 데이터들을 읽을 수 있어야 하므로,
읽기 권한이 부여됩니다.

데이터 세그먼트는 다시

  1. 쓰기가 가능한 세그먼트
  2. 쓰기가 불가능한 세그먼트

로 구분됩니다.

쓰기가 가능한 세그먼트는
전역 변수와 같이 프로그램이 실행되면서 값이 변할 수 있는 데이터들이 위치합니다.

이런 세그먼트는 'data 세그먼트'라고 부릅니다.

반면 쓰기가 불가능한 세그먼트는
전역 상수와 같이 프로그램이 실행되면서 값이 변하면 안되는 데이터들이 위치합니다.

이런 세그먼트는 'rodata(read-only data) 세그먼트'라고 부릅니다.

// data
int data_num = 31337; 

// data
char data_rwstr[] = "writable_data";  

// rodata
const char data_rostr[] = "readonly_data"; 

// str_ptr은 data, 문자열은 rodata
char *str_ptr = "readonly";  

int main() {...}

위의 예제에서 data_num, data_rwstr, str_ptr은 전역 변수로 data 세그먼트에 위치합니다.

반면 data_rostr"readonly"는 전역 상수로 rodata 세그먼트에 위치합니다.


(3) BSS 세그먼트

  • BSS 세그먼트(Block Started by Symbol Segment)
    : 컴파일 시점에 값이 정해지지 않은 전역 변수가 위치하는 영역
    : 읽기, 쓰기 권한

이 세그먼트에는,
개발자가 선언만 하고 초기화하지 않은 전역변수 등이 포함됩니다.

이 영역은 프로그램이 시작될 때, 모두 0으로 값이 초기화됩니다.

따라서, C언어를 사용할 때,
초기화되지 않은 전역 변수의 값은 0이 됩니다.

int bss_data;

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

위의 코드에서 bss_data는 초기화되지 않은 전역 변수이므로, BSS 세그먼트에 위치합니다.


(4) 스택 세그먼트

  • 스택 세그먼트(Stack Segment)
    : 함수의 인자나 지역변수와 같은 임시변수가 위치하는 영역
    : 읽기, 쓰기 권한

스택 세그먼트는 '스택 프레임(Stack Frame)'이라는 단위로 사용됩니다.

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

위의 코드에서 choice는 지역 변수이므로, 스택 세그먼트에 위치합니다.


(5) 힙 세그먼트

  • 힙 세그먼트(Heap Segment)
    : 힙 데이터가 위치하는 영역
    : 읽기, 쓰기 권한

힙 세그먼트는 스택 세그먼트와 반대 방향으로 자랍니다.

C언어에서 malloc(), calloc() 등을 통해 할당받는 메모리가 힙 세그먼트에 위치합니다.

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()으로 할당받은 영역은 힙 세그먼트에 위치합니다.


3. Q&A

-


4. 마치며

오랜만에 글을 쓰네요.

리버싱 핵심원리, '코드엔진'의 basic 리버싱 문제로 리버싱 공부를 하다가

시스템 해킹으로 넘어왔습니다.

보안 분야는 정말 쉽지 않은 것 같습니다...😢

공부를 하면 할수록 더 모르게 되는 느낌이에요
물론 시간이 약이라지만...

울고싶어요ㅠㅠ

진짜 겨우겨우 하고 있는데 너무 힘드네요....

[Reference] : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.

profile
아무것도 모르는 컴공 학생의 Wonder_Land

0개의 댓글