0. 프로세스 메모리란?
우리가 c언어와 같은 컴파일러로 바이너리 파일을 만들고 해당 프로그램을 실행하게 되면 해당 바이너리 파일은 cpu의 연산과정을 거쳐 실행이 되게 됩니다. 이때 cpu가 성능이 매우 빠르고 좋아도 한번에 모든 프로세스를 처리 할 수 없기 때문에 메모리를 사용하여 cpu가 처리하는 과정을 잘게 쪼개 여러 프로세스에 배분하는 방식으로 작동합니다 .
1. 리눅스에서의 프로세스 메모리
리눅스에서는 프로세스의 메모리를 5가지로 나눕니다. 이 나눈 부분을 "세그먼트" 라고 칭하며 코드 세그먼트, 데이터 세그먼트 bss세그먼트, 스택 세그먼트, 힙세그먼트로 나눌 수 있습니다. 이때 운영체제는 세그먼트 별로 읽기, 쓰기, 실행 권한을 부여하여 각 용도에 맞게 적절한 권한을 부여합니다.
1. 코드 세그먼트( ps. 텍스트 세그먼트라고 하기도 함)
코드 세그먼트에는 실행 가능한 코드가 위치하는 영역으로 프로그램이 동작하기 위해서 읽기 권한과 실행 권한이 부여됩니다. 운영체제별로 다르긴 하지만 대부분의 os는 공격자가 코드를 삽입하는 행위를 방지하기 위해 이 세그먼트에 쓰기권한을 제거합니다.
#include <stdio.h>
int add(int a, int b){ 어쩌구 }
int main(){ 어쩌구 return 0; }
2. 데이터 세그먼트
데이터 세그먼트에는 컴파일 시점에( 실행파일이 될때 ) 값이 정해진 전역변수 및 전역 상수들이 위치합니다. 데이터 세그먼트는 쓰기가 가능한 데이터 세그먼트와 쓰기가 불가능한 데이터 세그먼트 rodata(aka. read-only data) 세그먼트로 나누어집니다. 간단한 예제로 데이터 세그먼트와 rodata 세그먼트의 차이를 알아보겠습니다.
#include <stdio.h>
int number = 364;
char name[] = "Jalni_k"
//값이 변할 수 있는 데이터 세그먼트들
const char data[] = "read only"
// const char이므로 값이고정, rodata 세그먼트
char *st_ptr = "read only"
// st_ptr은 데이터 세그먼트, 포인터가 가르키는 "read only"는 rodata
3. BSS 세그먼트( Block Started By Symbol Segment )
bss세그먼트는 컴파일 시점에 값이 정해지지 않은 전역변수가 위치하는 메모리 영역입니다. 읽기 및 쓰기 권한이 부여되며 전역변수로 사용하기 위해 선언만 해두고 초기화를 해주지 않은 전역변수 등이 포함됩니다. 중요한 점은 이 세그먼트의 메모리 영역은 프로그램이 시작될 때, 모두 0으로 초기화가 됩니다. 따라서 C언어 코드를 작성할때 전역변수를 생성하고 초기화 해주지 않았다면 값이 0이 되게 됩니다.
#include <stdio.h>
int a;
int b;
int jalni;
// 초기화가 안된 전역변수들 = bss 세그먼트
int main(){ 어쩌구 return 0;}
4. 스택 세그먼트
스택 세그먼트는 함수의 인자나 지역 변수와 같은 임시 변수들이 실행중에 여기 저장됩니다. 스택은 스택 프레임이라는 단위로 사용됩니다. 스택 프레임은 함수가 호출될때 생성되고, 반환될때 해제됩니다. 바이너리 파일이 실행될때, 스택프레임을 얼마나 할당할지는 거의 계산이 불가능합니다. 따라서 운영체제는 프로세스를 시작할때 먼저 스택 프레임을 할당해주고 부족하면 확장해줍니다.
스택은 위로 쌓인다. 아래로 자란다. 라는 말들이 많은데 스택은 아래로 자란다고 표현합니다. 스택은 확장될 때, 기존 주소보다 낮은 주소로 확장되기 때문입니다. 스택은 cpu가 읽고 쓸 수 있어야 하기 때문에 읽기와 쓰기 권한이 부여됩니다.
#include <stdio.h>
int show_add(int a){
printf("number = %d", a);
return 0;
}
// 스택에 show_add의 ret주소와 인자가 쌓임
int main(){
int number = 30;
show_add(number);
return 0;
}
해당 c 코드를 한번 컴파일 해서 실행파일로 만들고 gdb라는 리눅스 디버거를통하여 어떤식으로 작동하는지 보기만 해보도록 하겠습니다. (나머지는 시스템 해킹에서 천천히 다루어 보겠습니다 )
5. 힙 세그먼트
힙 세그먼트는 c언어에서 malloc, calloc 등을 호출하여 메모리를 할당받아 사용할 경우 힙 세그먼트에 위치하게 됩니다. 스택가 마찬가지로 바이너리 파일 실행중에 동적으로 할당이 가능하며, 리눅스에서는 스택 세그먼트와 반대 방향으로 자랍니다. ( 위로 자랍니다 ) 0x0 → 0xffffffffffffffff 마찬가지로 읽기 권한과 쓰기 권한이 부여됩니다
#include <stiod.h>
int main() {
int *ptr_heap = malloc(sizeof(*heap_data_ptr));
// 힙 메모리 할당
*ptr_heap = 123;
// 할당받은 메모리에 값을 씀
printf("%d\n", *ptr_heap);
// 가져와서 출력
return 0;
}
여기까지 간단한 리눅스의 프로세스 메모리 구조를 알아보았습니다. 다음에는 레지스터에 대해서 알아볼 예정입니다.
시스템 해킹은 기본적인 CS 지식을 많이 요합니다. 제가 설명한 부분은 꼭 알고 계셔야 되는 개념이고, 더 공부하실분들은 더 공부하실 수록 나중에 많은 도움이 될 것 입니다.

"고생" 하세요
'System 관련 CS > 시스템 해킹을 위한 기초 지식' 카테고리의 다른 글
[ 시스템 106 ] pwngdb 사용법, gdb 명령어 정리 및 예시 (0) | 2023.08.01 |
---|---|
[ 시스템 105 ] 실행파일의 종류 (PE파일, ELF파일)를 알아보고 gdb, pwndbg 플러그인 설치해보자 (0) | 2023.08.01 |
[시스템 104] System Call (시스템 콜) (0) | 2022.09.07 |
[시스템 103] x86-64 어샘블리 기초지식 ( x86-64 assembly ) (0) | 2022.09.06 |
[시스템 102] 레지스터? x86-64 레지스터 분석 (0) | 2022.09.06 |