앙 규문띻 

 

 

PE file

오늘은 PE file 구조에 대해서 공부해야겠다.

그전에 PE파일에 대해서 잘 정리해놓은 블랙펄 콘치님의 PE둥 PE둥 시리즈들을 먼저 보고오면 좋을듯하다. (짱짱;;)

 

[PE둥 PE둥 놀면서 훑어보는 PE(1)]

[PE둥 PE둥 놀면서 훑어보는 PE(2)]

[PE둥 PE둥 놀면서 훑어보는 PE(3)]

 

PE file 이란 ? 

- PE 포맷(Portable Executable)은 윈도우 운영 체제에서 사용되는 실행 파일, DLL, object 코드, FON 폰트 파일[1] 등을 위한 파일 형식이다 (위키)

 

즉 .exe 뿐만아니라 dll, sys같은 파일들도 전부 PE 파일이라고 명칭할 수 있다고 한다.

에그 복잡해;;;

이미지 - https://slimv.tistory.com/entry/PE-File-Format-0x01

 

먼저 헤더부터 살펴보자.

PE 파일의 헤더는 3가지로 구분됩니다.

1) DoS Header

2) NT Header

3) Section Header

 

DoS Header

DoS쓰지도않는데 DoS헤더가 왜있을까!

=> PE file format을 만들 당시 널리 사용되던 DOS파일에 대해 하위호환성을 고려해서 만들었음.

그 결과로 인해 PE 헤더의 제일 앞부분에 기존 DOS EXE Header를 확장시킨 IMAGE_DOS_HEADER 구조체가 존재.

 

그렇다면 구조체 내용을 보자!

