[Digital Forensic] PE 구조 분석

 

PE 파일 구조

 

포렌식에서 메모리 분석을 통해 파일을 카빙한다거나 하드디스크 이미지에서 파일을 카빙하여 해당 파일이 무엇인지 분석하려고 할 때 꼭 필요한 지식이기에 PE 구조를 상세하게 공부해 볼 것이다.

 

일반적으로 포렌식에서 파일을 분석할 경우는 악성코드로 의심되는 파일을 분석하는 경우로 정적 분석과 동적 분석을 하게 된다.

 

리버스 엔지니어링 같은 경우에는 정적 분석에 속하며, 정적 분석을 할 경우 어셈블리어와 메모리 구조 등에 대한 지식도 있어야 하지만, PE 구조에 대한 지식도 있어야 하기에 이렇게 상세히 PE 구조를 알아보려고 한다.

 

PE(Portable Executable) 파일 포맷은 윈도우에 해당하는 파일 포맷으로 .exe, .dll, .cpl, .sys, .scr, .drv, .vxd, .ocx 확장자를 가진 파일들에게서 볼 수 있다.

 

PE 구조를 보다 보면 IMAGE로 시작하는 구조체들을 많이 보게 되며, 이는 윈도우에서 실행 파일을 IMAGE라는 명칭으로 대신하기 때문에 구조체 이름도 그렇게 지은 것이다.


PE 구조는 다음 그림과 같다.

 

[그림 1] PE 구조 도식화


(1) DOS 영역

  • 윈도우 실행 파일을 DOS 모드에서 실행하려고 할 때, 사용자에게 에러 메시지를 보여주기 위해 존재

  • 실제로 이 영역은 윈도우 실행 파일이 윈도우에서 실행할 때에는 아무런 영향을 주지 않는 부분이어서 볼만한 내용이 많지 않음

  • DOS MZ Header와 DOS Stub Program 영역으로 이루어짐

 

 

1-1. DOS MZ Header

  • WinNT.h 헤더 파일에 정의되어 있음

  • 눈 여겨 볼 구조체 멤버는 e_magic과 e_lfanew 구조체 멤버

  • 다른 구조체 멤버들은 사용하지 않거나 PE 파일 포맷 내에서는 사실상 의미가 없음

 

e_magic

  • 이 구조체 멤버는 DOS MZ Header의 맨 앞(PE 파일 맨 앞)을 나타내며 16진수는 '4D 5A' 값을 가짐

  • 아스키코드로 변환하면 MZ라는 값을 가짐

  • MZ는 도스를 최초로 설계한 사람 중 한 명인 Mark Zbikowksi의 이니셜

  • 모든 윈도우의 실행 파일은 MZ로 시작하기 때문에 이 값을 검색해 실행 파일을 찾을 수도 있음

 

e_lfanew

  • 이 구조체 멤버는 PE 파일의 머리 격인 IMAGE_NT_HEADER를 가리키는 오프셋 값을 가짐

 

DOS MZ Header의 구조체 구조는 다음과 같다.

 

[그림 2] DOS MZ Header의 구조체 구조

 

 

다음 그림은 notepad.exe의 DOS MZ Header 영역을 보여준다.

 

[그림 3] DOS MZ Header 영역

 

  • 4D 5A : e_magic

  • F0 00 00 00 : e_lfanew (E0 00 00 00)

이 두 값이 올바르면 프로그램은 실행하는데 문제가 없다.

 

 

1-2) Dos Stub Program

  • 사용자가 DOS 모드에서 윈도우용 실행 파일을 실행하였을 때 문구를 출력해주기 위한 코드가 있는 부분

 

출력하는 문구는 다음과 같다.

 

'This Program cannot be run is DOS mode'

 

 

이 부분은 오프셋 0x40 ~ 0x7F까지이다.

 

[그림 4] DOS Stub Program 부분


(2) PE Header 영역

  • PE 파일의 시작 부분

  • PE File Signature, PE File Header, PE File Optional Header 영역이 있음

  • IMAGE_NT_HEADERS 구조체로 이루어짐

[그림 5] IMAGE_NT_HEADERS 구조체 정보

 

위쪽에 정의된 구조체는 32bit 구조체이며 아래는 64bit 구조체이다.

 

