본문 바로가기
컴퓨터구조

[x32] PE File Format - PE Header, Section Header

by 배이즈 2025. 4. 6.
[리버싱 핵심원리] 책을 기반으로 작성되었습니다.

 

워게임을 풀다보니 PE 파일 구조에 대해서 좀 알아야겠다고 판단했다.

근데 너무 어렵고 지루하다.. 그치만 파이팅!!

PE파일 계열별 종류

  • 실행 : exe, scr
  • 라이브러리 : dll, ocx, cpl, drv
  • 드라이버 : sys, vxd
  • 오브젝트 : obj (컴파일 결과물)

→ 오브젝트 파일을 제외한 모든 것은 실행 가능한 파일이다.

 

dll, sys 등은 셸에서 직접 실행할 수는 없지만, 다른 형태의 방법(디버거 등)을 이용하여 실행이 가능한 파일이다.


기본 구조

파일이 메모리에 등록되면 섹션의 시작 위치와 크기가 조금 달라진다.

 

PE Header : DOS header ~ Section header

PE Body : 그 밑의 section

 

위치 표현 방식

  • 파일: Offset
  • 메모리: VA (Virtual Address, 절대 주소)

 

  • PE 헤더와 각 섹션의 끝에는 NULL padding 영역이 존재한다.
  • 메모리를 특정 크기에 맞추기 위해 빈 공간을 추가하는 것
    • 하드디스크에서 파일을 표현할 때 : 데이터의 묶음은 0x200(512byte)
    • 메모리를 표현할 때 : 데이터의 묶음은 0x1000 = VA
> 위에서 말한 '특정 크기' 가 무엇일까?
PE파일의 섹션들은 FileAlignment값(보통 512byte)에 맞춰서 정렬되는데 각 섹션의 크기는 이 값의 배수여야 한다. 그래서 부족한 부분을 NULL로 채워서 패딩하는 것이다.

VA, RVA

VA : 프로세스 가상 메모리의 절대주소

RVA (Relative Virtual Address) : 어느 기준 위치(imagebase)에서부터의 상대주소

→ VA + ImageBase = VA

> imagebase?
아래 Optional Header중 하나인데, 가상 메모리에서 PE 파일이 로딩되는 시작 주소를 나타낸다.
  • PE 헤더 내의 정보는 RVA 형태가 많다
    • PE 파일(DLL)이 프로세스 가상 메모리 특정 위치에 로딩되는 순간,
      이미 그 위치에 다른 DLL이 로딩되어 있을 수 있다.
    • 이런 경우 재배치(relocation)을 통해 빈 다른 위치에 로딩되어야 하는데,
      정보를 RVA로 해두면 재배치가 발생해도 기준위치에 대한 상대주소가 변하지 않으므로 문제 없이 접근가능!

 

cf) IMAGE_SECTION_HEADER의 멤버 VirtualAddress(RVA)와 가상메모리주소 VA는 용어만 같을 뿐 형식은 다르다.


이제 PE Header의 구성요소에 대해 알아보자.

DOS Header

기존 DOS EXE Header를 확장시킨 구조체가 존재한다.

typedef struct _IMAGE_DOS_HEADER {
	WORD e_magic;
	. 
	.
	LONG e_lfanew;
	} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

 

IMAGE_DOS_HEADER 구조체의 크기는 40

중요 멤버

  • e_magic : DOS signature (4D5A = MZ)
  • e_lfanew : NT header의 offset 표시 (파일에 따라 가변적임)

PE 스펙에 맞게 파일 시작 2바이트는 4D5A 이고, e_lfanew 값은 000000E0 이다. (리틀엔디언)


DOS Stub

 

옵션때문에 존재하며, 크기도 일정하지 않다. 없어도 실행에 문제 없다!

코드와 데이터의 혼합으로 이루어져 있다.

 

  • offset 40~4D 영역
    • 16비트 어셈블리 명령어 → 32비트에선 이쪽 명령어가 실행X
    • DOS환경이나 DOS용 디버거를 이용하면 저걸 실행함(이들은 PE 파일 포맷을 모름)
      • DOS환경에서 실행하면 “This program cannot be run in DOS mode”를 출력 후 종료

NT Header (IMAGE_NT_HEADERS)

