1. 개요
Portable Executable, PEMicrosoft Windows의 실행 파일(EXE) 및 모듈 파일(DLL)의 바이너리 포맷이다. 참고로 32비트와 64비트용 PE 파일은 서로 다른 구조체를 사용하는데 32비트용 PE 파일을 PE32이라고 부르고 64비트용 PE 파일은 PE+, PE32+이라고 부른다.
2. 구조
PE 파일의 구조는 다음과 같다.2.1. DOS Header 및 Stub
가장 맨 위에 위치한 영역으로 DOS 환경 헤더 및 실행 코드를 포함한다. MAGIC 값은 MZ이다. DOS 헤더는 DOS 환경에서 프로그램이 실행하는데 필요한 정보가 담겨있으며 SDK에 정의된 구조체는 IMAGE_DOS_HEADER이다.이 영역은 윈도우에서 사용하지 않으며 MS-DOS 같은 DOS 환경에서 프로그램을 실행하면 사용되는 영역이다. 보통은 "This program cannot be run in DOS mode."라는 메시지만 출력하고 끝난다. 이는 Windows의 실행 파일은 DOS와 동일하게 EXE 확장자로 되어 있으므로 혹시라도 Windows용 실행 파일을 DOS 환경에서 실행할 경우를 대비하여 삽입된 코드다.
2.2. NT Header
DOS Header 및 Stub 다음으로 위치한 영역으로 윈도우에서 실제로 사용한다. SDK에 정의된 구조체는 IMAGE_NT_HEADERS32 (32비트 PE 파일) 또는 IMAGE_NT_HEADERS64 (64비트 PE 파일)이다. IMAGE_NT_HEADERS도 정의되어 있지만 타켓 비트에 따라 32비트로 컴파일한다면 IMAGE_NT_HEADERS32, 64비트로 컴파일하면 IMAGE_NT_HEADERS64로 변환된다.NT Header에는 Signature, File Header, Optional Header가 포함되어 있다. Signature는 PE로 고정되고 File Header와 Optional Header는 다음과 같은 정보가 포함되어 있다.
- File Header (IMAGE_FILE_HEADER) - 타켓 아키텍처 유형, 섹션 수, 타임스탬프, COFF 기호 테이블, 기호 테이블의 기호 수, Optional Header의 크기 등이 포함되어 있다.
- Optional Header (IMAGE_OPTIONAL_HEADER32 또는 IMAGE_OPTIONAL_HEADER64) - 이미지가 로드될 주소(ImageBase), 이미지 버전, Entry Point, 서브시스템 유형 등이 포함되어 있다. Optional Header는 32비트와 64비트로 나뉘어져 있다.
여기서 서브시스템은 실행 환경을 말하는데 다음과 같다.
이름 | 값 | 설명 |
IMAGE_SUBSYSTEM_UNKNOWN | 0 | 알 수 없는 서브시스템 |
IMAGE_SUBSYSTEM_NATIVE | 1 | 서브시스템을 사용하지 않음[1]. 윈도우 내에서 실행할 수 없는 프로그램이다. |
IMAGE_SUBSYSTEM_WINDOWS_GUI | 2 | 윈도우 GUI 프로그램 |
IMAGE_SUBSYSTEM_WINDOWS_CUI | 3 | 윈도우 CUI 프로그램 |
IMAGE_SUBSYSTEM_OS2_CUI | 5 | OS/2 프로그램 |
IMAGE_SUBSYSTEM_POSIX_CUI | 7 | POSIX 프로그램 |
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI | 9 | Windows CE GUI 프로그램 |
IMAGE_SUBSYSTEM_XBOX | 14 | Xbox 프로그램 |
ImageBase는 PE 이미지가 로드될 가상 메모리 주소로 기본적으로 EXE 파일은 0x00400000, DLL 파일은 0x10000000, 드라이버(SYS) 파일은 0x00010000로 설정되어 있다. 물론 이 값은 개발자가 직접 바꿀수도 있다. 참고로 시스템 DLL(ntdll.dll, kernel32.dll, user32.dll 등)들은 특정 메모리 주소에만 로드되도록 ImageBase 값이 설정되어 있다.
만약에 로드될 메모리 주소에 이미 다른 PE 이미지가 로드되어 있다면 재배치 작업을 통해 다른 메모리 주소에 로드된다. 따라서 PE 이미지가 어느 메모리 주소에 로드될 지 알 수 없기 때문에 헤더 상의 Entry Point 등은 절대적 주소가 아닌 상대적 주소(RVA)로 되어 있다. 예로 들면 main 함수의 주소를 구해서 호출하려고 한다면 (현재 PE 이미지가 로드되어 있는 메모리 주소) + (헤더 상의 상대적 Entry Point 주소)로 하여 호출하게 된다.
2.3. Section Header
NT Header 다음으로 위치한 영역으로 섹션에 대한 정보들을 담고 있다. 섹션 수 만큼 존재한다. SDK에 정의된 구조체는 IMAGE_SECTION_HEADER이다. Section Header들 다음 영역은 각 섹션의 영역이다.이 헤더에는 섹션 이름, 섹션 시작지점, 섹션 크기 등이 포함되어 있다. 흔히 존재하는 섹션 정보는 다음과 같다.
섹션 이름 | 설명 |
.text | 프로그램 코드가 들어가 있는 영역이다. |
.data | 전역/static 변수가 들어가 있는 영역이다. |
.rdata | 읽기 전용(const) 변수가 들어가 있는 영역이다. |
.rsrc | 리소스 데이터(사진, 음악 등)가 들어가 있는 영역이다. |
PAGE | 커널 및 드라이버 전용, 사용되지 않으면 운영체제에 의해 페이징 파일로 옮겨질수도 있는 영역이다. 오직 함수만 들어갈 수 있다. |
INIT | 커널 및 드라이버 전용, 이 영역의 함수는 한번 호출되면 운영체제는 해당 함수의 코드를 메모리에서 제거한다. 초기화 함수와 같은 한번만 호출하는 함수에 쓰인다. |