어느 정도 직관적인 구조체 멤버 이름을 통해서 왜 PE 헤더 영역이 3가지로 나누여졌는지 알 수 있다.

 

 

2-1. PE File Signature

  • DOS의 e_magic 구조체 멤버처럼 PE 영역의 시작을 알리는 값을 가지고 있는 영역

  • 모든 파일에서 그 값은 항상 동일

  • 그 값은 'PE/0/0(0x50)/0x45/0x00/0x00)'

  • 메모리나 파일 시스템 분석에서 삭제된 실행 파일을 찾는 데에 중요한 단서가 됨

 

PE File Signature 값은 다음 그림과 같다.

 

[그림 6] PE File Signature 영역

 

여기서 중요한 것은 DOS MZ Header 설명 부분에서 설명한 것과 같이 DOS MZ Header 영역의 e_lfanew 구조체 멤버는 IMAGE_NT_HEADER의 주소를 가지고 있다고 설명하였으며, 여기서 그 사실 확인이 가능하다.

 

 

2-2. PE File Header

  • 실행 파일 자체의 메타데이터를 가지고 있음

  • 길이는 20바이트

  • 구조체는 IMAGE_FILE_HEADER 구조체 사용

 

구조체 정보는 다음 그림과 같다.

 

[그림 7] IMAGE_FILE_HEADER 구조체 정보

 

[그림 8] PE Header 영역 오프셋 구조

 

1) Machine

  • 실행 파일이 사용하는 CPU를 나타내는 번호를 가지고 있음 

 

Machine 종류는 다음 표와 같다.

 

 

[표 1] Machine 목록

CPU 종류

설명

IMAGE_FILE_MACHINE_UNKNOWN

0x00

어떤 머신에도 적용이 가능한 종류

IMAGE_FILE_MACHINE_AM33

0x01D3

마쯔시다 AM33

IMAGE_FILE_MACHINE_AMD_64

0x8664

64bit CPU(Intel 포함)

IMAGE_FILE_MACHINE_ARM

0x01C0

ARM(Little Endian)

IMAGE_FILE_MACHINE_EBC

0x0EBC

EFI Byte Code

IMAGE_FILE_MACHINE_I386

0x014C

Intel 386 또는 그 이후 Processor

IMAGE_FILE_MACHINE_IA64

0x0200

Intel Itanium Processor

IMAGE_FILE_MACHINE_M32R

0x9041

미쯔비시 M32(Little Endian)

IMAGE_FILE_MACHINE_MIPS16

0x0266

MIPS16

IMAGE_FILE_MACHINE_MIPSFPU

0x0366

MIPS(FPU 지원)

IMAGE_FILE_MACHINE_MIPSFPU16

0x0466

MIPS16(FPU 지원)

IMAGE_FILE_MACHINE_POWERPC

0x01F0

Power PC(Little Endian)

IMAGE_FILE_MACHINE_POWERCFP

0x01F1

Power PC(부동소수점 지원)

IMAGE_FILE_MACHINE_R4000

0x0166

MIPS(Little Endian)

IMAGE_FILE_MACHINE_SH3

0x01A2

Hitachi SH3

IMAGE_FILE_MACHINE_SH3PSP

0x01A3

Hitachi SH3 DSP

IMAGE_FILE_MACHINE_SH4

0x01A6

Hitachi SH4

IMAGE_FILE_MACHINE_SH5

0x01C2

Thumb

IMAGE_FILE_MACHINE_WCEMIPSV2

0x0169

MIPS(Little Endian WCE V2)

 

 

2) NumberOfSections(NoS)

  • 실행 파일이 가지고 있는 섹션의 개수를 의미

  • 만약 섹션을 삭제, 추가하면 이 구조체 멤버의 값 또한 변경 필요

 

3) TimeDataStamp

  • 실행 파일이 컴파일된 시간으로 이 시간은 컴파일 한 컴퓨터의 시스템 시간을 의미

  • 32비트 유닉스 포맷으로 GMT를 기준으로 함

  • 만약 '0x4A5BC60F' 값이 저장되어 있다면, 이 값을 10진수로 변환하였을 경우 '1247528463'라는 숫자를 의미

 

우리가 알아볼 수 있는 날짜로 변환하게 되면 다음과 같다.

 

