레지스터, 프로세스 메모리 구조, 섹션, 스택, 힙
플래그 레지스터
프로세서의 현재 상태를 저장하고 있는 레지스터
x64 아키텍처에서는 RFLAGS라고 불리는 64비트 크기의 플래그 레지스터가 존재함
- CF carry flag: 부호 없는 수의 연산 결과가 비트의 범위를 넘을 경우 설정
- ZF zero flag: 연산의 결과가 0일 경우 설정
- SF sign flag: 연산의 결과가 음수일 경우 설정
- OF overflow flag: 부호 이쓴 수의 연산 결과가 비트 범위를 넘을 경우 설정
프로세스 메모리 구조
- 섹션
윈도우 PE파일은 PE헤더와 1개 이상의 섹션으로 구성됨
섹션: 유사한 용도로 사용되는 데이터가 모여있는 영역
섹션에 대한 정보는 PE 헤더에 적혀있음. 섹션의 이름, 섹션의 크기, 섹션이 로드될 주소의 오프셋, 섹션의 속성과 권한
윈도우는 PE를 실행할 때 이 정보를 참조하여 PE의 각 섹션들을 가상 메모리의 적절한 세그먼트에 매핑
- .text
실행 가능한 기계코드가 위치한 영역
프로그램이 동작하려면 코드를 실행할 수 있어야 하므로 .text 세그먼트에는 읽기 권한과 실행 권한이 부여됨
반면, 쓰기 권한이 있으면 공격자가 악의적인 코드를 삽입하기 쉬워지므로 쓰기 권한은 보통 제거하는 편
ex) 정수 31337을 반환하는 main함수가 컴파일되면 554889e5b8697a00005dc3 라는 기계코드로 변환되는데 이 기계코드가 코드 세그먼트에 위치하게 됨
int main() { return 31337; }
- .data
컴파일 시점에 값이 정해진 전역변수가 위치. CPU가 이 섹션의 데이터를 읽고 쓸 수 있어야 하므로 읽기/쓰기 권한이 부여됨
ex) .data 섹션에 포함되는 여러 데이터의 유형
int data_num = 31337;
char data_rwstr[] = "writable_data"; // data
int main() { ... }
- .rdata
컴파일 시점에 값이 정해진 전역 상수와 참조할 DLL 및 외부 함수들의 정보가 저장됨
CPU가 이 섹션의 데이터를 읽을 수 있어야 하므로 읽기 권한은 부여되지만 쓰기는 불가능
ex) .rdata 섹션에 포함되는 여러 데이터의 유형. 주의 깊게 살펴봐야할 변수는 str_ptr. str_ptr은 “readonly”라는 문자열을 가리키고 있는데, str_ptr은 전역 변수로서 .data에 위치하지만, “readonly”는 상수 문자열로 취급되어 .rdata에 위치함.
const char data_rostr[] = "readonly_data";
char *str_ptr = "readonly"; // str_ptr은 .data, 문자열은 .rdata
int main() { ... }
섹션이 아닌 메모리
윈도우의 가상 메모리 공간에는 섹션만 로드되는 것이 아님. 프로그램 실행에 있어 필요한 스택과 힙도 가상 메모리 공간에 적재됨
- 스택
지역변수나 함수의 리턴주소가 저장됨
스택은 상위 주소 → 하위 주소로 자람
- 힙
프로그램이 여러 용도로 사용하기 위해 할당받는 공간. 모든 종류의 데이터가 저장될 수 있음
스택과 다른 점: 더 큰 데이터를 저장할 수 있고 전역적으로 접근 가능하도록 설계됨. 실행 중 동적으로 할당 받는 것도 차이점이 됨
ex) malloc(), calloc() 등으로 할당받은 메모리