007 PE File Format
I. PE(Portable Executable) File
: Microsoft에서 만든 Windows 운영체제(3.1 이후)에서 사용되는 32bit 형태의 실행 파일 형식, UNIX의 COFF(Common Object File Format) 기반
: PE32 (64bit는 PE+ 혹은 PE32+)
* 해당 게시물은 32bit를 기준으로 작성되었다.
i. Type
실행 가능 파일 : 실행 계열, 드라이버 계열, 라이브러리 계열 (단, 실행 계열만 셸에서도 실행)
ii. RVA
: Relative Virtual Address, ImageBase(기준 위치)에서부터의 상대 주소
> VA & RVA
> VA : Virtual Address, 프로세스 가상 메모리의 절대 주소
→ VA = RVA + ImageBase
※ RVA를 쓰는 이유 : 프로세스 가상 메모리 특정 위치에 로딩되려 할 때 해당 위치에 다른 PE 파일이 있을 경우 Relocation을 하기 위해
> RVA to RAW
: PE 파일이 메모리에 로딩됐을 때 각 섹션에서의 메모리 주소(RVA)와 파일 offset Mapping
: RVA가 속해있는 섹션 찾기 → 비례식을 사용하여 파일 offset(RAW) 계산
: RAW - PointerToRawData = RVA - VirtualAddress
RAW = RVA - VirtualAddress + PointerToRawData
iii. Basic Structure
→ 파일/메모리에서 섹션의 시작 위치는 각 파일/메모리의 최소 기본 단위의 배수에 해당하는 위치, 빈 공간은 NULL padding으로 채움, 구조체로 구성
※ Image : 메모리에 로딩된 상태
> DOS Header
: 하위 호환성 위해 DOS EXE Header 확장시킨 IMAGE_DOS_HEADER 구조체, 40의 크기, PE 구조 여부
> e_magic : DOS Signature, "4D5A"("MZ")
! 없으면 PE 파일이 아니거나 패킹된 것
> e_lfanew : IMAGE_NT_HEADERS(NT Header 구조체)의 offset(위치) 표시
> DOS Stub
: 코드와 데이터의 혼합으로 구성, 부정 크기, 존재 여부는 옵션
cf) 16bit에서는 40~4D 영역이 어셈블리 명령어
> IMAGE_NT_HEADERS
: NT Header 구조체, 3개로 구분
> Sinature : "50450000"("PE"00)
! 없으면 PE 파일이 아니거나 패킹된 것
> File Header
: IMAGE_FILE_HEADER, 파일의 개략적인 속성
> Machine : CPU 고유 값, 파일 동작 환경
→ IA-32 : 14Ch, IA-64 : 200h
> NumberOfSections : 섹션의 개수, 최소 1개 이상, 실제 섹션 개수와 다를 경우 실행 에러
> SizeOfOptionalHeader : NT Header의 IMAGE_OPTIONAL_HEADER32 구조체 크기 (64bit는 IMAGE_OPTIONAL_HEADER64 구조체)
> Characteristics : 파일 속성 및 형식, 실행 가능 형태 및 DLL 파일 여부 등의 정보들을 bit OR 형식으로 조합
→ 0002h(Executable, obj나 DLL의 경우 없음), 2000h(File_DLL)
→ Machine, NumberOfSections, SizeOfOptionalHeader, Characteristics가 정확히 세팅돼있지 않으면 파일 정상 실행되지 않음
> TimeDateStamp : 해당 파일의 빌드 시간, 개발 도구와 그 옵션에 따라 VB나 VC++는 이 값을 세팅해주지만, Delphi는 이 값을 세팅해주지 않음
> Optional Header
: PE 헤더 구조체 중 가장 큰 크기
> Magic : Optional Header의 비트 수, 32bit에서 10Bh, 64bit에서 20Bh
> AddressOfEntryPoint : EP(Entry Point)의 RVA 주소 값, 프로그램에서 최초로 실행되는 코드의 시작 주소
> ImageBase : 메모리에서 PE 파일이 로딩/매핑되는 시작 주소, 메모리 적재 전엔 0
: EXE, DLL 파일은 user memory 영역인 0~7FFFFFFF 범위 에 로딩 SYS 파일은 kernel memory 영역인 80000000~FFFFFFFF 범위에 로딩
: 일반적으로 개발 도구(VB/VC++/Delphi)들이 만든 EXE 파일의 Image Base 값은 00400000, DLL 파일의 IrnageBase값은 10000000
→ PE 로더는 PE 파일을 실행시키기 위해 프로세스 생성하고 메모리에 로딩한 후 EIP 값을 ImageBase+AddressOfEntryPoint 값으로 세팅
> FileAlignment, SectionAlignment : 파일에서의 섹션 최소 단위, 메모리에서의 섹션 최소 단위
> SizeOfImage : PE 파일이 메모리에 로딩됐을 때 가상 메모리에서 PE Image의 크기
> SizeOfHeader : PE 헤더의 크기, FileAlignment의 배수, 파일 시작에서 SizeOfHeader offset만큼 떨어진 위치가 첫번째 섹션 위치
> Subsystem : 시스템 종류가 시스템 드라이버(SYS)인 지 일반 실행(EXE, DLL)인 지 구분
> NumberOfRvaAndSizes : NT Header의 IMAGE_OPTIONAL_HEADER32 구조체의 DataDirectory 배열의 개수, 보통 10h
> DataDirectory
→ EXPORT, IMPORT, RESOURCE, TLS Directory
→ 위 값들이 정확히 세팅돼있지 않으면 파일 정상 실행되지 않음
> IMAGE_SECTION_HEADER
: 각 섹션의 속성(File/Memory에서의 시작 위치, 크기, 엑세스 권한 등)
> VirtualSize, VirtualAddress, SizeOfRawData, PoinToRawData, Charateristics :
> VirtualAddress, PoinToRawData : 각각 IMAGE_OPTIONAL_HEADER32의 SectionAlignment와 FileAlignment에 맞게 결정
> Characteristics : 아래 값들의 bit OR 조합으로 형성
→ CNT_CODE : 코드 섹션
→ CNT_INITIALIZED_DATA, CNT_UNINITIALIZED_DATA : 데이터가 초기화, 비초기화된 섹션
→ MEM_EXECUTE, MEM_READ, MEM_WRITE : 실행, 읽기, 쓰기가 가능한 섹션
> Name : NULL로 끝나야한다거나 ASCII값만 와야한다는 제한이나 규칙 x