[그림 9] Python을 이용하여 변환

 

4) PointerToSymbolTable(PTST)

  • COFF 심볼 파일의 오프셋을 값으로 가짐

  • 컴파일러에 의해 생성된 OBJ 파일이나 디버그 모드로 만들어져 COFF 디버그를 가진 PE 파일에서만 사용됨

  • COFF 심볼 테이블은 이제 더 이상 사용하지 않아 이 값은 반드시 0으로 설정되어야 함

 

5) NumberOfSymbols(NoS)

  • 심볼 테이블에 있는 엔트리 개수를 나타냄

  • COFF 심볼 테이블은 더 이상 사용되지 않으므로 반드시 0으로 설정되어야 함

 

6) SizeOfOptionalHeader(SOOH)

  • Optional Header의 크기를 값으로 가짐

  • 실행 파일에서만 필요하며, 일반적으로 32bit 시스템은 E0의 값을 가짐

 

7) Characteristics(Char)

  • 파일의 속성을 결정하는 값을 가짐

 

Characteristics의 값들은 다음 표와 같다.

 

 

[표 2] Characteristics

속성 이름

설명

IMAGE_FILE_RELOCS_STRIPPED

0x0001

실행 파일 전용, 이 파일은 재배치되지 않으므로 반드시 지정된 베이스 주소에 로그 필요, 지정된 베이스 주소가 사용 불가능하면 에러 발생

IMAGE_FILE_EXECUTABLE_IMAGE

0x0002

실행 파일 전용, 실행 파일은 검증되었으며 실행 가능, 이 값이 설정되어 있지 않으면 그것은 링크 에러 의미

IMAGE_FILE_LINK_NUMB_STRIPED

0x0004

COFF 라인 번호는 더 이상 사용하지 않으므로 이 값은 반드시 0이어야 함

IMAGE_FILE_LOCAL_SYMS_STRIPED

0x0008

로컬 심볼에 대한 COFF 심볼 테이블은 더 이상 사용하지 않으므로 이 값은 반드시 0이어야 함

IMAGE_FILE_AGGRESIVE_ES_TRIM

0x0010

해당 값은 윈도우 2000과 그 이후 버전에서는 더 이상 사용되지 않으므로 반드시 0이어야 함

IMAGE_FILE_LARGE_ADDRESS_AWARE

0x0020

애플리케이션은 2GB보다 큰 주소를 다룰 수 있음

?????

0x0040

예약 영역

IMAGE_FILE_BYTES_REVERSED_LO

0x0080

Little Endian, 해당 필드는 더 이상 사용하지 않으므로 반드시 0이어야 함

IMAGE_FILE_32BIT_MACHINE

0x0100

머신이 32bit Processor를 사용

IMAGE_FILE_DEBUG_STRIPED

0x0200

해당 실행 파일에는 디버깅 정보가 없음

IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP

0x0400

해당 실행 파일이 이동형 저장 장치에 있다면 실행 파일 전체를 페이지 파일로 복사함 

IMAGE_FILE_NET_RUN_FROM_SWAP

0x0800

해당 실행 파일이 네트워크로 연결된 저장 장치에 있다면 실행 파일 전체를 페이지 파일로 복사

IMAGE_FILE_SYSTEM

0x1000

이 실행 파일은 시스템 파일

IMAGE_FILE_DLL

0x2000

해당 실행 파일은 DLL 파일, 사실상 실행 파일로 취급

IMAGE_FILE_SYSTEM_ONLY

0x4000

해당 파일은 오직 Single Processor가 있는 머신에서만 실행되어야 함

IMAGE_FILE_BYTES_REVERSED_HI

0x8000

Big Endian, 해당 필드는 더 이상 사용되지 않으므로 0값을 가져야 함

 

PE Header 영역의 세 번째 영역인 PE File Optional Header 영역을 알아보기 전에 알아두어야 할 개념은 바로 RVA이다.

 

PE File Optional Header 영역에서 자주 언급되기 때문에 미리 알아두고 이해하는 것이 훨씬 효과적이다.

 

 

