본문 바로가기

리버스 엔지니어링

PE 헤더 -1


PE 헤더는 많은 구조체로 이루어져있다. 지금부터 각 구조체에 대해서 하나씩 살펴보자.


1. DOS 헤더

 DOS 파일에 대한 하위 호환성을 고려해서 만든 해더. 그 결과로 PE 헤더의 제일 앞부분에는 기존 DOS EXE Header를 확장 시킨 IMAGE_DOS_HEADER 구조체가 존재한다.



IMAGE_DOS_HEAGER 구조체의 크기는 40이다. 이 구조체에서 알아 두어야 할 중요한 맴버는 e_magic과 e_lfanew이다.


typedef struct _IMAGE_DOS_HEADER {     
    WORD   e_magic;          // DOS signature : 4D5A ("MZ")
    WORD   e_cblp;                     
    WORD   e_cp;                       
    WORD   e_crlc;                     
    WORD   e_cparhdr;                  
    WORD   e_minalloc;                 
    WORD   e_maxalloc;                 
    WORD   e_ss;                       
    WORD   e_sp;                       
    WORD   e_csum;                     
    WORD   e_ip;                       
    WORD   e_cs;                       
    WORD   e_lfarlc;                   
    WORD   e_ovno;                     
    WORD   e_res[4];                   
    WORD   e_oemid;                    
    WORD   e_oeminfo;                  
    WORD   e_res2[10];                  
    LONG   e_lfanew;         // offset to NT header 

  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; 



e_magic : DOS signature(4D5A => ASCII 값 "MZ")

e_lfanew : NT heager의 옵셋을 표시(파일에 따라 가변적인 값을 가짐)


모든 PE 파일은 시작부분(e_magic)에 DOS Signature("MZ")가 존재하고 e_lfanew 값이 가리키는 위치에 NT Header 구조체가 존재해야 한다.



notepad.exe를 확인해보면 시작 값이 4D5A값이 들어있는 것을 볼 수 있으며 마지막에 000000F8값은 e_lfanew 값을 볼 수 있다.


2.DOS Stub

 DOSD Heaber 밑에는 DOS Stub이 존재한다. DOS stub의 존재 여부는 옵션이며 크디도 일정하지 않다. 이 헤더가 없어도 파일 실행에는 문제가 없다.



40~4D영역은 16비트의 어셈블리 명령어이다. 32bit 이상의 Windows OS에서는 이쪽 명령어가 실행되지 않는다.DOS 환경에서 Notepad.exe를 실행하면 저 코드를 실행할 수 있다. 이 특성을 이용하면 하나의 실행 파일에 DOS와 Windows에서 모두 실행 가능한 파일을 만들 수도 있다.



3. NT Header


NT header구조체 IAGE_NT_HEADERS이다.


typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;                          // PE Signature : 50450000 ("PE"00)
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

 



IMAGE_NT_HEADER 구조체는 3개의 맴버로 되어있는데 제일 첫 멤버는  signature로 5045000h("PE"00)을 가진다. 그리고 FileHeader와 Optional Header 구조체 맴버가 있다.




50450000으로 시작하는 부분부터 F8크기가 되는 큰 구조체이다. 여기서 fileHeader와 OptionalHeader 구조체를 하나씩 살펴보자.


4. NT Header-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;


이 구조체에서 다음 4가지 맴버가 중요하다. 4가지 구조체가 정확히 세팅이 되어있지 않으면 파일은 정상적으로 실행되지 않는다.


a) Machine

 Machine 넘버는 CPU별로 고유한 값이며 32비트 Intel x86 호환칩은 14C의 값을 가진다. 64bit일 경우 200의 값을 가진다


b) NumberOfSections


PE 파일은 코드, 데이터, 리소스 등이 각각의 섹션에 나뉘어서 저장된다고 설명했다. NumberOfSections는 바로 그 섹션의 개수를 나타낸다. 그러므로 이 값은 반드시 0보다 커야하고 정의된 섹션의 개수와 실제 섹션의 개수가 다르면 에러가 발생한다.


c) SizeOfOptionalHeader

IMAGE_NT_Headers 구조체의 마지막 멤버는 IMAGE_OPTIONAL_HEADER32 구조체이다. SizeOfOptionalHeader 맴버는 바로 이  IMAGE_OPTIONAL_HEADER32 구조체의 크기를 나타낸다.  IMAGE_OPTIONAL_HEADER32는 C언어의 구조체이기 때문에 이미 그 크기가 결정되어있다. 그런데 WINDOWS의 PE로더는 IMAGE_OPTIONAL_HEADER의 SizeOfOptionalHeader 값을 보고  IMAGE_OPTIONAL_HEADER32 구조체 크기를 인식한다.


d)Characteristics

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


#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. 


여기서 0002와 2000의 값을 기억하자.


참고로 PE 파일중에 Characteristics 값에 0002h가 없는 경우가 있다. 예를들어 *.obj와 같은 object 파일 및 resource DLL 같은 파일을 들 수 있다.


위 4가지 말고도 TimeDateStamp 맴버가 있는데 이 값은 파일의 실행에 영향을 미치지 않는 값으로 해당 파일의 빌드 시간을 나타낸 값이다.

'리버스 엔지니어링' 카테고리의 다른 글

섹션 헤더  (0) 2018.01.16
NT 헤더 -2  (0) 2018.01.16
PE File Format -1  (0) 2018.01.10
함수 호출 규약  (0) 2018.01.10
Process Explorer란?  (0) 2018.01.10