컴파일러 - 실행파일 구조
실행파일 구조
1. 개요
우리가 프로그램을 사용할 때 많은 파일들을 접하고 윈도우의 경우 더블클릭으로 리눅스의 경우 명령어로 간단히 실행하지만
실제로 실행하는 가능한 파일의 형태는 한정되어있다.
2. PE FILE FORMAT (윈도우)
윈도우에서 실행 가능한 파일은 모두 PE 파일 포맷을 따르며 해당 파일의 확장자는 아래와 같다.
- 실행 파일 : EXE, SCR
- 라이브러리 : DLL, OCX
- 드라이버 : SYS
- 오브젝트 파일 : OBJ
(OBJ는 간접적으로든, 직접적으로든 실행할 방법은 없지만 스펙상으로는 PE 파일 포맷을 따른다)
PE 파일의 구조를 나타내면 아래와 같다.
세부적인 섹션 크기의 경우 파일에 따라 다르며, section header의 경우에도 어떤 섹션은 있고 어떤 섹션은 없는 식으로 달라질 수 있다. 오른쪽의 숫자는 offset으로 0번지부터 몇 개의 바이트로 이루어져있느냐를 말한다.
1) PE Header
DOS header 부터 Section header까지를 모두 더해서 PE HEADER라고 한다.
a. DOS header
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
제일 앞에 MZ 시그니처(DOS 실행파일 설계자인 Mark Zbikowski 의 이니셜)가 2바이트로 기재되어있으며 e_lfanew는 NT header가 어디부터 시작하는지 offset을 가르키고 있다.
b. DOS stub
이 부분은 있어도 되고 없어도 되는 옵션부분이다. 사실 DOS stub은 없어도 실행간에는 아무런 문제가 없다. 다만 notepad.exe를 열어보면 이부분이 있는 것을 확인 할 수 있는데, DOS에서 실행시 “This program cannot be run in DOS mode”라고 출력되게 되어있다.
따라서 이 부분의 존재 유무는 개발툴에 의존한다.
c. NT header
1
2
3
4
5
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;
NT 헤더는 처음에 PE 라는 시그니처를 4Bytes로 갖고 시작하며 그 아래로는 FileHeader와 OptionalHeader를 갖는다. FileHeader의 구조체는 아래와 같다.
1
2
3
4
5
6
7
8
9
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가지 값이다.
- Machine : CPU 별로 고유한 값이있는데, 32 bit intel의 경우 0x014c, 64bit intel의 경우 0x0200을 갖는다.
- NumberOfSections : 총 세션의 개수이며, 코드, 데이터, 리소스 등이 각각 섹션에 나눠서 저장되기 때문에 최소 1 이상이다.
- sizeOfOptionalHeader : 실제 OptionalHeader의 크기는 정해져있지만 윈도우에서는 이 값을 가지고 OptionalHeader의 크기를 인식한다.
- Characteristic : 실행 가능한지 혹은 DLL 인지 bit OR로 조합되어있으며 내용은 아래와 같다.
값 (16진수) | 플래그 이름 | 설명 |
---|---|---|
0x0001 | IMAGE_FILE_RELOCS_STRIPPED | 재배치 정보(relocations)가 제거됨 |
0x0002 | IMAGE_FILE_EXECUTABLE_IMAGE | 실행 가능한 이미지임 |
0x0004 | IMAGE_FILE_LINE_NUMS_STRIPPED | 라인 번호 정보(line numbers)가 제거됨 |
0x0008 | IMAGE_FILE_LOCAL_SYMS_STRIPPED | 로컬 심볼(local symbols)이 제거됨 |
0x0010 | IMAGE_FILE_AGGRESIVE_WS_TRIM | 워크셋(workset) 공격적 축소 |
0x0020 | IMAGE_FILE_LARGE_ADDRESS_AWARE | 2 GB 초과 주소 공간을 사용할 수 있음 |
0x0080 | IMAGE_FILE_BYTES_REVERSED_LO | 저(低) 바이트 순서가 뒤바뀜(사용 안 함) |
0x0100 | IMAGE_FILE_32BIT_MACHINE | 32비트 머신용 |
0x0200 | IMAGE_FILE_DEBUG_STRIPPED | 디버그 정보가 제거됨 |
0x0400 | IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP | 스왑 파일에서 실행 (이동식 미디어) |
0x0800 | IMAGE_FILE_NET_RUN_FROM_SWAP | 스왑 파일에서 실행 (네트워크 드라이브) |
0x1000 | IMAGE_FILE_SYSTEM | 시스템 파일 |
0x2000 | IMAGE_FILE_DLL | DLL |
0x4000 | IMAGE_FILE_UP_SYSTEM_ONLY | 멀티프로세서 사용자 모드(UP) 시스템 전용 |
0x8000 | IMAGE_FILE_BYTES_REVERSED_HI | 고(高) 바이트 순서가 뒤바뀜(사용 안 함) |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
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;
_IMAGE_OPTIONAL_HEADER에서 주요 값들은 아래의 10개이다.
- Magic : 해당 파일이 32비트(PE32)인지 64비트(PE32+)인지를 나타내는 값
- AddressOfEntryPoint : 프로그램이 시작되는 코드의 가상 주소(RVA)
- ImageBase : 로드될 때 기본적으로 매핑되는 메모리 상의 시작 주소
- SectionAlignment : 메모리에 로드할 때 각 섹션의 정렬 단위(바이트)
- FileAlignment : 디스크상에 저장될 때 각 섹션의 정렬 단위(바이트)
- SizeOfImage : 메모리에 로드된 전체 이미지(헤더 + 모든 섹션 포함)의 크기 (바이트)
- SizeOfHeader : DOS 헤더, PE 헤더 및 모든 섹션 헤더를 합친 크기 (바이트)
- Subsystem : 이 실행 파일이 어떤 환경에서 동작할지를 지정
- NumberOfRvaAndSizes : 데이터 디렉터리(Data Directory)의 엔트리 개수
- DataDirectory : 배열의 각 항목마다 정의된 값을 가지게 되는데 그 값은 아래와 같다.
인덱스 | 디렉터리 이름 | 설명 |
---|---|---|
0 | EXPORT_TABLE | 이 모듈이 외부에 제공(export)하는 함수·데이터의 목록 및 주소 테이블 |
1 | IMPORT_TABLE | 모듈이 참조(import)하는 외부 DLL의 함수·데이터 목록 및 주소 테이블 |
2 | RESOURCE_TABLE | 리소스 섹션(.rsrc)의 시작 주소와 크기 (아이콘, 문자열 테이블, 대화상자 리소스 등) |
3 | EXCEPTION_TABLE | 예외 처리 루틴과 언와인드 정보(64-bit의 경우 사용됨) |
4 | CERTIFICATE_TABLE | 디지털 서명(Certificate Table)의 시작 주소와 크기 |
5 | BASE_RELOCATION_TABLE | 재배치(relocation) 정보(.reloc 섹션)의 시작 주소와 크기 |
6 | DEBUG | 디버그 정보(심볼, 매핑 정보 등)의 시작 주소와 크기 |
7 | ARCHITECTURE | 예약(과거 IA-64용으로 사용되었으나 현재 무의미) |
8 | GLOBAL_PTR | 전역 포인터(global pointer) 테이블 주소 (MIPS 등 특정 아키텍처용) |
9 | TLS_TABLE | Thread-Local Storage 초기화 및 콜백 정보 |
10 | LOAD_CONFIG_TABLE | 보안 쿠키, 스택 검사, SEH 보호 등 로드 구성 정보 |
11 | BOUND_IMPORT | 바인드된 임포트 목록(Import Table과 함께 사용, 로딩 시 바인딩 최적화) |
12 | IAT | 실제 Import Address Table(Import Table과 유사하지만, 실행 시 패치된 주소를 담음) |
13 | DELAY_IMPORT_DESCRIPTOR | 지연 로딩(Delay-Load) DLL의 목록 및 로딩 시점 정보 |
14 | CLR_RUNTIME_HEADER | .NET 어셈블리용 CLR 헤더(.NET 런타임 메타데이터 시작 주소와 크기) |
15 | RESERVED | 예약(향후 확장 또는 특별 용도로 사용될 수 있음) |
d. Section header
각 섹션의 속성을 정의하기 위한 헤더이다. 기본적인 섹션 헤더의 구조는 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define IMAGE_SIZEOF_SHORT_NAME 8
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;
- VirtualSize : 메모리에서 섹션이 차지하는 크기
- VirtualAddress : 메모리에서 섹션의 시작 주소 (RVA)
- SizeOfRawData : 파일에서 섹션이 차지하는 크기
- PointerToRawData : 파일에서 섹션의 시작 위치
- Characteristics : 섹션의 특징 (bit OR), 아래의 표를 참조하면 된다.
값 (16진수) | 플래그 이름 | 설명 |
---|---|---|
0x00000008 | IMAGE_SCN_TYPE_NO_PAD | 섹션 패딩 없음 |
0x00000020 | IMAGE_SCN_CNT_CODE | 실행 코드(.text) 섹션 |
0x00000040 | IMAGE_SCN_CNT_INITIALIZED_DATA | 초기화된 데이터(.rdata/.data) 섹션 |
0x00000080 | IMAGE_SCN_CNT_UNINITIALIZED_DATA | 초기화 안 된 데이터(.bss) 섹션 |
0x00000100 | IMAGE_SCN_LNK_OTHER | 기타 링커 제어 |
0x00000200 | IMAGE_SCN_LNK_INFO | 디버그/설명용 정보 |
0x00000800 | IMAGE_SCN_LNK_REMOVE | 링커가 제거할 섹션 |
0x00001000 | IMAGE_SCN_LNK_COMDAT | COMDAT(overloadable) 섹션 |
0x00008000 | IMAGE_SCN_GPREL | 전역 포인터 상대 주소 사용 |
0x00020000 | IMAGE_SCN_MEM_PURGEABLE | 퓨저블(purgeable) |
0x00040000 | IMAGE_SCN_MEM_LOCKED | 메모리 잠김 |
0x00080000 | IMAGE_SCN_MEM_PRELOAD | 미리 로드 |
0x00100000 | IMAGE_SCN_ALIGN_1BYTES | 1바이트 정렬 |
0x00200000 | IMAGE_SCN_ALIGN_2BYTES | 2바이트 정렬 |
0x00300000 | IMAGE_SCN_ALIGN_4BYTES | 4바이트 정렬 |
0x00400000 | IMAGE_SCN_ALIGN_8BYTES | 8바이트 정렬 |
0x00500000 | IMAGE_SCN_ALIGN_16BYTES | 16바이트 정렬 |
0x00600000 | IMAGE_SCN_ALIGN_32BYTES | 32바이트 정렬 |
0x00700000 | IMAGE_SCN_ALIGN_64BYTES | 64바이트 정렬 |
0x00800000 | IMAGE_SCN_ALIGN_128BYTES | 128바이트 정렬 |
0x00900000 | IMAGE_SCN_ALIGN_256BYTES | 256바이트 정렬 |
0x00A00000 | IMAGE_SCN_ALIGN_512BYTES | 512바이트 정렬 |
0x00B00000 | IMAGE_SCN_ALIGN_1024BYTES | 1 024바이트 정렬 |
0x00C00000 | IMAGE_SCN_ALIGN_2048BYTES | 2 048바이트 정렬 |
0x00D00000 | IMAGE_SCN_ALIGN_4096BYTES | 4 096바이트 정렬 |
0x00E00000 | IMAGE_SCN_ALIGN_8192BYTES | 8 192바이트 정렬 |
0x01000000 | IMAGE_SCN_LNK_NRELOC_OVFL | 재배치 수 초과(OVFL) |
0x02000000 | IMAGE_SCN_MEM_DISCARDABLE | 디스크로부터 제거 가능(discardable) |
0x04000000 | IMAGE_SCN_MEM_NOT_CACHED | 캐시 불가 |
0x08000000 | IMAGE_SCN_MEM_NOT_PAGED | 페이징 불가 |
0x10000000 | IMAGE_SCN_MEM_SHARED | 공유 메모리 |
0x20000000 | IMAGE_SCN_MEM_EXECUTE | 실행 가능 |
0x40000000 | IMAGE_SCN_MEM_READ | 읽기 가능 |
0x80000000 | IMAGE_SCN_MEM_WRITE | 쓰기 가능 |
※ VA? RVA?
몇몇 속성의 주소가 RVA로 나타나있는 것을 확인 할 수 있는데 여기서 RVA란 상대적 가상주소(Relative Virtual Address)를 말한다.
이 RVA를 말하기 위해서는 먼저 VA부터 알아야하는데 VA는 가상 주소(Virtual Address)를 말한다.
아마 OS시간에 페이징과 가상 메모리 주소에 대해서 배웠을 것이다. (혹시 배우지 않았다면? 이곳 을 참고하라)
페이징간에 말하는 가상 메모리 주소가 바로 이 VA인데, RVA는 Optional_header에 있는 ImageBase 주소에서 부터 상대 주소를 말한다. 만약 RVA를 VA로 바꾸고 싶다면 아래와 같이 바꿀 수 있다.
\[RVA + ImageBase = VA\]위와 같은 구조를 따른다면 주소의 relocation이 일어나도 기준 위치(ImageBase)에 대한 상대주소가 바뀐게 아니기 때문에 해당 주소를 찾아갈 수 있다.
2) PE Body
Section
실질적으로 프로그램이 실행될 때 사용되는 코드와 데이터가 저장된 부분이다.
해당 섹션에 대한 크기와 속성은 각 섹션에 해당하는 IMAGE_SECTION_HEADER에 명시되어있다.
아래의 정보는 일반적으로 많이 사용하는 섹션에 대해서 적어둔 것이며, 개발자가 임의의 커스텀된 섹션을 제작할 수도 있다. 물론 그 경우에는 section header에 형식에 따라 관련 내용을 기재해두어야한다.
a. .text 섹션
실제 프로그램이 실행되는 코드이다. 실행 파일을 olly dbg 같은 걸로 뜯어볼때 실제 실행되는 코드들이 포함된 곳이다.
라이브러리 호출이나, 분기, 루프, 조건문 등이 모두 포함되어있으며 컴파일된 함수들이 포함되어있다.
해당 text 섹션의 header의 Characteristics 값은 아래와 같은 값을 포함한다.
- IMAGE_SCN_CNT_CODE : 0x00000020
- IMAGE_SCN_MEM_EXECUTE : 0x20000000
- IMAGE_SCN_MEM_READ : 0x40000000
b. .data 섹션
초기화된 전역 변수나 정적 변수를 담는데 사용되는 섹션이다. 구조체나 배열, 변수등 초기화된 데이터를 담는데 사용되며 주소값이 지정되어야하는 포인터 배열 역시 해당 섹션에 포함된다.
해당 data 섹션의 header의 Characteristics 값은 아래와 같은 값을 포함한다.
- IMAGE_SCN_CNT_INITIALIZED_DATA : 0x00000040
- IMAGE_SCN_MEM_READ : 0x40000000
- IMAGE_SCN_MEM_WRITE : 0x80000000
c. .rdata 섹션
읽기 전용 상수나 포인터 테이블을 포함하는데 사용되는 섹션이다. 문자열 상수나 const로 지정된 값들, 그리고 IAT(Import Address Table), INT(Import Name Table) 값이 포함되있다. (IAT와 INT는 추가 포스팅 예정이다)
해당 rdata 섹션의 header의 Characteristics 값은 아래와 같은 값을 포함한다.
- IMAGE_SCN_CNT_INITIALIZED_DATA : 0x00000040
- IMAGE_SCN_MEM_READ : 0x40000000
d. .rsrc 섹션
아이콘이나, 대화상자, 메뉴, 버전 정보등 리소스를 포함하고 있으며, 다국어 문자열이나 UI 구성요소 역시 해당 섹션에 포함된다.
해당 rsrc 섹션의 header의 Characteristics 값은 아래와 같은 값을 포함한다.
- IMAGE_SCN_CNT_INITIALIZED_DATA : 0x00000040
- IMAGE_SCN_MEM_READ : 0x40000000
e. .reloc 섹션
실행시에 주소 재배치를 위한 정보이다. 이미지가 기본주소가 아닌곳에 로드될 경우를 위한 섹션이라고 할 수 있다.
해당 reloc 섹션의 header의 Characteristics 값은 아래와 같은 값을 포함한다.
- IMAGE_SCN_CNT_INITIALIZED_DATA : 0x00000040
- IMAGE_SCN_MEM_DISCARDABLE : 0x02000000
- IMAGE_SCN_MEM_READ : 0x40000000
f. .pdata 섹션
x64에서 예외처리 테이블을 위한 섹션이다.
해당 pdata 섹션의 header의 Characteristics 값은 아래와 같은 값을 포함한다.
- IMAGE_SCN_CNT_INITIALIZED_DATA : 0x00000040
- IMAGE_SCN_MEM_READ : 0x40000000
3. ELF(Executable and Linkable Format, 유닉스, 리눅스등)
PE 파일 포맷이 그러했듯이 ELF 파일도 범주내에 라이브러리와 같은 파일들이 포함된다. 가령 컴파일 후 어셈블러로 번역된 오브젝트 파일이나 공유 라이브러리 같은 파일 뿐만 아니라 프로세스의 메모리 상태를 저장하는 파일인 코어 덤프 파일도 ELF 구조를 따른다.
이러한 ELF 구조를 그림으로 나타내면 아래와 같다.
크게 볼때 ELF 구조는 ELF 헤더, 프로그램 헤더 테이블, 섹션, 섹션 헤더 테이블로 구성되어있다.
각 요소를 하나씩 알아보겠다.
1) ELF Header
ELF Header는 아래와 같이 정의되어있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
ElfN_Half e_type; /* Object file type */
ElfN_Half e_machine; /* Architecture */
ElfN_Word e_version; /* Object file version */
ElfN_Addr e_entry; /* Entry point virtual address */
ElfN_Off e_phoff; /* Program header table file offset */
ElfN_Off e_shoff; /* Section header table file offset */
ElfN_Word e_flags; /* Processor-specific flags */
ElfN_Half e_ehsize; /* ELF header size in bytes */
ElfN_Half e_phentsize; /* Program header table entry size */
ElfN_Half e_phnum; /* Program header entry count */
ElfN_Half e_shentsize; /* Section header table entry size */
ElfN_Half e_shnum; /* Section header table entry count */
ElfN_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
a. e_ident
오프셋 | 상수 이름 | 의미 | 주요 값(예) 및 설명 |
---|---|---|---|
0x00 | EI_MAG0 | 매직 넘버(식별) 바이트 0 | 0x7F |
0x01 | EI_MAG1 | 매직 넘버(식별) 바이트 1 | 'E' (ASCII 0x45) |
0x02 | EI_MAG2 | 매직 넘버(식별) 바이트 2 | 'L' (ASCII 0x4C) |
0x03 | EI_MAG3 | 매직 넘버(식별) 바이트 3 | 'F' (ASCII 0x46) |
0x04 | EI_CLASS | 파일 클래스 | 0 = ELFCLASSNONE (없음)1 = ELFCLASS32 (32비트)2 = ELFCLASS64 (64비트) |
0x05 | EI_DATA | 데이터 인코딩 방식 | 0 = ELFDATANONE1 = ELFDATA2LSB (리틀엔디안)2 = ELFDATA2MSB (빅엔디안) |
0x06 | EI_VERSION | ELF 헤더 버전 | 0 = EV_NONE (유효하지 않음)1 = EV_CURRENT (현재 버전) |
0x07 | EI_PAD[0] | 패딩의 시작 (0x07~0x0F까지 전부 0) | 향후 확장용으로 예약된 영역 — 반드시 0x00 으로 채움 |
b. 그 외의 값
필드 이름 | 오프셋 | 크기 | 설명 |
---|---|---|---|
e_type | 0x10 | 2바이트 | 파일 타입 (예: ET_EXEC , ET_DYN , ET_REL , ET_CORE ) |
e_machine | 0x12 | 2바이트 | 대상 아키텍처 (예: EM_X86_64 , EM_AARCH64 등) |
e_version | 0x14 | 4바이트 | ELF 버전 (EV_CURRENT = 1) |
e_entry | 0x18 | 8바이트 | 프로그램 진입점 가상주소 (entry point) |
e_phoff | 0x20 | 8바이트 | 프로그램 헤더 테이블 시작 오프셋 |
e_shoff | 0x28 | 8바이트 | 섹션 헤더 테이블 시작 오프셋 |
e_flags | 0x30 | 4바이트 | 아키텍처별 플래그 |
e_ehsize | 0x34 | 2바이트 | ELF 헤더 전체 크기 (바이트) |
e_phentsize | 0x36 | 2바이트 | 프로그램 헤더 한 엔트리 크기 |
e_phnum | 0x38 | 2바이트 | 프로그램 헤더 엔트리 개수 |
e_shentsize | 0x3A | 2바이트 | 섹션 헤더 한 엔트리 크기 |
e_shnum | 0x3C | 2바이트 | 섹션 헤더 엔트리 개수 |
e_shstrndx | 0x3E | 2바이트 | 섹션 이름 문자열 테이블 인덱스 (섹션 헤더 테이블 내 위치) |
2) Program Header Table
1
2
3
4
5
6
7
8
9
10
typedef struct {
Elf64_Word p_type;
Elf64_Word p_flags;
Elf64_Off p_offset;
Elf64_Addr p_vaddr;
Elf64_Addr p_paddr;
Elf64_Xword p_filesz;
Elf64_Xword p_memsz;
Elf64_Xword p_align;
} Elf64_Phdr;
필드 | 타입 | 크기 | 설명 |
---|---|---|---|
p_type | Elf64_Word | 4바이트 | 세그먼트 유형 (PT_LOAD , PT_DYNAMIC , PT_INTERP 등) |
p_flags | Elf64_Word | 4바이트 | 읽기·쓰기·실행 권한 (PF_R /PF_W /PF_X ) |
p_offset | Elf64_Off | 8바이트 | ELF 파일 내 이 세그먼트가 시작하는 오프셋 |
p_vaddr | Elf64_Addr | 8바이트 | 이 세그먼트를 매핑할 가상 메모리 주소 |
p_paddr | Elf64_Addr | 8바이트 | 물리 주소 (일반 유저 공간에서는 무시) |
p_filesz | Elf64_Xword | 8바이트 | 파일에 저장된 데이터 크기 |
p_memsz | Elf64_Xword | 8바이트 | 메모리에 로드될 때 확보할 전체 크기 |
p_align | Elf64_Xword | 8바이트 | 파일·메모리 상의 정렬 경계 (예: 페이지 크기) |
3) Section Header Table
링커가 주로 읽는 테이블로, “섹션(section)” 단위로 코드·데이터를 분리 관리한다.
필드 | 설명 |
---|---|
sh_name | 섹션 이름(문자열 테이블 인덱스) |
sh_type | 섹션 유형(PROGBITS, SYMTAB, STRTAB, RELA 등) |
sh_flags | 속성(ALLOC, WRITE, EXECINSTR 등) |
sh_addr | 메모리 내 가상 주소 |
sh_offset | 파일 내 오프셋 |
sh_size | 섹션 크기 |
sh_link | 연관된 섹션 인덱스 |
sh_info | 추가 정보(심볼 테이블일 때 심볼 개수 등) |
sh_addralign | 정렬 단위 |
sh_entsize | 엔트리 크기(테이블 형 섹션일 때) |
.text: 실행 코드
.data: 초기화된 전역/정적 변수
.bss: 초기화되지 않은 전역/정적 변수 (파일엔 없고, 메모리만 확보)
.rodata: 읽기 전용 상수
.symtab/.dynsym: 심볼 테이블 (정적/동적)
.strtab/.dynstr: 문자열 테이블 (심볼·섹션 이름)
.rel(a).text/.rel(a).data: 재배치(relocation) 정보
참고자료
- Wikipedia - Portable Executable
- 리버스코어 - PE FILE FORMAT
- Microsoft - winnt.h
- notepad.exe 분석
- 보통의 개발자:티스토리 - ELF 파일 구조
- 위키백과 - ELF 파일 구조
- codebrowser - elf.h