2-3. RVA (Relative Virtual Address)

  • 실행 파일이 프로세스를 만들고 실행 파일의 코드와 데이터가 가상 메모리에 맵핑이 되면 메모리에 맵핑되는 지점의 시작 주소가 베이스 주소가 됨

  • RVA는 가상 메모리 상의 베이스 주소를 기준으로 로드된 실행 파일의 상대적 위치를 의미

 

가상 메모리에서의 가상 주소는 아래와 같은 형식으로 구할 수 있다.

 

 

가상 주소 = 베이스 주소(Base Address) + RVA

 

 

RVA는 가상 메모리에 맵핑된 이후의 오프셋 값이므로 PE 파일상의 오프셋과 같다고 생각하면 안된다.

 

물론 PE 파일은 가상 메모리에 그대로 맵핑이 되어 순서가 맵핑 되기 전과 같지만, 가상 메모리에 맵핑이 되면 섹션 사이에 패딩이 생겨 오프셋 값이 달라진다.

 

 

2-4. PE File Optional Header

  • 실행 파일의 실행 정보가 담긴 부분

  • 필수적인 부분이므로, PE File Header보다 더 중요

  • Option이라는 이름이 붙은 이유는 오브젝트가 이 영역을 선택적으로 가질 수 있기 때문

  • 정작 오브젝트에서는 별다른 기능을 발휘하지 않음

 

PE File Optional Header 부분은 IMAGE_OPTIONAL_HEADER 구조체로 되어 있다.

 

크기가 유동적이고 앞서 보았던 IMAGE_NT_HEADER의 SizeOfOptionalHeader 구조체 멤버에 의해 크기가 결정되고, PE File Header 바로 뒤에 위치 한다.

 

IMAGE_OPTIONAL_HEADER 구조체는 Standard Fields, NT additional Fields, IMAGE_DATA_DIRECTORY로 구분이 된다.

 

 

Standard Fields

  • 파일을 로드하고 실행하는 것에 대한 정보가 들어있음

[그림 10] Standard Fields

 

[그림 11] Standard Field 영역 오프셋 구조

 

1) Magic

  • 실행 파일의 상태를 나타내는 부호 없는 정수 값을 가짐

  • 이 값이 '0x010B' 라면 일반적인 실행 파일을 뜻하며, '0x0107' 이라면 ROM 이미지, '0x020B'라면 64bit Executable 타입이라는 것을 의미

 

2) MajorLinkerVersion(V1)

  • 링커의 상위 버전 넘버를 저장하는 멤버

 

3) MinorLinkerVersion(V2)

  • 링커의 하위 버전 넘버를 저장하는 멤버

 

4) SizeOfCode

  • 코드 섹션(.text)의 크기 정보를 가지고 있음

  • 코드 섹션이 여러 개라면 그것들의 합에 대한 값을 가짐

 

5) SizeOfInitialized Data

  • 초기화된 데이터 섹션의 크기를 값으로 가짐

  • SizeOfCode 멤버와 마찬가지로 자신이 담당하고 있는 섹션의 수가 여러 개라면 그들의 합에 대한 값을 가짐

 

6) SizeOfUninitialized Data

  • 초기화되지 않은 데이터 섹션의 크기를 값으로 가짐

  • 섹션이 여러 개일 경우 SizeOfInitialized Data 구조체 멤버와 동일한 성격을 가짐

 

7) AddressOfEntry Point

  • 메모리에 맵핑된 상태에서의 실행 코드 주소를 가짐

  • 이 주소는 절대적 주소가 아닌 상대적 주소(RVA)

  • 보통 .text 섹션 내의 특정 주소를 가리킴

  • 실행 파일에서의 이 주소는 실행 코드의 시작 주소

  • 디바이스 드라이버에서는 초기화 함수의 주소가 됨

  • 반면 DLL 파일은 이 값이 선택적이며, 이 값이 없다면 반드시 0으로 되어야 함

 

8) BaseOfCode

  • 실행 파일이 메모리에 맵핑된 상태에서의 코드 섹션(.text) 주소를 가짐

  • 이 값은 상대적 주소인 RVA 값

 

9) BaseOfData

  • 실행 파일이 메모리에 맵핑된 상태에서의 데이터 섹션(.data) 주소를 가짐

  • 이 값은 상대적 주소인 RVA 값

 

 

