공부/Reversing

PE파일 구조

완이버스 2023. 12. 28. 15:25

 

magic code는 linux에서의 ELF 헤더에서 ASCII값이 "ELF"를 확인할 수 있듯이 MS-DOS 헤더는 "MZ"로 식별가능

 

typedef struct _IMAGE_DOS_HEADER
{
    WORD e_magic;       // Magic number
    WORD e_cblp;        // Byte on last page of file
    WORD e_cp;          // Pages in file
    WORD e_crlc;        // Relocations
    WORD e_cparhdr;     // Size of header in paragraphs
    WORD e_minalloc;    // Minimum extra paragraphs needed
    WORD e_maxalloc;    // Maximum extra paragraphs needed
    WORD e_ss;          // Initial (relative) SS value
    WORD e_sp;          // Checksum
    WORD e_ip;          // Initital IP value
    WORD e_cs;          // Initial (relative) CS value
    WORD e_lfarlc;      // File address of relocation table
    WORD e_ovno;        // Overlay number
    WORD e_res[4];      // Reserved words
    WORD e_oemid;       // OEM identifier (for e_oeminfo)
    WORD e_oeminfo;     // OEM information; e_oemid specific
    WORD e_res2[10];    // Reserved words
    LONG e_lfanew;      // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

이 구조체에서 확인할 점은 e_magic과 e_lfanew만 알아두면 된다. 나머지는 잘 사용하지 않음

e_magic은 2byte
e_lfanew은 4byte의 크기를 가지고 있음
e_lfanew는 IMAGE_NT_HEADER의 구조체 위치를 가르키고 있음(Little Endian)

e_lfanew는 000000D8을 가르키고 있고 해당 주소에 위치하는 값을 확인하면 "PE"(4byte)라는 값이 담겨있는 것을 확인

해당 부분이 PE Header의 시작이라는 점을 알 수 있다

 


중간 결론

IMAGE_DOS_HEADER에서 e_magic의 값이 0x5A4D는 PE 파일이라는 것과
e_lfanew의 값은 IMAGE_NT_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;

source : https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_file_header

 

Machine : 어떤 CPU에서 실행 가능한지 정보

NumberOfSections : 파일이 가진 섹션의 갯수( .text, .data, .rdata, .rsrc )

TimeDateStamp : obj -> EXE파일을 만든 시간을 알림 (델파이 program은 1992년으로 표기)

SizeofOptionalHeader : IMAGE_OPTIONAL_HEADER32의 구조체 크기를 가짐 ( OS마다 크기가 다르기에 PE로더에서는 해당 값을 먼저 확인)

Characteristics : 파일의 형식


IMAGE_OPTIONAL_HEADER

typedef struct _IMAGE_OPTIONAL_HEADER {
  WORD                 Magic;
  BYTE                 MajorLinkerVersion;
  BYTE                 MinorLinkerVersion;
  DWORD                SizeOfCode;
  DWORD                SizeOfInitializedData;
  DWORD                SizeOfUninitializedData;
  DWORD                AddressOfEntryPoint;
  DWORD                BaseOfCode;
  DWORD                BaseOfData;
  DWORD                ImageBase;
  DWORD                SectionAlignment;
  DWORD                FileAlignment;
  WORD                 MajorOperatingSystemVersion;
  WORD                 MinorOperatingSystemVersion;
  WORD                 MajorImageVersion;
  WORD                 MinorImageVersion;
  WORD                 MajorSubsystemVersion;
  WORD                 MinorSubsystemVersion;
  DWORD                Win32VersionValue;
  DWORD                SizeOfImage;
  DWORD                SizeOfHeaders;
  DWORD                CheckSum;
  WORD                 Subsystem;
  WORD                 DllCharacteristics;
  DWORD                SizeOfStackReserve;
  DWORD                SizeOfStackCommit;
  DWORD                SizeOfHeapReserve;
  DWORD                SizeOfHeapCommit;
  DWORD                LoaderFlags;
  DWORD                NumberOfRvaAndSizes;
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

source : https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32

 

Magic : 32bit -> 0x10B

             64bit -> 0x20B

SizeofCode : 코드 양의 전체 크기( executable )

AddressOfEntryPoint : 파일이 메모리에서 시작되는 지점 ( main문 시작 지점?) 보안을 위해 ASLR 적용된 상태

BaseOfCode : 실행 코드 위치 (ImageBase + BaseofCode 의 값부터 코드 시작) 좀더 정확한 주소를 알려주는 것으로 추정

AddressOfEntryPoint와 BaseOfCode의 차이를 잘 모르겠다...

ImageBase : 로드할 가상 메모리 주소 (Linux에서 readelf -h <file> 했을때 EntryPoint 정보와 같은 의미로 느껴짐)

SizeOfImage : 메모리 로드시에 전체 크기

SizeOfHeader : PE header의 크기

typedef struct _IMAGE_DATA_DIRECTORY {
  DWORD VirtualAddress;
  DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

- VirtualAddress와 Size 필드

- Export, Import, Rsrc 디렉터리와 IAT등의 가상 주소와 크기 정보

 

각각의 Directory정보는 https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_data_directory

 

IMAGE_DATA_DIRECTORY (winnt.h) - Win32 apps

Represents the data directory.

learn.microsoft.com

에서 확인함


Sections

프로그램의 실제 내용을 담고 있는 block

 

 


IMAGE_SECTION_HEADER

- 각 섹션에 대한 이름, 시작 주소, 사이즈 등의 정보를 관리하는 구조체

typedef struct _IMAGE_SECTION_HEADER {
  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];
  union {
    DWORD PhysicalAddress;
    DWORD VirtualSize;
  } Misc;
  DWORD VirtualAddress;
  DWORD SizeOfRawData;
  DWORD PointerToRawData;
  DWORD PointerToRelocations;
  DWORD PointerToLinenumbers;
  WORD  NumberOfRelocations;
  WORD  NumberOfLinenumbers;
  DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

SOURCE : https://learn.microsoft.com/ko-kr/windows/win32/api/winnt/ns-winnt-image_section_header


처음 공부할때는 뭔 소리인지 정말 모르겠고 어렵게 느껴졌는데 학교에서 linux에 대해서 공부하고 방학기간에 다시 공부하니까 linux 파일 구조와 유사한 점도 있어서 그런지 이해가 조금씩 되는것 같다.

해당 글은 일단 이런게 있다 라는 정도로 나중에 필요한 정보를 찾기 위해 가독성 없이 마구잡이로 넣은 느낌이 있는데 추후에 좀더 공부하면서 정리해 나가는 것이 좋다고 생각한다.

'공부 > Reversing' 카테고리의 다른 글

PE Packer  (0) 2023.12.29
PE구조  (1) 2023.12.22
리버싱 핵심원리 Day1  (0) 2023.08.02