위 DOS Header의 e_lfanew 에서 언급한 부분이다.

typedef struct _IMAGE_NT_HEADERS {
	DWORD **Signature**;
	IMAGE_FILE_HEADER **FileHeader**;
	IMAGE_OPTIONAL_HEADER32 **OptionalHeader**;
} IMAGE_NT_HEADER32, *PIMAGE_NT_HEADERS32;

 

IMAGE_NT_HEADERS 구조체의 크기는 F8이고, 1개의 멤버, 2개의 구조체로 구성된다.

 

  1. Signature : 5045000h (”PE”00) 값을 가진다.
  2. FileHeader
  3. Optional Header

NT Header - FileHeader (IMAGE_FILE_HEADER)

IMAGE_FILE_HEADER : 파일의 개략적인 속성을 나타내는 구조체

typedef struct _IMAGE_FILE_HEADER {
  WORD  Machine;
  WORD  NumberOfSections;
  DWORD TimeDateStamp;
  DWORD PointerToSymbolTable;
  DWORD NumberOfSymbols;
  WORD  SizeOfOptionalHeader;
  WORD  Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

 

1. Machine

  • CPU별로 고유한 값
  • 32bit Intel x86 : 14C (4C 01)

 

2. NumberOfSections

  • PE파일은 코드, 데이터, 리소스 등이 각각의 Section에 나뉘어 저장된다.
    바로 그 Section의 개수를 나타내는 멤버이다.
  • 반드시 0보다 커야 한다.
  • 정의된 섹션 개수와 실제 섹션이 다르면 실행 에러가 발생한다.

 

3. SizeOfOptionalHeader

  • IMAGE_NT_HEADERS 구조체의 마지막 멤버인 IMAGE_OPTIONAL_HEADERS32 구조체의 크기를 나타낸다.
  • 위 구조체는 C언어의 구조체이므로 이미 크기가 결정되어 있다.
    그러나 Windows의 PE로더는 IMAGE_FILE_HEADER의 SizeOfOptionalHeader 값을 보고 IMAGE_OPTIONAL_HEADERS32의 크기를 인식한다.
    • cf) e_lfanew + SizeOfOptionalHeader = 꽈배기 PE (PE Patch) 만들 수 있다
> 꽈배기 PE 만드는 방법?
1. e_lfanew 조작
e_lfanew 값을 원래 PE Header 위치가 아닌 다른 곳을 가리키게 변경할 수 있다.
예를 들어, PE Header를 파일 끝부분으로 옮기고 e_lfanew 값을 수정하면,
정상적인 분석 도구에서는 PE Header가 없는 것처럼 보이게 할 수 있다.

2. SizeOfOptionalHeader 조작
SizeOfOptionalHeader 값을 조작하여 PE Header 크기를 비정상적으로 만들어서,
로더가 정상적으로 로드하지만 분석 도구는 잘못된 정보를 읽게 만들 수 있다.

> 꽈배기 PE의 효과는?
역공학 방해
: IDA, PE-bear 같은 도구에서 정상적인 PE 구조가 아닌 것처럼 보이게 할 수 있다.
패커 우회
: 일부 패커 탐지 및 디컴파일러를 우회할 수도 있다.
기타 분석 방해 기법과 결합 
: Import Table 교란, 섹션 테이블 변조 등과 함께 쓰일 수 있다.

 

4. Characteristics

  • 파일의 속성을 나타내는 값
    - 실행 가능한 형태인지 / DLL 파일인지 / 시스템 파일인지 등의 정보들이 bit OR 형식으로 조합된다.
  • 0002h가 없는 경우(not executable) : *.obj / resource DLL

 

5. TimeDateStamp

  • 파일 실행에 영향X
  • 해당 파일의 빌드 시간을 나타낸 값
  • 개발 도구에 따라 세팅 여부가 다름

notepad.exe의 IMAGE_FILE_HEADER


NT Header - Optional Header (IMAGE_OPTIONAL_HEADER32)

1. Magic

  • 이미지 파일의 상태
    • IMAGE_OPTIONAL_HEADER32 : 10B
    • IMAGE_OPTIONAL_HEADER64 : 20B

 