NT Additional Fields

  • 해당 영역은 Standard Fields 바로 다음부터 시작되며, 많은 구조체 멤버로 이루어짐

  • 이 영역에서 제공되는 정보는 윈도우 링커와 로더가 사용하는 정보

 

다음 그림은 해당 영역의 오프셋 구조이다.

 

[그림 12] NT Additional Fields

 

[그림 13] NT Additional Fields 영역 오프셋 구조

 

1) ImageBase

  • 가상 메모리에 로드된 실행 파일의 주소를 가짐

  • 이 값은 반드시 64KB이어야 함

  • ImageBase 구조체 멤버 값과 Standard Fields의 BaseOfCode 값을 더하면 .text 섹션의 가상 메모리 주소

 

2) SectionAlignment

  • 가상 주소에 맵핑될 때 섹션이 할당 받을 가상 주소의 기준 값을 가짐

  • 섹션은 메모리에 맵핑될 때 섹션별로 맵핑되기 때문

  • 섹션의 시작 주소는 항상 메모리 페이지의 배수여야 하므로 이 값은 FileAlignment와 같거나 커야 함

  • 기본 값은 페이지의 크기와 같음

 

3) FileAlignment

  • PE 파일 내에서 섹션들이 시작하는 주소의 기준 값

  • 따라서 PE 파일 내에서의 섹션이 시작하는 주소는 항상 이 값의 배수가 됨

  • 이 값은 2에서 512 사이에 있는 2의 제곱이거나 64KB가 되지만 기본 값은 512

  • SectionAlignment의 값이 메모리 페이지 크기보다 작다면 FileAlignment의 값과 동일해야 함

 

4) MajorOperatingSystemVersion(mOS1)

  • 실행 파일을 실행하는데 필요한 OS의 최소 상위 버전 값을 가짐

 

5) MinorOperationSystemVersion(mOS2)

  • 실행 파일을 실행하는데 필요한 OS의 최소 하위 버전 값을 가짐

 

6) MajorImageVersion(mIV1)

  • 실행 파일의 상위 버전의 값을 가짐

 

7) MinorImageVersion(mIV2)

  • 실행 파일의 하위 버전의 값을 가짐

 

8) MajorSubsystem(mSV1)

  • 실행 파일을 실행하는데 필요한 서브 시스템의 상위 버전의 값을 가짐

 

9) MinorSubsystem(mSV2)

  • 실행 파일을 실행하는데 필요한 서브 시스템의 하위 버전의 값을 가짐

 

10) Win32Version

  • 이 멤버는 VC++6.0 SDK까지는 예약 영역이었음

  • 7.0부터 지금의 이름으로 바뀌었으며, 하지만 여전히 사용하지 않아 보통 0으로 되어 있음

 

11) SizeOfImage

  • 메모리에 로드 되었을 때 실행 파일의 크기 값을 가짐

  • 이 값은 헤더를 포함했을 때의 크기이며, 반드시 SectionAlignment의 배수여야 함

  • 이 값을 기준으로 해서 가상 메모리에서 실행 파일을 맵핑할 공간을 예약

 

12) SizeOfHeader

  • DOS Stub, PE 헤더, 섹션 헤더의 합 값을 가짐

  • 이 값은 무조건 FileAlignment의 배수여야 함

 

13) Checksum

  • 실행 파일의 체크섬 값을 가짐

  • 체크섬 알고리즘은 IMAGEHELP, DLL 파일에 있으며 이 값은 모든 드라이버와 부팅 시 로드 된 DLL 파일, 중요한 윈도우 프로세스가 로드한 DLL 파일을 검증할 때 사용

 

14) Subsystem

  • 실행 파일을 실행하는데 필요한 서브 시스템의 값을 가짐

 

다음 표는 Subsystem 값 목록표이다.

 

 

[표 3] Subsystem 값 목록표

상수

값 

목록표

IMAGE_SUBSYSTEM_UNKNOWN

0

알 수 없는 SubSystem

IMAGE_SUBSYSTEM_NATIVE

1

디바이스 드라이버와 Native Windows Process

IMAGE_SUBSYSTEM_WINDOWS_GUI

2

Windows GUI SubSystem

IMAGE_SUBSYSTEM_WINDOWS_GUI

3

Windows CUI SubSystem

