250 likes | 943 Views
실행파일 (PE) 의 구조. 정 의. PE(Portable Executable) 정의 : PE 구조로 된 PE 파일들은 플랫폼에 관계없이 Win32 운영 체제가 돌아가는 시스템이면 어디서든 실행 가능하다는 의미 EXE 와 DLL 등의 파일구조를 PE 파일 포맷이라고 명명함 COFF 라는 포맷을 계승한 파일 포맷. PE 파일의 전체구조. IMAGE_DOS_HEADER. DOS HEADER 는 항상 64byte 의 크기를 가지며 윈도우 내에서 IMAGE_DOS_HEADER 라는 구조체로 정의되어
E N D
정의 • PE(Portable Executable) • 정의 : PE구조로 된 PE파일들은 플랫폼에 관계없이 Win32 운영 체제가 돌아가는 시스템이면 어디서든 실행 가능하다는 의미 EXE와DLL등의 파일구조를 PE파일 포맷이라고 명명함 COFF라는 포맷을 계승한 파일 포맷
IMAGE_DOS_HEADER DOS HEADER는 항상 64byte의 크기를 가지며 윈도우 내에서 IMAGE_DOS_HEADER라는 구조체로 정의되어 있다(WinNT.h) DOS HEADER구조체의 처음 e_magic값은 항상 MZ로 고정되며, 마지막 e_lfanew값은 PE 헤더의 시작위치를 가 르키는offset값이다 DOS STUB CODE는 없어도 되는 부분으로서 프로그램을 도스 모드에서 실행시켰을 때 실행되는 코드이며, 일반 적으로 “This program must be run under Microsoft Windows”라는 메시지를 출력하고 종료하는 코드가 삽 입되어 있으며 사이즈는 가변이다.
IMAGE_DOS_HEADER IMAGE_DOS_HEADER
RVA : PE파일 내의 파일 offset과는 무관하며, PE파일이 가상주소 공간에 로드되었을 때의 그 시작주소에 대한 상대주소를 나타냄. 메모리상에서의 PE파일의 시작주소에 대한 offset • PE파일에서의 절대주소 : Imagebase • PE파일에서의 상대주소 : 모든 RVA값 • RVA를 통해서 파일상의 offset구하기 • RVA값이 속한 섹션을 찾는다.(섹션테이블) • 그 섹션의 RVA값과 PointerToRawData값의 차를 구한다.(converter) • 찾고자 하는 RVA값에서 converter값을 뺀다.
IMAGE_NT_HEADERS • Signature(4byte) : PE(50 45 00 00)의 값을 갖는다. • IMAGE_FILE_HEADER(20byte) • Machine:CPU의 ID를 나타낸다.0x14c(IA32) 또는 0x200(IA64) • NumberOfSections :섹션의 개수를 의미한다. • TimeDateStamp : 링커가 해당 파일을 만들어낸 시간을 의미한다. • SizeOfOptionalHeader : 파일헤더 뒤에오는OPTIONAL_HEADER 의 사이즈를 나타낸다.(데이터 디렉토리가 존재할 경우 224byte, 존 재하지 않을경우96byte) • Characteristics : PE파일의 속성을 의미한다. 일반적으로 실행파일의 경우 0x10f값을 가진다.
IMAGE_NT_HEADERS 디버거로 실행(calc.exe) Peview로 calc.exe open • IMAGE_OPTIONAL_HEADER • Magic : IMAGE_OPTIONAL_HEADER를 나타내는 시그너처로서 32bit PE의 경우 0x010b, 64bit의 경우 0x020b값을 가진다. • AddressOfEntryPoint :로더가 실행을 개시할(최초로 실행될 코드) 주소. 이 주소는 RVA로서 보통 .text 섹션의 시작점이다.
IMAGE_NT_HEADERS ImageBase 파일상태 메모리에 로드된 상태 • ImageBase : 해당 PE가 가상주소 공간에 로드될때의 그 시작주소. 기본적으로 EXE파일은 0x00400000, DLL파일은 0x10000000
IMAGE_NT_HEADERS FileAlignment를 맞춰주기 위한 padding SectionAlignment를 맞춰주기 위한 padding 파일상태 메모리에 로드된 상태 파일상태 • SectionAlignment : 메모리상에서 섹션의 각 섹션이 차지해야 하는 최소의 단위. 각 섹션은 SectionAlignment X n 의 주소에 서 시작해야 한다. 기본값으로 4K(0x1000) • FileAlignment : PE파일 내에서의 섹션들의 정렬단위. SectionAlignment와 같은 개념. 보통 0x200 또는 0x100 메모리에 로드된 상태
IMAGE_NT_HEADERS • SizeOfImage : 메모리에 로드된PE Image 전체의 크기를 나타내며, 이 값은 ImageBase 필드가 가리키는 주소값으로부터 해당 PE의 마지막 섹션까지의 크기이다. SectionAlignment 의 배수가 되어야 한다. • SizeOfHeaders : DOS HEADER에서부터 패딩을 포함한 section table까지의 크기의 총합. FileAlignment의 배수가 되어야 한다. • NumberOfRvaAndSizes : IMAGE_DATA_DIRECTORY 구조체 배열 의 원소의 개수를 나타낸다. DATA_DIRECTORY를 가지고 있 다면 16(0x10) 값을 가진다. • IMAGE_DATA_DIRECTORY : Virtualaddress와 Size로 구성된 구조 체로서 마지막 구조체의 값은 0x00으로 채워져 있다.
IMAGE_NT_HEADERS SizeOfImage SizeOfHeaders 파일상태 메모리에 로드된 상태
IMAGE_NT_HEADERS VirtualAddress EXPORT TABLE Size VirtualAddress • 임포트 테이블의 메모리상에서 • 의 시작점과 크기에 대한 정보를 • 가지고 있다. IMPORT TABLE Size ……… • IMAGE_DATA_DIRECTORY
Import_Section Typedef struct _IMAGE_IMPORT_DESCRIPTOR{ union{ DWORD Characteristics; DWORD OriginalFirstThunk; }; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; DWORD FirstThunk; } IMAGE_IMPORT_DESCRIPTOR; 임포트 섹션이 존재하지 않을수도 있으며, 다른 섹션에 포함되어 있을 수 있다. 임포트 섹션이 존재하지 않을 경우 IMAGE_DATA_DIRECTORY내의 IMAGE_DIRECTORY_ENTRY_IMPORT를 통하여 임포트 테이블의 위치를 알아낼수 있다. 임포트 테이블은 IMAGE_IMPORT_DESCRIPTOR라는 구조체의 배열로 구성 되며, 이 구조체는 임포트한 DLL의 정보를 담고 있다.
Import_Section • IMAGE_IMPORT_DESCRIPTOR • OriginalFirstThunk • INT(Import Name Table)(또는 ILT(Import Lookup Table))을 가르키는 RVA값으로서 INT는 IMAGE_THUNK_DATA라는 구조체 의 배열로 구성된다. • 이때 IMAGE_THUNK_DATA배열의 각 원소는 IMAGE_IMPORT_BY_NAME이라는 구조체를 가리키는 RVA값을 가진다. • TimeDateStamp • 시간과 날짜를 나타내는 시간 스탬프이다. • 바인딩 전에는 항상 0이다. • ForwarderChain • 일반적으로 바인딩되지 않은 경우 0이다.
Import_Section • IMAGE_IMPORT_DESCRIPTOR • Name • 임포트된DLL의 이름을 담과 있는 NULL로 끝나는 아스키 문자 열에 대한 RVA값을 가진다. • FirstThunk • OriginalFirstThunk와마찬가지로 IMAGE_THUNK_DATA의 배열 을 가리키며, IMAGE_THUNK_DATA의 배열의 각 원소 역시 IMAGE_IMPORT_BY_NAME을 가리킨다. • PE가 가상주소 공간에 매핑되면 IMAGE_THUNK_DATA는 임포 트한DLL내의 사용할 실제 함수의 주소를 담게 된다. • 실제 함수의 주소를 담고 있는 IMAGE_THUNK_DATA의 이 배열 을 IAT(Import Address Table)이라고 하며, 실제 함수의 주소가 IAT에 설정된 것을 바인딩 되었다고 한다.
Import_Section • IMAGE_THUNK_DATA • AddressOfData • INT와 IAT가 바인딩 전에 모두 IMAGE_IMPORT_BY_NAME 구조 체를 가리키고 있을 때, IMAGE_THUNK_DATA는 AddressOfData 의 의미로 사용되었다고 볼수 있다. • Ordinal • 모듈정의 파일을 통하여 서수 파일을 지정하게 되면 링커는 DLL링크시함수명을 통하지 않고, 서수를 통해서 사용된 함수를 링크하게 된다. • Function • 바인딩 후에 IAT테이블이 실제 함수의 주소로 변경되었을 경우 Function의 의미로 사용된 것이며, INT는 Function의 의미로 사용될 일이 없다. • IAT는 바인딩전에는 AddressOfData, 또는 Ordinal의 의미로 사용되고, 바인딩 후에는 Function의 의미로 사용된다.
Import_Section IMAGE_THUNK_DATA IMAGE_IMPORT_BY_NAME <INT> IMAGE_THUNK_DATA 바인딩 전 바인딩 후 <IAT>
Import_Section 임포트 테이블의 내용 OriginalFirstThunk FirstThunk INT IAT 바인딩 후의 IAT 변경모습
Import_Section • 임포트 과정 • PE파일을 메모리에 로드한 후 데이터 디렉토리의두번째엔트리인IMAGE_DIRECTORY_ENTRY_IMPORT로 부터임포트 테이블의 주소를 구한다. • 임포트 테이블의 Name필드를 통해 임포트 할 DLL의 이름을 알아낸 후, 공간 확보후DLL을 가상주소공간에 맵핑 시킨다. • INT를 통해서 임포트 할 함수의 이름 또는 서수(Ordinal)값을 알아낸다. • 위의 정보를 이용하여 해당 DLL의 익스포트 테이블을 통해 필요한 함수의 실제 주소를 알아내서 IAT를 수정한다.
IMAGE_SECTION_HEADER Typedef struct _IMAGE_SECTION_HEADER{ BYTE Name[8]; 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; • IMAGE_SECTION_HEADER는 40byte로 구성되며다음과 같이 정의된다.
IMAGE_SECTION_HEADER • DWORD Name • 섹션의 아스키 이름을 나타낸다. 8바이트까지 지정가능하며, NULL문자는 제외된다. 로딩과정과 상관이 없다. • DWORD VirtualAddress • PE에서 해당 섹션을 매핑시켜야 할 가상 주소 공간 상의 RVA값이다. • 메모리에서 본 해당 섹션의 시작주소를 의미한다. • DWORD SizeOfRawData • 파일 상태에서의 섹션의 사이즈 값(패딩된 상태) • FileAlignment값의 배수이다. • DWORD PointerToRawData • 파일 상태에서의 섹션이 시작하는 offset값이다. • DWORD Characteristics • 해당 섹션의 속성을 나타내는 플래그의 집합이다.
IMAGE_SECTION_HEADER PointerToRawData가 지정한 곳에서 부터 SizeOfRawData만큼의 데이터를 읽어들여 VirtualAddress가 가르키는 곳에 맵핑한 후에 Characteristics에 설정된 속성 정보적용 파일상태 메모리에 로드된 상태
SECTION 섹션은 같은 성질의 데이터들을 로더가 구분할 수 있도록 저정해둔 영역을 말한다.
참고 자료 • Zesrever의 지식펌프 • 조립하면서 배우는 PE • http://zesrever.xstone.org/ • Null2root • PE포멧 분석(PDF) • http://hdp.null2root.org/ • Windows 시스템 실행파일의 구조와 원리 – 이호동 저