| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- Dos Stub
- Python
- __stdcall
- Rich Header
- 리버싱
- 함수 호출 규약
- pe format
- __vectorcall
- 프로그래머스
- ABI
- CodeEngn
- x32
- 32bit
- 코드엔진
- image section header
- __cdecl
- 리치헤더
- Programmers
- Reversing
- rev
- x64
- RVA
- 파이썬
- Image dos header
- stack frame
- 크랙미
- Calling Convention
- crackme
- __fastcall
- 실행파일
- Today
- Total
kj0on
[Definition] 64비트 윈도우 실행 파일 구조 (PE+ Format) 본문
0. 32비트 윈도우 실행 파일 구조 (PE Format)
32비트 윈도우 실행 파일 구조에 대한 자세한 설명은 https://kj0on.tistory.com/21 참고
이 글은 32비트 Windows 실행 파일의 구조와 비교하여 그 차이점을 중심으로 설명한다.
1. 기준 정의
동일 플랫폼 내에서도 일관성이 결여된 멤버는 32비트 구조와 64비트 구조를 비교하는 기준으로 적합하지 않으므로 제외해야 한다. 차이점을 보다 체계적으로 파악할 수 있도록 다음과 같은 기준을 정한다.
1-1. 값
동일한 멤버라도 32비트와 64비트에서 기본값이 달라지는 경우가 있다. 이러한 차이는 로더의 해석 및 실행 동작에 직접적인 영향을 미치므로 각 플랫폼 고유의 기본값이 나타나는지를 살펴본다. 다만 양쪽 플랫폼 모두에 존재할 수 있는 값이라도 그 차이가 플랫폼별 설계 및 동작 차이에 기인한다면 해당 멤버를 비교 대상에 포함한다.
1-2. 크기
동일한 의미를 지니는 멤버라도 자료형이 차지하는 바이트 수가 다르면 구조체 내 오프셋이 변경되어 헤더 레이아웃이 달라진다. 이러한 레이아웃 변화를 파악하기 위해 크기 차이가 있는 멤버를 비교한다.
1-3. 존재 여부
특정 플랫폼에서만 정의되고 반대 플랫폼에는 아예 존재하지 않는 멤버는 구조 설계상의 근본적인 차이를 드러낸다. 따라서 비교 분석 시 이러한 유무 차이도 확인하여 핵심적인 구조적 차이를 살펴본다.
2. PE+ Format - HEADERS
2-1. IMAGE_FILE_HEADER

2-1-1. 영역

2-1-2. 구조체
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;
2-1-3. 상이 멤버
| 변수명 | 자료형 | 크기(BYTE) | 설명 |
| Machine | WORD | 2 | 컴퓨터의 아키텍처 유형입니다. 이미지 파일은 지정된 컴퓨터 또는 지정된 컴퓨터를 에뮬레이트하는 시스템에서만 실행할 수 있습니다. 이 멤버는 다음 값 중 하나일 수 있습니다. 유효한 컴퓨터 유형 및 지원되는 아키텍처의 전체 목록은 PE 형식 설명서를 참조하세요. |
| NumberOfSections | WORD | 2 | 섹션 수입니다. 이는 머리글 바로 뒤에 오는 섹션 테이블의 크기를 나타냅니다. Windows 로더는 섹션 수를 96으로 제한합니다. |
| SizeOfOptionalHeader | WORD | 2 | 선택적 헤더의 크기(바이트)입니다. 개체 파일의 경우 이 값은 0이어야 합니다. |
| Characteristics | WORD | 2 | 이미지의 특징입니다. 이 멤버는 다음 값 중 하나 이상일 수 있습니다. |
https://learn.microsoft.com/ko-kr/windows/win32/api/winnt/ns-winnt-image_file_header
3-1-4. 상이 멤버 (상세)
3-1-4-1. Machine

Machine 멤버는 실행 파일이 어떤 아키텍처에서 실행될 것인지를 나타내는 필드다. 이 필드는 대상 CPU 아키텍처에 따라 고유한 값을 갖도록 정의되어 있으며 32비트와 64비트 플랫폼 간의 구조 차이를 직접적으로 반영한다. 32비트에서는 0x014C(IMAGE_FILE_MACHINE_I386)가 사용되며 이는 Intel x86 아키텍처를 의미한다. 반면 64비트에서는 0x8664(IMAGE_FILE_MACHINE_AMD64)가 지정되어 x64 아키텍처에서 실행됨을 명시한다. 이 차이는 전혀 다른 명령어 집합 구조(Instruction Set Architecture, ISA)를 대상으로 하기 때문에 발생하며 윈도우로더는 이 값을 통해 해당 실행 파일을 현재 시스템에서 실행할 수 있는지 판단한다. 따라서 Machine 필드는 32비트와 64비트 PE 파일 간의 가장 근본적인 구조 차이를 드러내는 멤버로 간주할 수 있다.
3-1-4-2. NumberOfSections