IMAGE_SUBSYSTEM_POSIX_CUI

7

Posix CUI Subsystem

IMAGE_SUBSYSTEM_WINDOWS_CE_GUI

9

Windows CE

IMAGE_SUBSYSTEM_EFI_APPLICATION

10

EFI (Extensible Firmware Interface) Application

IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER

11

Boot Service가 있는 EFI 드라이버

IMAGE_SUBSYSTEM_RUNTIME_DRIVER

12

Runtime Service가 있는 EFI 드라이버

IMAGE_SUBSYSTEM_EFI_ROM

13

EFI ROM Image

IMAGE_SUBSYSTEM_XBOX

14

XBOX

 

15) DLLchr(DllCharacteristics)

  • PE 파일이 DLL 파일인 경우 파일의 속성정보를 의미

 

16) SizeOfStackReserve

  • 예약할 스택의 크기 값을 가짐

 

17) SizeOfStackCommit

  • 커밋할 스택의 크기 값을 가짐

 

18) SizeOfHeapReserve

  • 예약할 힙의 크기 값을 가짐

19) SizeOfHeapCommit

  • 커밋할 힙의 크기 값을 가짐

 

20) LoaderFlags

  • 예약된 영역으로 0으로 채워져 있음

 

21) NumberOfRvaAndSize

  • Optional Header의 나머지 부분에 있는 데이터 디렉토리 엔트리의 숫자 값을 가짐

 

 

IMAGE_DATA_DIRECTORY

  • WinNT.h를 보면 IMAGE_OPTIONAL_HEADER 안에 있지만, 사실은 NT Additional Fields 하부 구조체

 

구조체 선언은 다음 그림과 같다.

 

[그림 14] IMAGE_DATA_DIRECTORY 구조체

 

WinNT.h에 명시되어 있는 것을 보면 IMAGE_DATA_DIRECTORY의 엔트리 개수는 16개로 정의되어 있다.

 

[그림 15] 엔트리 정의

 

[그림 16] 엔트리 목록

 

엔트리는 [그림 16]과 같이 정의되고, 마지막은 0으로 예약되어 있기 때문에 16개가 되는 것이다.

 

[그림 17] 엔트리 오프셋 구조

 

각 부분은 주소와 크기로만 나누어진다.


(3) Section

3-1) Section Header

  • 섹션 헤더 영역에는 각 세션의 헤더들이 있으며, 섹션들은 이 헤더에 메타 데이터와 메모리에 로드될 때 필요한 정보를 저장

 

섹션 헤더의 구조체 형태는 다음 그림과 같다.

 

[그림 18] IMAGE_SECTION_HEADER 구조체

 

위 [그림 18]에는 보이지 않지만 IMAGE_SIZEOF_SHORT_NAME은 8바이트로 정의되어 있으며, 이름 길이가 8바이트를 넘어가게 되면 8바이트 이후는 잘리며, 8바이트 보다 짧을 경우 남는 공간은 NULL로 채워진다.

 

 

다음 그림은 .text 섹션의 헤더 영역 오프셋 구조이다.

 

[그림 19] IMAGE_SECTION_HEADER 오프셋 구조

 

1) Name

  • 섹션의 이름 부분

  • 8바이트 내에서 사용자가 임의로 수정 가능 

 

2) VirtualSize

  • 메모리에 로드되는 섹션의 전체 크기

  • 이 필드는 실행 파일에서만 존재하고 오브젝트 파일에서는 0으로 설정되어 있음

  • SizeOfRawData보다 크다면 섹션의 나머지 부분은 0으로 채워짐

 

3) VirtalAddress

  • 메모리에 로드 되었을 때의 RVA 값을 가짐

  • 오브젝트 파일의 경우 0으로 설정 됨

 

4) SizeOfRawData

  • 실행 파일의 경우 디스크 상에서 초기화된 데이터의 크기를 말함

  • Optional Header의 FileAlignment 값의 배수이어야 함

  • VirtualSize보다 작다면 섹션 나머지 부분은 0으로 채워짐

  • 오브젝트 파일의 경우 0으로 설정 됨

 

5) PointerToRawData(PotToRawData)

  • PE 파일 내에서의 섹션 위치의 오프셋을 값으로 가짐

  • 실행 파일의 경우 이 값은 무조건 FileAlignment의 배수여야 함

 