typedef struct _IMAGE_DOS_HEADER {
     WORD  e_magic;      /* 00: MZ Header signature */
     WORD  e_cblp;       /* 02: Bytes on last page of file */
     WORD  e_cp;         /* 04: Pages in file */
     WORD  e_crlc;       /* 06: Relocations */
     WORD  e_cparhdr;    /* 08: Size of header in paragraphs */
     WORD  e_minalloc;   /* 0a: Minimum extra paragraphs needed */
     WORD  e_maxalloc;   /* 0c: Maximum extra paragraphs needed */
     WORD  e_ss;         /* 0e: Initial (relative) SS value */
     WORD  e_sp;         /* 10: Initial SP value */
     WORD  e_csum;       /* 12: Checksum */
     WORD  e_ip;         /* 14: Initial IP value */
     WORD  e_cs;         /* 16: Initial (relative) CS value */
     WORD  e_lfarlc;     /* 18: File address of relocation table */
     WORD  e_ovno;       /* 1a: Overlay number */
     WORD  e_res[4];     /* 1c: Reserved words */
     WORD  e_oemid;      /* 24: OEM identifier (for e_oeminfo) */
     WORD  e_oeminfo;    /* 26: OEM information; e_oemid specific */
     WORD  e_res2[10];   /* 28: Reserved words */
     DWORD e_lfanew;     /* 3c: Offset to extended header */
 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

크기는 총 40이다. 여기서 일단 살펴볼껀 2가지를 살펴보자. 

- e_magic

- e_lfanew

 

e_magic : DOS signature 4D5A를 나타낸다. ('MZ')

MZ 보이시죠 헤헿

PE의 스펙에 맞게 '4D5A'의 2바이트로 시작하며 'MZ'가 보입니다. 그 다음으로

e_lfanew : NT Header의 offset을 나타낸다.

빨간네모박스가 e_lfanew 값입니다. 000000D8 입니다. (리틀 엔디언)

실제 000000D8에 'PE'가 보이네요 [NT Header 시작부분]

이 값들은 PE header이기 때문에 변조하면 정상 실행되지 않습니다.

 

DoS Stub

다음으로 DoS Stub 입니다.

DoS Header와 NT Header 사이가 그렇다면 DoS Stub이 되겠죠 ??

이 부분입니다!

DOS Stub의 존재여부는 옵션이며 크기도 일정하지 않습니다. (없어도 파일실행과 상관없음!)

위그림에서 offset 40~4D(this 전) 까지는 16비트 어셈영역이셈 32비트 운영체제에서 실행안됌. (DOS환경에서 실행)

=> 즉 DoS환경과 window 환경 둘다 호환되는 실행파일을 만들 수 있다?!

 

NT Header

다음으로 NT Header의 구조체 Image_NT_HEADERS입니다.

 

typedef struct _IMAGE_NT_HEADERS {
  DWORD Signature;
  IMAGE_FILE_HEADER FileHeader;     /* 0x04 */
  IMAGE_OPTIONAL_HEADER32 OptionalHeader;   /* 0x18 */
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

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;

typedef struct _IMAGE_OPTIONAL_HEADER {  
 
   /* Standard fields */
   WORD  Magic; /* 0x10b or 0x107 */ /* 0x00 */
   BYTE  MajorLinkerVersion;
   BYTE  MinorLinkerVersion;
   DWORD SizeOfCode;
   DWORD SizeOfInitializedData;
   DWORD SizeOfUninitializedData;
   DWORD AddressOfEntryPoint;        /* 0x10 */
   DWORD BaseOfCode;
   DWORD BaseOfData;
 
   /* NT additional fields */
   DWORD ImageBase;
   DWORD SectionAlignment;       /* 0x20 */
   DWORD FileAlignment;
   WORD  MajorOperatingSystemVersion;
   WORD  MinorOperatingSystemVersion;
   WORD  MajorImageVersion;
   WORD  MinorImageVersion;
   WORD  MajorSubsystemVersion;      /* 0x30 */
   WORD  MinorSubsystemVersion;
   DWORD Win32VersionValue;
   DWORD SizeOfImage;
   DWORD SizeOfHeaders;
   DWORD CheckSum;           /* 0x40 */
   WORD  Subsystem;
   WORD  DllCharacteristics;
   DWORD SizeOfStackReserve;
   DWORD SizeOfStackCommit;
   DWORD SizeOfHeapReserve;      /* 0x50 */
   DWORD SizeOfHeapCommit;
   DWORD LoaderFlags;
   DWORD NumberOfRvaAndSizes;
   IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; /* 0x60 */
 } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

복잡;;;

 

먼저 IMAGE_NT_HEADERS 부터 봅시다...

typedef struct _IMAGE_NT_HEADERS {
  DWORD Signature;
  IMAGE_FILE_HEADER FileHeader;     /* 0x04 */
  IMAGE_OPTIONAL_HEADER32 OptionalHeader;   /* 0x18 */
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

제일 첫번째 멤버로 Signature(50450000)을 가집니다 'PE'글자를 나타내며 이글자가 보이면 NT header의 시작이라고 볼 수 있습니다.

오오

다음으로 밑에 2개의 구조체가 있네요?

먼저 파일의 대략적인 속성을 나타내는 NT header - 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;

이 구조체에서는 4가지 멤버가 중요합니다. (이 값이 정확하지 않으면 파일이 정상실행 되지않는다고 함.)

 

#1. Machine

Machine 넘버는 CPU 별로 고유한 값이며 32비트 Intel x86 호환 칩은 14C의 값을 가짐

=> 쉽게말해 이게 어떤 환경에 돌아가는지 알려줌 ^호^

014C면 intel 386 환경!

Machine의 넘버링은 아래를 참조하자!

/* These are the settings of the Machine field. */
#define IMAGE_FILE_MACHINE_UNKNOWN  0
#define IMAGE_FILE_MACHINE_I860     0x014d
#define IMAGE_FILE_MACHINE_I386     0x014c
#define IMAGE_FILE_MACHINE_R3000    0x0162
#define IMAGE_FILE_MACHINE_R4000    0x0166
#define IMAGE_FILE_MACHINE_R10000   0x0168
#define IMAGE_FILE_MACHINE_WCEMIPSV2    0x0169
#define IMAGE_FILE_MACHINE_ALPHA    0x0184
#define IMAGE_FILE_MACHINE_SH3      0x01a2
#define IMAGE_FILE_MACHINE_SH3DSP   0x01a3
#define IMAGE_FILE_MACHINE_SH3E     0x01a4
#define IMAGE_FILE_MACHINE_SH4      0x01a6
#define IMAGE_FILE_MACHINE_SH5      0x01a8
#define IMAGE_FILE_MACHINE_ARM      0x01c0
#define IMAGE_FILE_MACHINE_THUMB    0x01c2
#define IMAGE_FILE_MACHINE_ARMNT    0x01c4
#define IMAGE_FILE_MACHINE_ARM64    0xaa64
#define IMAGE_FILE_MACHINE_AM33     0x01d3
#define IMAGE_FILE_MACHINE_POWERPC  0x01f0
#define IMAGE_FILE_MACHINE_POWERPCFP    0x01f1
#define IMAGE_FILE_MACHINE_IA64     0x0200
#define IMAGE_FILE_MACHINE_MIPS16   0x0266
#define IMAGE_FILE_MACHINE_ALPHA64  0x0284
#define IMAGE_FILE_MACHINE_MIPSFPU  0x0366
#define IMAGE_FILE_MACHINE_MIPSFPU16    0x0466
#define IMAGE_FILE_MACHINE_AXP64    IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE  0x0520
#define IMAGE_FILE_MACHINE_CEF      0x0cef
#define IMAGE_FILE_MACHINE_EBC      0x0ebc
#define IMAGE_FILE_MACHINE_AMD64    0x8664
#define IMAGE_FILE_MACHINE_M32R     0x9041
#define IMAGE_FILE_MACHINE_CEE      0xc0ee

[Machine 넘버 참조]

 

#2. NumberOfSections

PE 파일은 코드, 데이터, 리소스가 각각의 세션에 나뉘어져서 저장됩니다.

이 멤버는 그 섹션의 개수를 나타낸다고 합니다. 이 값은 반드시 0보다 커야하며 정의된 섹션 개수와 실제 섹션이 다르면 실행 에러가 발생합니다.

 

위 그림에서 4개의 섹션을 가지고 있다고 합니다. PEview를 통해서 확인해볼까요?

놀랍군요! ㅋㅋㅋㅋㅋ

#3. SizeofOptionalHeader

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

이 멤버는 바로 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 나타냅니다.

PE32+의 경우에는  IMAGE_OPTIONAL_HEADER64 구조체를 활용합니다. (두개의 구조체 크기는 다름)

=> 이렇게 구조체의 범위를 미리 잡아놓고 그만큼의 데이터를 파일의 정보로 받아들임 ㅋ.

 

#4. Characteristics

파일의 속성을 나타내는 값으로, 실행 가능한 형태인지 혹은 DLL파일인지의 정보들이 bit OR 형식으로 조합됨

ex) '0102' = '0100' + '0002'

=> PE 파일이 어떤 속성을 가지고 있는지 조합된 숫자로 파악한다!

 

0002와 2000은 기억하도록 하자(실행가능과 DLL)

#define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  
                                                     // (i.e. no unresolved externel references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004  // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008  // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM         0x0010  // Agressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE       0x0020  // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO         0x0080  // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED            0x0200  // Debugging info stripped from 
                                                     // file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   0x0400  // If Image is on removable media, 
                                                     // copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP         0x0800  // If Image is on Net, 
                                                     // copy and run from the swap file.
#define IMAGE_FILE_SYSTEM                    0x1000  // System File.
#define IMAGE_FILE_DLL                       0x2000  // File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY            0x4000  // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI         0x8000  // Bytes of machine word are reversed.

[Characteristic]

'Reversing' 카테고리의 다른 글

PE file(3)  (0) 2019.04.03
PE File(2)  (0) 2019.04.03
리버싱 5  (0) 2019.04.02
리버싱 4  (0) 2019.03.29
리버싱 3 Stackframe  (0) 2019.03.28

+ Recent posts