ws1004 2019. 3. 25. 17:58

PE Format 이란?

PE(Portable Executable) 파일을 그대로 해석해 보자면, 옮겨 다니면서 실행시킬수 있는 파일을 뜻하는데, 이는 MS 뿐만 아니라 다른 운영체제와 이식성을 좋게 만들기 위해서 사용합니다.
또한, windows 운영체제에서 사용되는 실행 파일 형식입니다.

PE파일은 32비트 형태의 실행 파일을 의미하며 PE32라는 용어를 사용합니다.
하지만 32비트가 아닌 64비트는 PE+ 또는 PE32+ 라고 이야기 하며, PE의 확장 형태 입니다.
[PE64라고 생각할 때도 있지만 실수 하시면 안됩니다.]


PE File 종류?


PE File의 기본 구조

PE File의 기본 구조는 다음과 같습니다.
( 메모리에 적재(loading 또는 mapping)됬을때의 모습 )
섹션의 시작 위치는 각 파일/메모리의 최소 기본 단위의 배수에 위치를 하며, 빈 공간은 위의 사진과 같이 NULL값으로 채워집니다.

Null값이 있는 이유
이 Null을 padding이라고 불리기도 합니다.

위의 사진 처럼
네모 체크한 부분이 시작 위치 인데 이 위치는 최소단위의 배수 라는 것 입니다.

VA(Virtual Address, 절대 주소)는 프로세스 가상 메모리의 절대 주소!
RVA(Relative Virtual Address, 상대 주소)는 어느 기준 위치, 즉, ImageBase에서 부터의 상대주소를 이야기 합니다.

VA = RVA + ImageBase 라는 식이 성립 합니다.

PE가 메모리에 적재되기 전에 기본 ImageBase는 0의 값을 가지고 있다.
그리고 나서 PE안에 상대주소인 RVA가 적히게 되는 것 입니다.

메모리에 적재가 됬을때 상대 주소(RVA)가 들어가는 이유는 절대 주소(VA)는 가상의 주소이기 때문에 Relocation(재배치) 되기 어렵기 때문에 PE 헤더 정보들이 절대 주소(VA)로 되어있으면 정상적으로 엑세스가 이루어 지지 않습니다.

그래서 변하지 않는 상대주소(RVA)를 사용하는 것 입니다.

가상 주소인 VA는 0x00000000 ~ 0xFFFFFFFF 의 범위 입니다. 
프로그램은 4byte 메모리를 사용합니다.

PE Header는 구조체로 이루어져 있으며 DOS header ~ Section header 인것 으로 보입니다.

1.DOS header

DOS EXE Header를 확장시킨 IMAGE_DOS_HEADER 구조체가 존재 합니다.

IMAGE_DOS_HEADER 구조체의 크기는 40 입니다.

해당 _IMAGE_DOS_HEADER 구조체 에서 중요한 멤버는 e_magic과 e_lfanew 멤버 입니다.

e_magic : DOS signature (4D5A => ASCII 값 "MZ")
e_lfanew : NT header의 옵셋을  표시(파일에 따라 가변적인 값을 가짐)

모든 PE파일은 시작부분에 DOS signature("MZ")가 존재 합니다. 
또한 e_lfanew값이 가르키는 위치에 NT header가 존재 해야 합니다.

NT header의 구조체는 IMAGE_NT_HEADERS입니다.

notepad.exe 파일을 열어보면 e_magic 값으로 4D 5A ("MZ") 가 있고 e_lfanew 값으로 00 00 00 E0 이 들어 있습니다. ( E0000000가 아닙니다. 리틀 엔디언으로 읽어야 하기 때문에 )

*Intel 계열의 CPU는 자료를 역순으로 저장합니다.

2.DOS Stub

DOS Header 밑에 바로 존재하는 DOS Stub입니다.
DOS Stub는 없어도 파일 실행에는 문제가 없습니다.

notepad.exe에서 확인해 보겠습니다.

DOS 환경에서는 명령어가 16비트 형태입니다.

사진에 문자열 This program cannot be run in DOS mode를 보시면 DOS mode에서 이 프로그램을 실행하면 다음과 같은 문자열을 출력하고 종료하는것 같습니다.


3.NT Header

NT header의 구조체는 IMAGE_NT_HEADERS입니다.


IMAGE_NT_HEADERS 구조체는 3개의 멤버로 구성 되어 있습니다.

IMAGE_NT_HEADERS 구조체의 첫 멤버인 Signature는 다음의 사진에서 확인할 수 있습니다.


IMAGE_NT_HEADERS 구조체의 크기는 F8입니다 
나머지 2개 멤버인 FileHeader 와 OptionalHeader 구조체를 하나하나 설명하겠습니다.

4.NT Header - File Header

파일의 개략적인 속성을 나타내는 IMAGE_FILE_HEADER 구조체 입니다.


IMAGE_FILE_HEADER 구조체 에서 가장 중요한 멤버는 4개 입니다.

4-1.NT Header - File Header - Machine

Machine 넘버는 CPU별로 고유한 값이고, 32비트 Intel x86호환 칩은 14C의 값을 가집니다.


4-2.NT Header - File Header - NumberOfSections

PE 파일은 코드, 데이터, 리소스 등으로 각각 섹션이 나뉘어서 저장됩니다.

그 각각 저장 되는 섹션의 수를 담아 놓은 구조체가 이 NumberOfSections 구조체 입니다.

이값은 반드시 0보다 커야 하며, 정의된 섹션의 개수와 실제 섹션의 개수가 다르면 실행 에러가 발생합니다.