6) PointerToRelocations(PotToRelocations)

  • 섹션 재배치에 대한 시작 지점 오프셋을 값으로 가짐

  • 이 값은 보통 오브젝트 파일에 사용

  • 실행 파일의 경우 0으로 설정 됨

 

7) PointerToLineNumbers(PotToLineNumbers)

  • 해당 섹션에 대한 Line-Number 엔트리의 시작 지점 주소 값을 가짐

  • Line-Number 엔트리는 COFF 포맷으로 지금은 사용하지 않기 때문에 0으로 설정 되어 있음

 

8) NumberOfRelocations(Nolo)

  • 해당 섹션의 재배치 엔트리 숫자를 값으로 가짐

  • 실행 파일의 경우 항상 0으로 설정되어 있음

 

9) NumberOfLineNumber(Nolo)

  • 해당 섹션에 대한 Line-Number 엔트리의 숫자를 가짐

 

10) Characteristics

  • 해당 섹션의 속성 정보를 표시하는 값을 가짐

 

 

Characteristics에 대한 속성 목록은 다음 표와 같다.

 

[표 4] 속성 목록표

종류

설명

IMAGE_SCN_CNT_CODE

0x00000020

실행 코드를 가지고 있음

IMAGE_SCN_CNT_INITIALIZED_DATA

0x00000040

초기화된 데이터를 가지고 있음

IMAGE_SCN_CNT_UNINITIALIZED_DATA

0x00000080

초기화되지 않은 데이터를 가지고 있음

IMAGE_SCN_MEM_NOT_CACHED

0x04000000

캐시될 수 없음

IMAGE_SCN_MEM_NOT_PAGED

0x08000000

페이징되지 않음

IMAGE_SCN_MEM_SHARED

0x10000000

메모리에서 공유될 수 없음

IMAGE_SCN_MEM_EXECUTE

0x20000000

코드로서 실행될 수 없음

IMAGE_SCN_MEM_READ

0x40000000

읽기가 가능

IMAGE_SCN_MEM_WRITE

0x80000000

쓰기가 가능

 

더 많은 속성들이 있지만 중요한 것만 목록화 한 내용이다.

 

더 많은 속성들은 WinNT.h 파일을 참고하면 확인 가능하다.

 

만약 섹션 이름이 임의로 바뀌어 어떠한 섹션인지 파악이 되지 않는다면 섹션 속성 정보를 보는 것도 하나의 방법이다.

 

섹션 속성 정보들은 각 섹션마다 기본적으로 정의되어 있어 섹션 속성 정보만 봐도 어떠한 섹션인지 파악 가능하다.

 

 

앞에서 알아본 PE 구조들은 실행 파일들의 정보나 메모리에 로드될 때의 정보 등이었다.

 

이제 알아볼 섹션은 실행 파일의 동작에 관한 정보들이다.

 

섹션에는 여러 가지 섹션이 있는데, 대부분 .text, .data, .rdata, .rsrc, .edata, .idata, .reloc 섹션이 많이 사용된다.

 

 

분석하고자 하는 파일의 섹션 정보는 PE File Header를 보면 NumberOfSections에 색션 개수가 나타나 있으며, 각 섹션의 위치 정보 등은 Section Header 영역을 보면 확인할 수 있다.

 

[그림 20] NumberOfSections

 

[그림 21] 섹션 헤더 영역의 섹션 이름

 

 

3-2. .text 섹션

  • 실행 파일의 소스코드가 들어있는 섹션

  • 초기화되거나 초기화되지 않은 변수, import와 export 테이블 정보를 제외한 나머지 소스코드들이 저장됨

 

해당 섹션의 기본 속성 정보는 다음과 같다.

  • IMAGE_SCN_CNT_CODE

  • IMAGE_SCN_MEM_EXCUTE

  • IMAGE_SCN_MEM_READ

 

섹션의 위치와 크기는 섹션 헤더(PointerToRawData, SizeOfRawData)를 참고하면 알 수 있으며 .text 섹션 헤더에서의 PE 파일 내에서의 .text 섹션 위치와 크기를 확인할 수 있다.

 

[그림 22] .text 섹션의 오프셋과 크기

 

