티스토리 뷰
#----------------------------------- Segmentation -----------------------------------#
Segmentation은 메모리에 영역을 분할하고 할당하는 작업이다. PE나 ELF 구조 파일의 경우 섹션을 나눌 경우 페이징에 맞춰 나누기 위해 헤더를 두고 헤더의 오프셋 값을 참조하여 배치하게 되지만 Segmentation의 경우 그렇지 않다. Segment Register를 참조하여 여기의 값을 오프셋으로 사용하여 메모리에 영역을 분할하고 할당한다.
이때, 실제 주소모드와 보호 모드에 따라 약간씩 달라진다.
실제 주소 모드의 경우 레지스터가 가리키는 값이 곧 물리 주소이기 때문에 레지스터의 값을 바로 계산하여 영역을 배치한다.
- Segment Register는 16비트의 크기를 가지지만 사실 20비트라고 한다....(왜...?) 그래서 16비트의 Segment Register의 값에 16을 곱해 20비트로 만든 후 범용 레지스터와 더하여 물리 주소로 사용한다.
- 끝...
세그먼트 |
오프셋 |
목적 |
코드 세그먼트(CS) |
IP |
실행되는 명령어 |
스택 세그먼트(SS) |
SP 또는 BP |
스택 주소 참조 |
데이터 세그먼트(DS) |
AX, BX, CX, DX 같은 범용 레지스터와 BX, DI, SI |
데이터 주소 참조 |
보조 세그먼트(ES) |
스트링 명령어를 위한 DI |
스트링의 목적지 주소 |
보호 모드는 실제 주소 모드의 몇 가지 문제점을 해결하기 위해 페이징 기법과 Segmentation을 둘 다 사용한다.
그리고 이름 그대로 메모리를 보호하기 때문에 할당된 메모리 주소를 넘어서 참조할 경우 예외 처리한다.
보호모드는 상위 13비트를 GDT(Global Descripter Table)의 인덱스로 사용한다. 여기에는 가상 메모리에 매핑되는 주소가 나열되어 있다. 나머지 3비트 중 1비트는 LDT(1) 또는 GDT(0)를 결정, 2비트는 특권 레벨(0 ~ 3)을 결정한다.
GDT에서 얻은 디스크립터 테이블은 64비트인데 여기에 base address가 반으로 나눠져 배치되어 있다.
이것을 합치면 Segment에 맞는 주소가 나오고 범용 레지스터를 Offset으로 사용하여 물리 주소로 만들어진다.
여기서 Segment(Code, Data, Stack)의 bass address와 limit가 Big Size의 경우 0x00000000 ~ 0xffffffff의 크기를 가지는데 Windows 메모리 배치에 맞지 않는다.
이것은 Windows에서 커널의 메모리에 대한 보호를 세그먼트 디스크립터를 통하여 하고 있지 않기 때문에 문제없다.
참고 사이트 :
세그먼테이션 과정에서 제일 중요한 작업은 접근하고 하는 메모리의 속성을 가지고 있는 세그먼트 디스크립터(Segment Descriptor)를 가져오는 것이다.
#----------------------------------- Segmentation(LDT & GDT) -----------------------------------#
GDT와 LDT의 경우 비슷하다... 차이점은 GDT는 Global, 전체 각 태스크의 세그먼트 디스크립터를 배치해둔 것이고,
LDT는 Local 태스크 당 독립적으로 존재하는 디스크립터 테이블을 LDT라고 부른다.
즉, GDT는 운영체제나 커널 모드를 의미하고 LDT는 프로세스와 유저 모드(소켓이나 파일 입출력? 같은거...)를 의미한다.
GDT애 존재하는 LDT의 세그먼트 디스크립터는 (DPL이 00, S bit가 0, Type이 0010)이 되어 LDT 디스크립터로 사용될 것을 나타낸다.
#----------------------------------- 기타 정리 -----------------------------------#
DS에 관한...
인텔 마이크로프로세서에서는 데이터 세그먼트가 현재의 실행 특권레벨보다 낮은 것은 모두 허용하기 때문에 애플리케이션 세그먼트가 커널레벨에서 사용되어도 무방
Windows에서는 유저레벨에서 커널레벨로의 진입 시 데이터 세그먼트에 대한 내용은 바꾸어 주지 않았기 때문
커널 레벨에서의 SS와 DS는 유사하지만 SS는 DPL 값이 0이라는 커널레벨의 특권은 가져야 한다.
데이터 세그먼트와 달리 스택 세그먼트는 현재 실행되고 있는 특권수준과 같은 특권레벨을 가져야 하기 때문이다.
FS는 본래 TIB(Thread Information Block, 다른 말로 TEB)를 지시하는데 사용하는데 여기에 예외 처리 정보가 포함되어 있음
그리고 FS[0x04]에는 Stack의 Base Address가 들어있음. 그리고 -4KByte 차이로 다음 스레드의 Base Address가...
커널레벨의 FS는 어플리케이션 레벨과는 많이 다르다. 커널레벨에서의 베이스 어드레스는 커널 영역의 주소를 가진다.
또, 커널레벨의 FS의 베이스 어드레스는 스레드마다 바뀌지 않는다.
게다가, 유저레벨처럼 스레드마다 독립적인 용도로 사용되어 지지 않고, 운영체제에서 현재 프로세서의 여러 가지 정보를 나타내기 위해
KPCR(Kernel's Processor Control Region)이라는 구조체를 만들어 사용한다.
따라서, 유저레벨에서 커널레벨로 제어 이행이 수행될 경우 반드시 FS 레지스터를 커널레벨의 세그먼트 값으로 바꿔주어야 함
안 바꿔주면 커널레벨의 FS가 아닌 유저레벨의 FS를 참조
참고 : Windows 구조와 원리(OS를 관통하는 프로그래밍의 원리), 정덕영 저
'System > Windows' 카테고리의 다른 글
Windows ROP (2) | 2016.02.12 |
---|---|
Windows SEH Overwrite (0) | 2016.02.11 |
Windows Stack OverFlow (0) | 2016.02.11 |
Windows Shellcode (0) | 2016.01.30 |
Windows 보안 기법 (0) | 2015.12.29 |