PE 헤더(PE Header)
PE(Portable Executalbe File Format)
- Windows 실행 파일 형식을 의미
- 실행 파일 게열 : EXE, SCR(Screen Saver)
- 라이브러리 계열 : DLL, OCX(Active X)
- 드라이버 계열 : SYS
- 오브젝트 파일 계열 : OBJ
- Windows 환경에 따라 32bit(PE32), 64bit(PE+ 또는 PE32+)라고 부른다
- PE파일을 통해서 프로그램이 사용하는 API또는 DLL등 다양한 정보와 어느 메모리 주소에 로딩되는지 확인할 수 있다
- Linux나 UNIX 계열의 환경에서는 PE파일과 비슷한 형식으로 ELF(Executable and Linkable Format)파일이 존재한다
빌드 과정
- obj 파일 -> [c/ cpp] 파일을 빌드하면 컴파일러는 [c/ cpp]의 모든 헤더파일과 소스 파일을 합쳐 기계어 파일을 만든다
- 링킹 작업 -> OS에서 파일을 실행하기 위해 링커는 동적 라이브러리나 각종 리소스 데이터, 임포트, 익스포트 테이블을 처리할 수 있는 정보를 저장한다
- PE 포맷 -> Windows(윈도우)에서 실행 및 배포를 위해 만든 파일 포맷
소스 코드 작성 -> 컴파일로 바이너리파일 OBJ -> 필요한 라이브러리 링킹 -> exe파일
PE 파일 구조
※ PE 파일은 구조체로 되어 있다. PE 구조체를 보면 매우 복잡한데 억지로 외우기보다는 많이보고 익숙해지는게 중요!!
IMAGE_DOS_HEADER (4D 5A)
- IMAGE_DOS_HEADER에 들어갈 내용은 WinNT.H 헤더 파일에 구조체의 형식으로 설정되어 있다
- 이 구조체에서 확인 해야할 필드는 첫 필드 e_magic, 마지막 필드 e_lfanew 부분이다
- e_magic : PE 구조를 가진 파일인지 확인 할 때 사용한다 (4D 5A : MZ)
- 프로그램을 실행 하면 가장 먼저 2Byte(WORD)를 읽어 온 후 PE구조가 맞는지 확인한다
- e_lfanew : 다음에 나올 IMAGE_NT_HEADER의 시작 주소를 저장, 파일에 따라 다른 값이 저장
- 주소 값을 저장 할 때 Little Endian 표기법을 사용한다!!
typedef struct _IMAGE_DOS_HEADER{ // DOS의 .EXE HEADER
WORD e_magic; // ★ Magic Number (4D 5A : MZ)
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; // Maxmum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial 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 identigier (for e_ominfo)
WORD e_ominfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
WORD e_lfanew; // ★ File addrs of new exe Header ->
// IMAGE_NT_HEADER 구조체 위치를 알아내는 데 사용!!
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
IMAGE_NT_HEADER(50 45 00 00 : PE\0\0)
- 파일 실행에 필요한 정보가 저장되어 있다
- WinNT.H의 구조체에서 e_lfanew에 저장된 주소값이 이 부분을 가르킨다
- Signature : 50 45 00 00 (PE\0\0)의 4Byte 공간 ASCII Code로 PE 구조를 가진 파일이라는 것을 알려준다
- File_Header : 동작하는 CPU, 섹션 수, 생성 시간 등 파일의 대략적인 정보를 저장한다
- Optional_header : 파일의 실행에 필요한 주요 정보를 저장한다
typedef struct _IMAGE_NT_HEADERS{
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HJEADERS32, *PIMAGE_NT_HEADERS32;
- Signature는 바이러스 제작들이 자신의 값을 표시하는데 자주 이용되어 왔다, 50 45 00 00이 아닐경우 악성코드에 의한 감염 표시용으로 많이 활용됐다
IMAGE_FILE_HEADER
- 파일을 실행하기 위한 가장 기본적인 데이터가 담겨 있는 구조체
typedf struct _IMAGE_FILE_HEADER{
WORD Machine;
WROD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWROD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FIEL_HEADER, *PIMAGE_FILE_HEADER;
- Machine : 어떤 CPU에서 이파일이 실행될 수 있는지를 알려준다
- ★NumberOfSections : 섹션이 몇 개있는지 알려준다. 섹션에는 .text, .rdata, .data, .rsrc로 4개의 섹션이 존재
- 값은 4로 표시된다. 만약 패킹이나 프로텍팅 등으로 섹션 수가 증가한다면 이 값도 증가!!
- TimeDataStamp : 이 파일을 빌드한 날짜가 표시된다. obj파일이 컴파일을 통해 EXE로 생성한 시간
- 변조될 가능성도 있어 완전히 신뢰는 X, 델파이의 경우 정상적으로 이 값을 저장 X -> 1992년으로 표시
- SizeOfOptionalHeader : IMAGE_OPTINAL_HEADER32의 구조체 크기다
- Characteristics : DLL인지 EXE인지 구분하는 용도
IMAGE OPTIONAL_HEADER
- PE구조체중 중욯란 값을 가장 많이 담고 있다!!
typedef struct _IMAGE_OPTIONAL_HEADER{
// Standard Fileds
WORD Magic; // ★
BYTE MajorLinkerVersion; // ★
BYTE MinorLinkerVersion; // ★
DWORD SizeOfCode; // ★
DOWRD SizeOfInitializedData;
DOWRD SizeOFUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
// NT Additional Filedes
DWORD ImageBase; // ★
DOWRD 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;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // ★
}IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32
- Magic : bit 표식 32bit일 경우 0x10B, 64bit일 경우 0x20B
- SizeOfCOde : 코드 양의 전체 크기, 악성코드는 이 필드를 읽어 자신의 코드를 복제할 위치를 기준을 잡기도한다
- MajorLinkerVersion & MajorLinkerVersion : 어떤 버전의 컴파일러로 빌드했는지 알려준다
- ImageBase : 파일이 실행될 경우 실제 가상메모리에 올라가는 번지를 가리킨다(exe : 0x400000, dll : 0x10000000)
- AddressOfEntry : 실제 파일이 메모리에 실행되는 시작점, Entry Point(엔트리 포인트)
- 언패킹할 때 OEP(Orignal Entry Pointer)를 찾아하는데 바로 이곳을 가리킨다
- BaseOfCode : 실제로 코드가 실행되는 번지
- ImageBase는 PE파일 전체에 대한 시작주소이고 코드 영역이 시작 되는 베이스 주소는 BaseOfCode를 더한 값
- SectionAlignment , FileAlignment : 각 섹션을 정렬하기 위한 저장단위
- SizeOfImage : EXE/DLL이 메모리에 로딩됐을 때 전체 크기
- SizeOfHeaders : PE 헤더의 크기를 알려주는 필드
- Subsystem : 이 프로그램이 CLI인지 GUI인지 알려주는 필드
- DataDirectory : IMAGE_DATA_DIRECTORY의 구조체, VirtualAddress와 Size라는 필드가 포함
- 익스포트 디렉터리나 임포트 디렉터리, 리소스 디렉터리, IAT등 각각의 가상 주소와 크기를 이 필드를 통해 확인
typedef struct _IMAGE_DATA_DIRECTORY{
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
IMAGE_NUMBEROF_DIRECTORY_ENTERIES는 16이라는 숫자로 정의 돼있으면 각각에 배열은 밑에 정리
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // ★Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // ★Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // ★Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTRUE 7
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
#define IMAGE_DIRECTORY_ENTRY_TLS 9 //★ TLS Direcotry
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
#define IMAGE_DIRECTORY_ENTRY_IAT //★ Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
#define IMAGE_DIRECTORY_ENTRY_C0M_DESCRIPTOR
IMAGE_SECTION_HEADER
- 섹션 헤더는 주로 각 섹션에 대한 이름, 시작 주소, 사이즈 등의 정보를 관리하는 구조체!!
- 코드 섹션, 데이터 섹션의 정보를 구할 수있다
#define IMAGE_SIZEOF_SHORT_NAME
typedf struct _IMAGE_SECTION_HEADER{
BYTE NAME[IMAGE_SIZEOF_SHORT_NAME];
union{
DWORD PhysicalAddress;
DWORD VirtualSize; // 메모리에서 섹션의 크기
} Misc;
DWORD VirtualAddress; // 메모리에서 섹션의 시작주소(RVA)
DWORD SizeOfRawData; // 파일에서 섹션 크기
DWORD PointerToRawData; // 파일에서 섹션 Offset
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics; // 섹션의 속성
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
IMAGE_IMPORT_DESCRIPTOR
- IAT(Import Address Table) : PE 파일이 사용하는 외부 API함수의 주소들을 배열 형태로 저장해놓는 영역!!
- IAT를 이용하여 API가 아무리 많이 호출되도 모두 동일한 IAT에 접근하면 되므로 코드 섹션이 변경될일이 없다
- 만약 함수를 직접 호출 할경우 DLL이 다른 번지수로 올라가게되면 함수 호출 위치를 모두 바꿔줘야한다
typedf struct _IMAGE_IMPORT_DESCRIPTOR{
union{
DWORD Characteristics;
DWORD OriginalFirstThunk; //Import Name Table주소(RAV)
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name; // 라이브러리(DLL) 이름 문자열 주소
DWORD FirstThunk; // IAT 주소(RAV)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGED *PIMAGE_IMPORT_DESCRIPTOR;
'개인 공부 > 리버싱' 카테고리의 다른 글
Reverse Engineering-008(반복문) (0) | 2023.04.19 |
---|---|
Reverse Engineering-007 PE-02(PE-01추가 & 조건문) (0) | 2023.04.17 |
Reverse Engineering-005 (0) | 2023.04.13 |
Reverse Engineering-004(C++) (0) | 2023.04.12 |
Reverse Engineering-003(구조체 & API) (0) | 2023.04.12 |