NumberOfSections 멤버는 PE 파일 내에 존재하는 섹션의 개수를 나타내는 필드다. 일반적으로 이 값은 링커의 설정과 빌드 환경에 따라 결정되므로 플랫폼 간 차이라기보다는 빌드 결과의 차이에 불과할 수 있다. 하지만 비교에서 이 멤버를 포함한 이유는 64비트 PE 파일에서 .pdata 섹션이 추가로 존재하는 경향이 있기 때문이다. .pdata는 예외 처리 테이블을 담고 있는 섹션으로 특히 x64에서는 SEH(Structured Exception Handling) 대신 프레임 기반의 예외 처리 방식을 채택함에 따라 해당 섹션이 필수적으로 생성된다. 반면 32비트에서는 이 섹션이 없거나 예외 처리를 다른 방식으로 구현하기 때문에 생성되지 않는 경우가 많다. 따라서 NumberOfSections 값의 증가는 단순 수치 변화가 아닌 .pdata 섹션 도입이라는 구조적 차이를 반영하고 있다고 볼 수 있다.
3-1-4-3. SizeOfOptionalHeader

SizeOfOptionalHeader 멤버는 IMAGE_OPTIONAL_HEADER의 크기를 바이트 단위로 나타낸다. 이 값은 고정된 크기의 구조체가 아니라 플랫폼에 따라 포함되는 필드와 그 크기가 달라지기 때문에 32비트와 64비트에서 서로 다르게 설정된다. 이러한 차이는 64비트 구조가 필요한 필드만 남기고 정리된 구조로 설계된 결과이며 해당 멤버는 PE 헤더 레이아웃 분석의 기준점이 되기 때문에 크기 차이를 통해 플랫폼별 구조 정의 차이를 분명히 드러낸다.

SizeOfOptionalHeader 멤버는 32비트에서 0x00E0 64비트에서 0x00F0으로 설정되어있다. 이러한 값의 차이는 구조체 내 필드 구성 및 자료형 크기 차이로 인한 결과이며 32비트와 64비트 플랫폼 간 IMAGE_OPTIONAL_HEADER 구조의 차이를 보여준다.
3-1-4-4. Characteristics

Characteristics 멤버는 해당 PE 파일의 전반적인 속성과 동작 방식을 플래그 형태로 지정한다. 32비트에서 나타나는 0x0102 값은 IMAGE_FILE_EXECUTABLE_IMAGE와 IMAGE_FILE_32BIT_MACHINE 플래그가 함께 설정된 것으로 이 PE 파일이 32비트 아키텍처용임을 명시한다. 반면 64비트에서는 IMAGE_FILE_EXECUTABLE_IMAGE와 IMAGE_FILE_LARGE_ADDRESS_AWARE 플래그의 조합이다. IMAGE_FILE_32BIT_MACHINE 플래그는 당연히 제거되고 대신 64비트에서는 2GB 이상의 주소 공간을 활용할 수 있도록 IMAGE_FILE_LARGE_ADDRESS_AWARE 속성이 기본 활성화된다. 이처럼 Characteristics의 차이는 단순한 값의 변경이 아니라 아키텍처별 지원 기능과 실행 환경의 설계 차이를 반영하며 플랫폼 고유의 실행 모델 차이를 명확히 보여주는 멤버다.
3-2. IMAGE_OPTIONAL_HEADER64

3-2-1. 영역

3-2-2. 구조체
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG 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;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
https://learn.microsoft.com/ko-kr/windows/win32/api/winnt/ns-winnt-image_optional_header64
3-2-3. 상이 멤버
| 변수명 | 자료형 | 크기(BYTE) | 설명 |
| Magic | WORD | 2 | 이미지 파일의 상태입니다. 이 멤버는 다음 값 중 하나일 수 있습니다. |
| BaseOfData | 이미지 베이스를 기준으로 데이터 섹션의 시작 부분에 대한 포인터입니다. | ||
| ImageBase | ULONGLONG |
8 | 메모리에 로드될 때 이미지의 첫 번째 바이트의 기본 주소입니다. 이 값은 64K 바이트의 배수입니다. DLL의 기본값은 0x10000000. 애플리케이션의 기본값은 0x00010000 Windows CE 제외하면 0x00400000. |
| DllCharacteristics | WORD | 2 | 이미지의 DLL 특성입니다. 다음 값이 정의됩니다. |
| SizeOfStackReserve | ULONGLONG | 4 | 스택에 대해 예약할 바이트 수입니다. SizeOfStackCommit 멤버가 지정한 메모리만 로드 시 커밋됩니다. 나머지는 이 예약 크기에 도달할 때까지 한 번에 한 페이지씩 사용할 수 있습니다. |
| SizeOfStackCommit | ULONGLONG | 4 | 스택에 커밋할 바이트 수입니다. |
| SizeOfHeapReserve | ULONGLONG | 4 | 로컬 힙에 대해 예약할 바이트 수입니다. SizeOfHeapCommit 멤버가 지정한 메모리만 로드 시 커밋됩니다. 나머지는 이 예약 크기에 도달할 때까지 한 번에 한 페이지씩 사용할 수 있습니다. |
| SizeOfHeapCommit | ULONGLONG | 4 | 로컬 힙에 대해 커밋할 바이트 수입니다. |
3-2-4. 상이 멤버 (상세)
3-2-4-1. Magic

