본문 바로가기

System/windows

PE 파일

1. PE

- PE파일은 윈도우 운영체제에서 사용되는 실행 파일 포맷이다. 윈도우 시스템에서 동작되는 대부분의 실행 파일은 PE파일.

윈도우 운영체제에서 사용되는 실행 파일(exe), DLL파일, object파일, 폰트 파일, sys파일 그리고 드라이버 파일 등을 위한 파일 형식이다. (컴파일의 결과물인 OBJ 파일을 제외한 모든 것은 실행 가능한 파일.)

종류 주요 확장자
실행 계열 EXE, SCR
드라이버 계열 SYS, VXD
라이브러리 계열 DLL, OCX, CPL, DRV
오브젝트 파일 계열 OBJ

 

PE구조는 DOS헤더, PE 헤더, 각 Section 헤더, 각 Section 테이블로 구성되어 있다.

- PE포맷은 정해진 포맷을 가짐. 이 정보들을 추출하여 언제 어떤 머신에서 어떤 컴파일러로 제작 되었는지를 확인할 수 있음.

- Section테이블: 데이터 존재. 들어있는 데이터에 대한 메타데이터가 각 Section헤더에 저장.

- Section 헤더: 파일이 실행되기 위해 필요한 모든 정보 존재.

[그림 1] CatchMind.exe파일

[그림 1] 설명
CatchMind.exe 파일을 PEView로 열면 PE Header부분을 확인할 수 있다. 해당 툴을 통해 PE파일의 구조를 분석할 수 있다. PE 파일의 Header내용을 확인할 수 있음.

 

[그림 2] CatchMind.exe파일이 메모리에 적재(loading or mapping)될 때의 모습

[그림 2] 설명

# PE Header = DOS header ~ Section header
# PE Body = 나머지 Section들 모두

파일에서는 offset으로, 메모리에서는 VA(virtual Address, 절대주소)로 위치를 표현하며 파일이 메모리에 로딩되면 모양이 달라진다. 파일의 내용은 보통 코드(.text), 데이터(.data), 리소스(.rsrc) 섹션에 나뉘어 저장됨.

→ 각 용도별로 여러 섹션이 나뉘어 저장

PE Header와 각 Section의 끝에 존재하는 NULL은 padding이다. 이는 최소기본단위를 사용하기 위해서 존재한다.

 

 

2. 각 헤더와 섹션의 역할

 [그림 1]의 좌측을 보면 CatchMind.exe파일의 PE 헤더와 PE 바디를 모두 확인할 수 있다. PE 헤더는 많은 구조체로 구성되어 있는데, 아래에 각 구조체와 구조체 멤버에 대해 간단히 설명할 것이다.

 

PE 헤더:: DOS 헤더

1) DOS_HEADER

[그림 3] DOS Header의 구조체 IMAGE_NT_HEADERS

PE파일이 제작되던 당시 DOS 파일이 널리 사용되고 있었기에, DOS 파일에 대한 하위 호환성을 제공하기 위해 만든 헤더이다. [그림 2]에 따라 PE Header의 제일 앞부분에 DOS Header가 존재한다. 기존의 DOS EXE Header를 확장한 IMAGE_NT_HEADERS 구조체를 확인할 수 있다. IMAGE_NT_HEADERS 구조체의 크기는 40이며, 주요 멤버 두 가지는 아래와 같다.

WORD    e_magic DOS signature ( [그림 1]의 첫 2byte 부분. 4D5A를 의미 )
LONG     e_lfanew NT header의 offset을 표시 (해당 위치에 NT header 구조체가 존재해야 함)

 

2) DOS Stub

[그림 4] NT Header의 구조체 IMAGE_NT_HEADER

DOS Header 아래에 존재. DOS Stub의 존재는 선택 사항이다. CatchMind.exe 파일을 DOS 환경에서 실행하거나, DOS전용 디버거를 이용해서 실행하면 이 부분의 코드를 실행시킬 수 있다.

 

 

3)NT Header

[그림 5] NT Header의 구조체 IMAGE_NT_HEADER

이 구조체의 멤버는 3가지로 구성되어 있다.

DOS Header의 마지막 멤버(e_lfanew)가 가리키는 위치에서부터 시작한다.

DWORD    Signature   // Signature PE.. 값을 가진다. 50450000(PE00)
IMAGE_FILE_HEADER     FileHeader    //구조체 멤버
IMAGE_NT_HEADER32    OptionalHeader    //구조체 멤버

 

[그림 6] NT HEADER의 IMAGE_FILE_HEADER

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

해당 구조체에서 Machine, NumberOfSections, SizeOfOptionalHeader, Characteristics 값이 정확하게 셋팅되어 있지 않으면 파일을 정상적으로 실행되지 않는다. 각 멤버에 대해 간단히 서술하면 다음과 같다.