4-3.NT Header - File Header - SizeOfOptionalHeader

IMAGE_NT_HEADERS 구조체의 마지막 멤버는 IMAGE_OPTIONAL_HEADER32 구조체입니다. 

SizeOfOptionalHeader 멤버가 위의 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 나타냅니다.

Windows의 PE로더는 SizeOfOptionalHeader의 크기를 보고 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 인식하게 됩니다.

4-4.NT Header - File Header - Characteristics

파일의 속성값을 나타내는 멤버로서 실행가능한 형태인지(executable or not) 혹은 DLL파일인지 등의 정보들이 bit OR 형식으로 조합 됩니다.

아래의 정의는 winnt.h파일에 정의된 Characteristics의 값입니다.
(0002h 와 2000h의 값이 중요합니다. 기억해 두기로 합시다.)


5.NT Header - Optional Header

PE 헤더 구조체중에서 가장 크기가 큰 구조체인 IMAGE_OPTIONAL_HEADER32입니다.

화살표 표시한 9가지가 IMAGE_OPTIONAL_HEADER32 구조체에서 중요한 멤버입니다.

5-1.NT Header - Optional Header - Magic

Magic 넘버는 IMAGE_OPTIONAL_HEADER32 구조체의 경우 10B, IMAGE_OPTIONAL_HEADER64 구조체인 경우 20B의 값을 가지게 됩니다.

5-2.NT Header - Optional Header - AddressOfEntryPoint

AddressOfEntryPoint는 EP의 RVA값을 가지고 있습니다. 
프로그램에서 최초로 실행되는 코드의 시작 주소로 매우중요한 값을 가지고 있는 멤버입니다.

5-3.NT Header - Optional Header - ImageBase

PE파일이 맵핑 되는 시작 주소를 가리킵니다.

EXE, DLL 파일은 user memory 영역인 0~7FFFFFFF 범위에 로딩되고, SYS 파일은 kernel memory 영역인 80000000~FFFFFFFF 범위에 로딩되게 됩니다.

PE로더는 PE파일을 실행시키기 위해 프로세스를 생성하고 파일을 메모리에 로딩한 후 EIP 레지스터 값을 ImageBase + AddressOfEntryPoint 값으로 세팅합니다.

5-4.NT Header - Optional Header - SectionAlignment, FileAlignment

PE파일의 Body 부분은 섹션으로 나뉘어져 있습니다.
파일에서 섹션의 최소단위를 나타낸 것이 FileAlignment이고, 메모리에서 섹션의 최소단위를 나타낸 것이 SectionAlignment입니다.

파일/메모리의 섹션의 크기는 반드시 각각 FileAlignment/SectionAlignment의 배수가 되어야 합니다.

5-5.NT Header - Optional Header - SizeOfImage

PE 파일이 메모리에 로딩 되었을때 가상 메모리의 PE Image가 차지하는 크기를 나타냅니다.

5-6.NT Header - Optional Header - SizeOfHeader

PE헤더의 전체 크기를 이야기 합니다. 

이값은 FileAlignment의 배수 여야 합니다.
파일의 시작으로 부터 SizeOfHeader 옵셋 만큼 떨어진 위치에 첫 번째 섹션이 위치 합니다.

5-7.NT Header - Optional Header - Subsystem

Subsystem의 값을 보고 시스템 드라이버 파일(*.sys)인지, 일반 실행 파일(*.exe, *.dll)인지 구분할수 있습니다.

의미
비고
1
Driver file (*.sys)
시스템 드라이버(예 : ntfs.sys)
2
GUI 파일(*.exe)
창 기반 애플리케이션(예 : notepad.exe)
3
CUI 파일 (*.exe)
콘솔 기반 애플리케이션(예 : cmd.exe)

5-8.NT Header - Optional Header - NumberOfRvaAndSizes

IMAGE_OPTIONAL_HEADER32 구조체의 마지막 멤버인 DataDirectory 배열의 개수를 나타냅니다.

5-9.NT Header - Optional Header - DataDirectory

IMAGE_DATA_DIRECTORY 구조체 배열로 배열의 각 항목마다 정의된 값을 가집니다.


EXPORT, IMPORT, RESOURCE, TLS Directory를 잘 기억해 둡시다.

IMAGE_OPTIONAL_HEADER

notepad.exe 에서의 IMAGE_OPTIONAL_HEADER 구조체 전체를 확인해 보겠습니다.

↑ IMAGE_OPTIONAL_HEADER 부분

6.섹션 헤더

긱 세션의 속성을 정의한것이 섹션 헤더 입니다.

PE파일을 여러개의 섹션 구조로 만들었을때 가장 큰 장점은 프로그램의 안정성 입니다.
code와 data가 하나의 섹션으로 되어있고 서로 뒤죽 박죽 섞여 있다면, 그 복잡함은 안정성에 문제가 생길수 있습니다.

종류
액세스 권한
code
실행, 읽기 권한
data
비실행, 읽기, 쓰기 권한
resourse
비실행, 읽기 권한

IMAGE_SECTION_HEADER

섹션 헤더의 IMAGE_SECTION_HEADER 구조체를 확인해 보겠습니다.

자주 사용하는 중요 멤버는 다음과 같습니다.

항목
의미
VirtualSize
메모리에서 섹션이 차지하는 크기
VirtualAddress
메모리에서 섹션의 시작 주소 (RVA)
SizeOfRawData
파일에서 섹션이 차지하는 크기
PointerToRawData
파일에서 섹션의 시작 위치
Characteristics
섹션의 속성(bit OR)