위 [그림 22]의 빨간색 박스를 보면 PE 파일 내에서의 주소를 확인할 수 있다.

 

.text 섹션의 마지막 오프셋은 주소와 크기를 더하게 되면 결과 값이 나오게 된다.

 

.text 섹션의 마지막 오프셋으로 이동한 다음, 섹션의 모습을 보면 어느 정도 데이터가 있다가 나머지는 0으로 채워져 있는 것을 확인할 수 있다.

 

이러한 이유는 SizeOfRawData의 경우 FileAlignment(IMAGE_OPTIONAL_HEADER)의 배수가 되어야 하므로 실제 데이터의 크기를 가지고 있는 VirtualSize 값을 FileAlignment의 값의 배수로 올림하여 크기가 늘어나 채울 데이터가 없어 0으로 채운 것이다.

 

실제 메모리에 .text 섹션이 올라갈 때는 0을 제외하고 올라가게 된다.

 

 

이러한 방법으로 모든 섹션의 영역을 찾을 수 있다.

 

이 방법을 계속 섹션마다 사용하여 영역을 찾는 것을 보여줄 수는 없기 때문에 이후에 나오는 섹션들은 특징과 속성 정보 등만 소개한다.

 

 

3-3. .data 섹션

  • 읽기/쓰기가 가능한 일반적인 데이터를 위한 섹션

  • 정적 변수와 전역 변수가 저장

 

해당 섹션의 기본 속성 정보는 다음과 같다.

  • IMAGE_SCN_CNT_INITIALIZED_DATA

  • IMAGE_SCN_MEM_READ

  • IMAGE_SCN_MEM_WRITE

 

 

3-4. .rdata 섹션

  • 읽기 전용 섹션으로 상수와 배열로 직접 정의된 문자열과 런타임 라이브러리에서 사용하는 에러 메시지 등이 저장

 

해당 섹션의 기본 속성 정보는 다음과 같다.

  • IMAGE_SCN_CNT_INITIALIZED_DATA

  • IMAGE_SCN_MEM_READ

 

 

3-5. .idata, .edata 섹션

  • .idata 섹션은 import table이 들어 있으며, .edata 섹션은 export table이 의미

  • 이 섹션들은 실행하는데 꼭 필요한 섹션은 아님

 

.idata 섹션의 기본 속성 정보는 다음과 같다.

  • IMAGE_SCN_CNT_INITIALIZED_DATA

  • IMAGE_SCN_MEM_READ

  • IMAGE_SCN_MEM_WRITE

 

.edata 섹션의 기본 속성 정보는 다음과 같다.

  • IMAGE_SCN_CNT_INITIALIZED_DATA

  • IMAGE_MEM_READ

 

 

3-6. .reloc 섹션

  • PE 파일이 메모리 상에 로드될 때 재배치에 관해 참조하는 정보가 들어있는 섹션

  • 실행 파일의 경우 이 섹션은 필요하지 않을 수 있지만, DLL 파일의 경우 메모리에 로드될 때 재배치를 염두하고 있기 때문에 이 섹션이 없으면 실행되지 않을 수도 있음

 

.reloc 섹션의 기본 속성 정보는 다음과 같다.

  • IMAGE_SCN_MEM_READ

  • IMAGE_SCN_CNT_INITIALIZED_DATA

  • IMAGE_SCN_TYPE_NOLOAD

 

이렇게 해서 PE 포맷을 모두 알아보았다.

 

조금만 더 깊숙이 들어가면 그 구조는 매우 복잡하며, 알아야 할 것들이 너무 많기 때문에 포렌식 업무를 수행함에 있어 알아두어야 할 것들만 소개하였다.

 

앞서 말했듯이 PE 포맷은 파일 카빙, 파일 시그니처 탐지에 유용하게 사용될 수 있어 이렇게 소개하였다.

 

포렌식 관점에서 PE 구조를 보면 파일 생성 시간 값을 저장하는 PE File Header 영역에 TimeDataStamp 구조체 멤버가 중요하고 또 파일의 종류를 결정하는 PE File Header 영역에 Characteristics 구조체 멤버가 중요하다.


# Reference

 

http://www.yes24.com/Product/Goods/8511539

+ Recent posts