Machine CPU별 고유한 값
NumberOfSections 코드, 데이터, 리소스 등 섹션의 개수. 반드시 0보다 커야한다. 정의된 섹션 수와 실제 섹션의 수가 다르다면 에러가 발생한다.
SizeOfOptionalHeader 이 멤버의 값은 IMAGE_NT_HEADER 구조체의 마지막 멤버인 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 나타낸다. 
윈도우의 PE Loader는 IMAGE_FILE_HEADER의 SizeOfOptionalHeader 값을 보고 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 인식한다. 
이 멤버에 구조체 크기를 명시하는 이유는, PE32+ 형태의 파일인 경우 IMAGE_OPTIONAL_HEADER32 구조체 대신에IMAGE_OPTIONAL_HEADER64 구조체를 사용하는데 이 두 구조체의 크기가 다르기 때문이다.
Characteristics 파일의 속성을 나타내는 값. 실행 가능한 형태(executable or not) 혹은 DLL파일인지 등의 정보가 존재함.

 

[그림 7] NT HEADER의 IMAGE_OPTIONAL_HEADER. 내용 이하 생략

IMAGE_OPTIONAL_HEADER32구조체는 PE 헤더 구조체 중 가장 크기가 크다. 주요 멤버는 9가지로, 이 값들이 잘못 셋팅되어 있다면 파일이 정상적으로 실행되지 않는다.

 

IMAGE_OPTIONAL_HEADER32 구조체 주요 멤버
Magoic 32bit 환경의 경우 10ㅠ, 64bit는 20B를 가진다.
AddressOfEntryPoint EP(Entry Point)의 프로세스 시작 위치 주소값(RVA)을 가짐
ImageBase PE 파일이 로딩되는 시작 주소.
메모리에 파일 load 후 EIP = ImageBase+AddressOfEntryPoint
SectionAlignment 메모리에서의 섹션 최소 단위
FileAlignment 파일에서의 섹션 최소단위
SizeOfImage PE파일이 메모리에 로딩되었을 때 가상메모리에서 Pe Image가 차지하는 크기
SizeOfHeader PE헤더의 전체 크기
Subsystem .sys인지 일반 실행 파일(.exe, .dll)인지 구분
NumberOfRvaAndSizes DataDiretory 배열의 개수. PE Loader는 해당 값을 보고 배열 크기 인식
DataDirectory IMAGE_DATA_DIRECTORY 구조체의 배열.

 

 

PE헤더: Section Header

섹션 헤더는 각 섹션의 속성을 정의한 것이다. 섹션 별로 IMAGE_SECTION_HEADER 구조체가 존재한다.

[그림 8] IMAGE_SECTION_HEADER

[표 3] IMAGE_SECTION_HEADER 구조체 주요 멤버
VirttualSize 메모리에서 섹션이 차지하는 크기
VirtualAdress 메모리에서 섹션의 시작 주소(RVA)
SizeOfRawData 파일에서 섹션이 차지하는 크기
PointerToRawData 파일에서 섹션의 시작 위치
Characteristics 섹션의 속성

이미지(image)
PE 파일이 메모리에 로딩될 때 파일이 그대로 올라가는 것이 아닌, Section Header에 정의된 대로 맞추어서 올라감.
- 파일에서의 PE와 메모리에서의 PE는 서로 다른 모양
- 이를 구별하기 위해 메모리에 로딩된 상태를 이미지(image)라고 함.

 

 

Section의 역할

섹션은 PE파일이 가상 주소 공간에 로드된 이후.. Code, DATA, resource 등 프로그램 실행에 필요한 정보가 위치하는 영역을 의미한다.

1) Code(“.text”): 프로그램의 실행 코드

2) DATA(".data")
- data: 전역변수, 정적변수 위치. 읽기 쓰기가 가능한 데이터 섹션.
- rdata: 상수형 변수, 문자열 상수 등이 위치. 읽기만 가능한 데이터 섹션
- bss: 초기화되지 않은 전역변수가 위치함.

3) RESOURCE(".rsrc"): icon, cursor 등의 Windows Application Resource 관련 데이터 위치.

 


PE파일 분석

PE파일을 분석할 때 내가 찾는 데이터 위치를 알기 위해서는 헤더 정보의 주소를 확인하면 된다.

주소의 종류는 크게 세 가지로 나뉜다.

pFile PE파일 내부에서의 오프셋 (파일)
PE파일이 물리적으로 하드디스크에 저장되었을 떄 의미있는 값.
RVA 메모리에 로드됐을 때, 기준값에서 얼마나 떨어져 있는지를 나타내는 상대 위치 (메모리)
PE파일이 메모리에 로드됐을 때 의미있는 값.
VA 가상 메모리상에 저장되는 실제 주소 (메모리)
PE파일이 메모리에 로드됐을 때 의미있는 값.

 

IAT(Impoart Address Table)

- PE 파일 안에 어떤 라이브러리의 어떤 함수를 가져다 쓰는가를 기록..

- 로더는 PE파일을 메모리로 로딩 시 IAT에 기록된 API이름을 참조해서 실제 주소를 찾아 API를 가리키는 주소를 적어둔다.