Magic 멤버는 해당 PE 파일이 32비트용인지 64비트용인지를 구분하는 식별자다. 32비트에서는 0x010B(PE)가 사용되고 64비트에서는 0x020B(PE+)가 사용된다. 이 값은 구조체 해석 방식에 직접적인 영향을 주며 윈도우로더는 이 값을 기반으로 이후의 Optional Header를 IMAGE_OPTIONAL_HEADER32로 파싱할지 IMAGE_OPTIONAL_HEADER64로 파싱할지를 결정한다. Magic 필드의 차이는 단순한 구분이 아니라 전체 PE 구조의 해석 방향을 바꾸는 역할을 하므로 플랫폼에 따라 명확히 구분되어야 하며 그 존재 자체가 구조 설계 차이의 핵심을 보여준다.
3-2-4-1. BaseOfData

BaseOfData 멤버는 IMAGE_OPTIONAL_HEADER32 구조체에만 존재하고 64비트인 IMAGE_OPTIONAL_HEADER64에는 포함되지 않는다. 이 필드는 초기화된 데이터 섹션의 시작 주소를 명시하며 32비트에서는 BaseOfCode와 함께 코드와 데이터의 메모리 배치를 구분하는 데 사용된다. 그러나 64비트에서는 ImageBase가 64비트로 확장 되면서 구조체 크기 최적화를 위해 BaseOfData 멤버가 64비트 구조에서 제거되었다(https://wiki.osdev.org/PE). BaseOfData는 런타임에서 실질적으로 사용되지 않는 필드로 간주되기 때문에 삭제되더라도 프로그램의 실행에는 영향을 주지 않는다. BaseOfData처럼 특정 플랫폼에만 존재하는 멤버는 32비트와 64비트 PE 포맷 간의 구조적 차이를 파악하는데 있어 중요하다.
3-2-4-1. ImageBase

ImageBase 멤버는 PE 파일이 메모리에 로드될 때 기준이 되는 가상 주소를 나타내는 필드다. 32비트에서는 DWORD(4byte)로 정의되며 주소 지정 범위가 제한된다. 반면 64비트에서는 ULONGLONG(8byte)으로 확장되며 이는 64비트 아키텍처에서 제공하는 더 넓은 가상 주소 공간을 활용하기 위함이다. 이러한 자료형의 차이는 단순한 크기 확장이 아니라 플랫폼마다 가상 메모리를 다루는 방식과 요구사항이 전혀 다르다는 점에서 기인한 것으로 32비트와 64비트 실행 환경에 따라 구조체 내 멤버의 크기가 달라질 수 있음을 보여준다.
3-2-4-1. DllCharacteristics

| 상수 | 값 | 의미 |
| IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | 0x0020 | 이미지에서 높은 엔트로피 64비트 가상 주소 공간을 처리할 수 있습니다. |
https://learn.microsoft.com/ko-kr/windows/win32/debug/pe-format#dll-characteristics
DllCharacteristics 멤버는 실행 파일이나 DLL의 보안 및 로딩 동작에 영향을 주는 다양한 특성을 플래그 형태로 지정한다. 값의 차이는 IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA(0x0020) 플래그의 유무에서 비롯된다. 이 플래그는 64비트 시스템에서 ASLR(Address Space Layout Randomization) 적용 시 더 넓은 가상 주소 공간을 활용해 모듈의 로드 주소를 높은 엔트로피로 랜덤화할 수 있음을 나타낸다. 반면 32비트 환경에서도 ASLR은 지원되지만 가용 주소 공간이 제한되어 있어 랜덤화 가능한 비트 수가 적고 효과도 제한적이다. 이러한 구조적 한계로 인해 32비트에서는 HIGH_ENTROPY_VA 플래그가 실질적으로 의미가 없으며 기능적으로도 작동하지 않는다. 해당 플래그는 /HIGHENTROPYVA 옵션에 의해 결정되며 32비트에는 적용되지 않고 무시된다. MSVC 링커는 64비트 실행 파일에 /HIGHENTROPYVA 옵션을 기본 활성화 하기 때문에 DllCharacteristics 값의 차이가 발생하는 것이다(https://learn.microsoft.com/ko-kr/cpp/build/reference/highentropyva-support-64-bit-aslr?view=msvc-170). 이 차이는 기능 유효성의 차이와 플랫폼의 주소 공간 설계에서 기인한 구조적 차이로 해당 플래그가 64비트에서만 유효하게 작동하고 32비트에서는 형식적으로만 포함될 수 있는 이유를 잘 보여준다.
3-2-4-1. SizeOfStackReserve

SizeOfStackReserve 멤버는 프로세스가 시작될 때 첫 스레드를 위해 예약해 두는 스택의 가상 주소 공간 크기를 나타낸다.32비트에서는 이 필드가 DWORD(4byte) 형식으로 정의되어 있으며 스택의 최대 크기를 4GB 이내로 제한한다. 반면 64비트에서는 ULONGLONG(8byte) 형식으로 확장되어 훨씬 더 큰 스택 영역을 예약할 수 있다. 이러한 자료형의 차이는 플랫폼별 주소 공간 설계와 실행 환경에 따른 요구사항을 반영한 결과이며 32비트와 64비트 실행 환경에 따라 구조체 내 멤버의 크기가 달라질 수 있음을 보여준다.
3-2-4-1. SizeOfStackCommit

SizeOfStackCommit은 프로세스 시작 시 실제로 커밋되는 스택 메모리의 초기 크기를 나타내는 필드다. 32비트에서는 이 필드가 DWORD(4byte) 형식으로 정의되어 있어 커밋 가능한 최대 크기가 4GB로 제한된다. 반면 64비트에서는 ULONGLONG(8byte) 형식으로 확장되어 더 큰 스택 메모리를 정밀하게 설정할 수 있도록 설계되었다. 이러한 자료형의 차이는 플랫폼별 주소 공간 설계와 실행 환경에 따른 요구사항을 반영한 결과이며 32비트와 64비트 실행 환경에 따라 구조체 내 멤버의 크기가 달라질 수 있음을 보여준다.
3-2-4-1. SizeOfHeapReserve

SizeOfHeapReserve 멤버는 프로세스 시작 시 생성되는 기본 힙에 대해 예약 해 둘 가상 주소 공간의 크기를 나타낸다. 32비트에서는 이 필드가 DWORD(4byte) 형식으로 정의되어 있으며 힙의 최대 크기를 4GB 이내로 제한한다. 반면 64비트에서는 ULONGLONG(8byte) 형식으로 확장되어 훨씬 더 큰 힙 영역을 예약할 수 있다. 이러한 자료형의 차이는 플랫폼별 주소 공간 설계와 실행 환경에 따른 요구사항을 반영한 결과이며 32비트와 64비트 실행 환경에 따라 구조체 내 멤버의 크기가 달라질 수 있음을 보여준다.
3-2-4-1. SizeOfHeapCommit

SizeOfHeapCommit은 프로세스가 시작될 때 기본 힙에 대해 커밋할 실제 메모리의 초기 크기를 지정하는 필드다. 32비트에서는 이 필드가 DWORD(4byte) 형식으로 정의되어 있어 커밋 가능한 최대 크기가 4GB로 제한된다. 반면 64비트에서는 ULONGLONG(8byte) 형식으로 확장되어 더 큰 힙 메모리를 정밀하게 설정할 수 있도록 설계되었다. 이러한 자료형의 차이는 플랫폼별 주소 공간 설계와 실행 환경에 따른 요구사항을 반영한 결과이며 32비트와 64비트 실행 환경에 따라 구조체 내 멤버의 크기가 달라질 수 있음을 보여준다.
4. 실습파일
위 모든 이미지는 HelloWorld64.exe 파일을 분석한 결과다.
'Reversing > Definition' 카테고리의 다른 글
| [Definition] VA & RVA & RAW (0) | 2025.07.06 |
|---|---|
| [Definition] Rich Header (3) | 2025.07.05 |
| [Definition] MS-DOS Stub Program (0) | 2025.07.04 |
| [Definition] 32비트 윈도우 실행 파일 구조 (PE Format) (2) | 2025.07.03 |
| [Definition] COFF (Common Object File Format) (1) | 2025.07.01 |