2. AddressOfEntryPoint

  • 이 값이야말로 프로그램에서 최초로 실행되는 코드의 시작 주소이다.
  • EP(Entry Point)의 RVA(Relative Virtual Address) 값을 가진다.

 

3. ImageBase

  • 0~FFFFFFFF 범위의 광활한 프로세스의 가상 메모리에서 PE 파일이 로딩되는 시작 주소를 나타냄

 

<파일 유형 별 로딩 위치>

  • EXE, DLL 파일 : user memory 영역인 0~7FFFFFFF 범위에 로딩됨
  • SYS 파일 : kernel memory 영역인 80000000~FFFFFFFF 범위에 로딩됨

- 일반적으로 EXE 파일의 ImageBase값은 00400000, DLL 파일은 10000000 이다.

 

PE 로더는 PE 파일을 실행시키기 위해

  • 프로세스 생성 → 파일을 메모리에 로딩 → EIP 값을 ImageBase+AddressOfEntryPoint 값으로 세팅

 

4. SectionAlignment, FileAlignment

  • SectionAlignment : 메모리에서 섹션의 최소단위
  • FileAlignment : 파일에서 섹션의 최소단위

- 하나의 파일에서 이 두 값은 같을 수도 있고 다를 수도 있다.

- 파일/메모리의 섹션 크기는 반드시 각각 Alignment의 배수가 되어야 한다. (padding)

 

5. SizeOfImage

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

- 일반적으로 파일의 크기와 메모리에 로딩된 크기는 다르다.

 

6. SizeOfHeader

PE 헤더의 전체 크기를 나타낸다.

- FileAlignment의 배수여야 한다.

- 파일 시작에서 SizeOfHeader Offset만큼 떨어진 위치에 1번째 섹션이 위치한다.

 

7. Subsystem

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

 

8. NumberOfRvaAndSizes

이 구조체의 마지막 멤버인 DataDirectory 배열의 개수(실제로 사용되는 디렉터리 개수)

 

구조체 정의에 배열 개수가 IMAGE_NUMBEROF_DIRECTORY_ENTRIES(16)라고 명시되어 있으나

PE Loader는 이 값을 보고 배열의 크기를 인식한다.

→ 즉 16이 아닐 수도 있다는 뜻

 

(일반적으로 PE 파일에서는 16개의 데이터 디렉터리가 존재하지만, 실제로 사용되는 디렉터리의 개수는 다를 수도 있다)

 

9. DataDirectory

IMAGE_DATA_DIRECTORY 구조체의 배열 (Directory = 어떤 구조체의 배열)

배열의 각 항목마다 정의된 값을 가진다.

 

notepad.exe 의 IMAGE_OPTIONAL_HEADER

 


PE Header 구조도


Section 헤더

code, data, resource 각 섹션의 속성(property)을 정의한 것이다.

IMAGE_SECTION_HEADER

VirtualAddress ← SectionAlignment(메모리에서 섹션 최소 단위)

PointerToRawData ← FileAlignment(파일에서 섹션 최소 단위)

 

파일에서의 섹션 크기 ≠ 메모리에 로딩된 섹션 크기

VirtualSize ≠ SizeOfRawData

 

  • Characteristics

이 값들의 조합(bit OR) 연산으로 이루어진다.

 

  • Name

PE스펙에는 섹션 Name에 대한 명시적 규칙이 없다.

→ 어떠한 값을 넣어도 되고 NULL로 채워도 무관

→ 그저 참고용일 뿐 어떤 정보로써 활용하기에는 장담X (이름만 보고 판단 금지!)

IMAGE_NT_HEADER 에 있는 Data Directory 의 값을 참조하자

.text
컴파일 후의 결과, 즉 실행되는 기계어 코드가 저장되는 섹션이다.
.data
초기화된 전역 변수를 가진다.
.rdata
문자열 상수, const로 선언된 변수처럼 읽기만 가능한 읽기 전용 데이터 섹션이다.
.bss
초기화되지 않은 전역 변수의 섹션이다.
.edata
EAT와 관련된 정보가 들어가 있는 섹션이다.
.idata
IAT와 관련된 정보가 들어가 있는 섹션이다.
.rsrc
리소스가 저장되는 섹션이다.

notepad.exe의 섹션 헤더

 

최근댓글

최근글

skin by © 2024 ttuttak