기술문서 08.10.24. 작성 PE 구조분석 Windows 계열 OS 의파일구성 (PE: Portable Executable) 작성자 : 동명대학교 THINK 강동현 (cari2052@gmail.com) 0. 시작하면서 -------------------------------------------------------------------------- p.02 1. PE 구성 ---------------------------------------------------------------------------- p.03 2. DOS Header ------------------------------------------------------------------------- p.05 2-1. DOS Header 구조체 3. DOS Stub Code ----------------------------------------------------------------------- p.07 4. PE Header -------------------------------------------------------------------------- p.08 4-1. PE Header 구조체 4-2. IMAGE_FILE_HEADER 구조체 4-3. IMAGE_OPTIONAL_HEADER 구조체 4-4. IMGAE_DATA_DIRECTORY 구조체 5. IMPORT ---------------------------------------------------------------------------- p.14 6. EXPORT---------------------------------------------------------------------------- p.16 6-1. EXPORT Table 6-2 EXPORT 방식 7. Section Table ------------------------------------------------------------------------ p.17 8. 맺음말 ----------------------------------------------------------------------------- p.19 9. 참고자료 --------------------------------------------------------------------------- p.20 10. 부록 ------------------------------------------------------------------------------ p.21 동명대학교정보보호동아리 -1-
0. 시작하면서본문서는파일구조에관한좀더세심하게관찰하기위한사람과, 프로그램역분석공부를시작하는사람들에게도움이되고자하는동기로시작하여작성된문서이다. Windows 계열 OS 에서사용하는 PE 구조에관한전체적인설명을담고있으며각구조체들의필드의미와설명을첨가하였다. 본문서를이해하려면아래와같은기반지식이요구된다. 구조체, 공용체 HEX 코드에관한기본지식 메모리의가상주소개념. Little Endian 과 Big Endian. 본문서에서아래와같은프로그램을사용하였다. VXI32.exe function_if.exe, function_printf.exe (by Visual Studio 2005) -2-
1. PE 구성 DOS Header, DOS Stub code, PE Header, Section Table + 1 개이상의 section 으로이루어져 있다. < 그림 1> PE 구조 디스크상의프로그램구성과메모리상의프로세스의구성은비슷하다. 하지만메모리상 프로세스의구조의 Section 들은유동적공간에할당받게된다. 이모든데이터가파일형식의구조로이루어져있으며, 내부 HEX 코드들의각위치에 해당하는의미는미리정의되어있는구조체로데이터를인식한다. -3-
< 그림 2> 파일내부 PE 구조 위의 < 그림 2> 는디스크상의 PE 구조 (DOS Header, DOS Stub Code, PE Header 등그위치 ) 를 표시하고있다. 위의 < 그림 2> 의박스이후부터 ( 주소 1D0 부터 ) 40bytes 까지는 section table 의영역이다. -4-
2. DOS Header 64bytes 의고정된크기를가지며, 디스크상의첫부분과메모리상 ImageBase 에위치하고있다. ( 아래의그림은 XVI32.exe 로간단한 if 문이코딩되어있는파일을열어서확인한사진임.) < 그림 3> MZ in DOS Header DOS Header의처음 2bytes는 DOS Header구조체의 e_magic 인자로대부분 MZ로시작한다. 이는 DOS 개발자가운데한명인 Mark Zbikowski 의이니셜로 DOS Header 의시그니쳐로사용하고있다. 그리고 DOS Header 의마지막 4bytes 는 DOS Hedaer 구조체의 e_lfanew 로 PE Header 의주소값을표기하고있다. -5-
2-1. DOS Header 구조체. typedef struct _IMAGE_DOS_HEADER { WORD e_magic; 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; } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; 이전설명한첫 2bytes 로 DOS Header 의식별자와, 마지막의 e_lfanew 값으로 PE Header 주소를알아볼수있다. WORD 와 LONG 은각각 16 비트 32 비트로 2bytes, 4bytes 크기를가지고있다. 이를계산해보면 WORD 형데이터분할 30 개, LONG 1 개로 60bytes +4bytes = 64bytes 의고정된공간을할당받게된다는것을알수있다.. -6-
3. DOS Stub Code 시작점 ( 파일의첫부분이나메모리상의 ImageBase) 에서부터 DOS Header 의사이즈인 64bytes 만큼떨어진곳에서찾을수있다. DOS mode 에서실행되며현재사용하지않는부분이다. 현재는사용하지않으며 Hex 에디터들로분석해보면 "This program cannot be run in DOS mode.." 이라는문자열을볼수있다. 32bit 프로그램을도스 (16bit) 에서실행할때위의문자열을출력해주는데쓰이고있다. < 그림 4> DOS Stub Code -7-
4. PE Header DOS Header에있는 e_lfanew 값을확인하여알수있다. -> e_flanew : 4bytes 크기의 LONG 변수로서 DOS Header 의마지막에위치한다. 즉, 64bytes 의 DOS Header 시작점에서 60bytes 만큼옮기면 e_flanew 값을확인할수있다. (ps. PE header 는 50 45 00 00 부터시작한다.) < 그림 5> PE Header 시작위치 -8-
4-1. PE Header 구조체 typedef struct _IMAGE_NT_HEADERS{ Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER OptionalHeader; } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS; PE Header 구조체는 Signature, FIleHeader, OptionalHeader 으로이루어져있으며, 각각 프로그램의기본속성정보가들어있다. FileHeader 와 OptionalHeader 는각각 IMAGE_FILE_HEADER, IMAGE_OPTIONAL_HEADER 구조체로이루어져있다. 4-2. IMAGE_FILE_HEADER 구조체 typedef struct _IMAGE_FILE_HEADER { WORD WORD WORD WORD Machin; NumberOfSections; TimeDateStamp; PointToSymbolTable; NumberOfSymbols; SizeOfOptionalHeader; Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; PE Header 구조체의 File Header 는프로그램의종류, 호환플랫폼그리고섹션의개수등의 정보가입력되어있다. Machine : 어떤플랫폼에사용되는지를표기. NumberOfSections: 프로그램의섹션의개수. TimeOfStamp: 파일생성날짜 SizeOfOptionalHeader: IMAGE_OPTIONAL_HEADER 구조체의크기 Characteristics: 파일의종류에대한플래그 (exe 파일 or dll 파일 ) -9-
4-3. IMAGE_OPTIONAL_HEADER 구조체 typedef struct _IMAGE_OPTIONAL_HEADER { // // Standard fields. // WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; SizeOfCode; SizeOfInitializedData; SizeOfUninitializedData; AddressOfEntryPoint; BaseOfCode; BaseOfData; // // NT additional fields. // ImageBase; SectionAlignment; FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; Win32VersionValue; SizeOfImage; SizeOfHeaders; CheckSum; WORD Subsystem; WORD DllCharacteristics; -10-
SizeOfStackReserve; SizeOfStackCommit; SizeOfHeapReserve; SizeOfHeapCommit; LoaderFlags; NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; PE 구조체의 Optional Header 는프로그램의논리적구조에대한중요한정보들이저장되어 있다. 30 개의필드와하나의데이터디렉토리로구성되어있으며중요한몇몇필드만이해하면된다. Magic: Optional Header 를구분하는시스니쳐 SizeOfCode : 보통.text 섹션의 CPU 실행기계어코드의전체크기 SizeOfInitiallzedData : 섹션영역에서읽기, 쓰기가불가능하고데이터가초기화되어있는섹션영역들의크기 SizeOfUninitiallzedData : SizeOfInitiallzeData 의반대개념 AddressOfEntryPoint : PE 파일이메모리에로드된후가장먼저실행되어야하는코드의주소 BaseOfCode : 코드영역의첫번째바이트주소 BaseOfData :.data 섹션의시작주소 (RVA 값 1 ) ImageBase : PE 파일이메모리에매필될시작주소. 보통 exe 파일은 0x00400000, dll 파일은 0x10000000 값을가진다. SectionAlignment : 메모리상에올려진후의섹션의배치간격 2 FileAlgnment : SectionAlignment 와유사하며, PointerToRawData 에기준으로사용한다. MajorSubsystemVersion, MinorSubsystemVersion : 1 RVA(Relative Virtual Address): 기준이되는가상주소로부터얼마만큼떨어져있는위치인지나타내는것. 2 SectionAlignment 의값단위로섹션의크기를할당한다. -11-
Win32 애플리케이션인경우 MajorSubsystemVersion 은 4 MinorSubsystem 은 0 이어야한다. Subsystem : Console 용애플리케이션인경우 0x3, GUI 용애플리케이션인경우 0x2 값을가진다. SizeOfImage : 메모리상에로드되는 PE 파일의총크기 SizeOfHeader : 디스크상의모든헤더의총사이즈이다. SizeOfStackReserve : 프로그램에서예약할 stack 의크기 SizeOfStackCommit : 프로그램에서 commit 할 stack 의크기 SizeOfHeapReserve : 프로그램에서예약할 Heap 의크기 SizeOfHeapCommit : 프로그램에서 commit 할 Heap 의크기 LoaderFlags : NumberOfRvaAndSizes 와함께안티리버싱에사용. NumberOfRvaAndSizes : 데이터디렉토리엔트리개수. DataDirectory : Export table, Import table, Resource 영역, Exception 영역, 디버그영역등을접근할수있는주소정보의배열. -12-
4-4. IMGAE_DATA_DIRECTORY 구조체 IMAGE_DATA_DIRECTORY 구조체는 IMAGE_OPTIONAL_HEADER 구조체의마지막필드이다. #define 되어있는 IMAGE_NUMBEROF_DIRECTORY_ENTRIES 값을가져와배열을만든다. 보통 16 으로정의되어있다. 또한 PE 파일에서중요한역할을하는개체들의위치와크기에대한정보를담고있다. IMAGE_NUMBEROF_DIRECTORY_ENTRIES Typedef struct _IMAGE_DATA_DIRECTORY{ VirtualAddress; VirtualSize; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; Data Directory 구조체배열의구성 #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 #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_ARCHITECTURE 7 #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 #define IMAGE_DIRECTORY_ENTRY_TLS 9 #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 #define IMAGE_DIRECTORY_ENTRY_IAT 12 #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 IMAGE_DATA_DIRECTORY 의 DataDirectory 의마지막 16 번째배열은 NULL 값을가지며 이는메모리상에서 DataDirectory 정의가끝났음을의미한다. -13-
5. IMPORT 프로그램이외부 DLL 파일을 import 하기위해사용하는테이블이다. 외부 DLL 에대한정보를 담고있다. 5-1 IMPORT Table IMAGE_IMPORT_DESCRIPTOR 구조체로이루어져있으며 ILT(Import lookup Table) 과 IAT(Import Address Table) 에관한정보를가지고있다. Optional Header 구조체의마지막필드인 DataDirectory 배열의두번째 (DataDirectory[1]) 필드가이 Import 테이블의주소를가리키고있다. Typedef struct _IMAGE_IMPORT_DESCRIPTOR{ union{ Characteristics; OriginalFirstThunk; } TimeDateStamp; ForwarderChain; Name; FirstThunk; } IMAGE_IMPORT_DESCRIPTOR; OriginalFirstThunk : ILT를가리키는 RVA값이다. IMAGE_THUNK_DATA로 구성된구조체이다 TimeDateStamp : 바인딩전 0, 바인딩후 -1 로설정된다. ForwarderChain : 바인딩전 0, 바인딩후 -1 로설정된다. FirstThunk : IAT를가리키는 RVA값이다. IMAGE_THUNK_DATA로 구성된구조체이다. 바인딩후실제함수의주소로사용된다. 5-2 IMAGE_THUNK_DATA 구조체 ILT와 IAT가구성되어있는구조체로바인딩전에는 ILT와 IAT는필드의 Ordinal 값을사용하고, 바인딩후 IAT는필드의 Function으로사용한다. ( 부록 <1> 참조 ) typedef struct_image_thunk_data32 { union{ ForwarderString; Function; Ordinal; AddressOfData; } u1; } IMAGE_THUNK_DATA32-14-
5-3 IMAGE_IMPORT_BY_NAME 구조체바인딩전 ILT 와 IAT 는 IMAGE_IMPORT_BY_NAME 구조체에가리키며, 바인딩후 IAT 만실제함수의주소를가리키게된다. typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; BYTE Name[?]; } IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME; Hint : Import 하는함수가추가될때마다 1 씩증가한다. Name : 실제함수이름을가리키는 RVA 값. -15-
6. EXPORT 외부 DLL 파일들을프로세스의내부에로드하기위해 Export Table 을사용한다. 6-1. EXPORT Table typedef struct _IMAGE_EXPORT_DIRECTORY{ WORD WORD Characteristics; TimeDateStamp; MajorVersion; MinorVersion; Name; Base; NumberOfFunctions; NumberOfNames; AddressOfFunctions; AddressOfNames; AddressOfNameOrdinals; } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; Name : DLL 의이름 (ASCII 문자열 ) RVA 값 NumberOfFunctions : AddressOfFunctions 가가리키는 RVA 배열수 NumverOfNames : AddressOfNames 가가리키는 RVA 배열수 AddressOfFunctions : 함수의실제주소를가리키는 RVA 값. AddressOfNames : 함수실제이름을가리키는 RVA 값. AddressOfNameOrdinals : 함수의 Ordinal( 서수 ) 값을가리키는 RVA 값 6-2 EXPORT 방식외부 DLL 파일내부에서특정함수를 export 할때,Name 방식과 Ordinal 방식이있다. Name 방식은특정함수의이름을지정하여해당함수를호출하는방식이며, Ordinal 방식은 DLL 파일에서 16bits 로각함수들을구분지어사용하는 Ordinal 값을이용하여특정함수를호출하는방식이다. -16-
7. Section Table Section table 은 PE Header 바로뒤에위치한다. 즉, 이를찾으려면 PE Header 시작주소에서 PE Header 사이즈만큼더해주면그위치가 Section table 정보가저장되어있을것이다. PE Header 는 PE signature (4bytes 고정 ) 과 File Header (20bytes 고정 ) 그리고 Optional Header (base size : 224bytes) 로구성되어있다. 정리하자면, 24bytes +Optional Header 사이즈가된다. 각섹션의위치는섹션테이블에저장되어있으며이는 Section Header 를통해확인가능하다. Section 위치는 Section Header 속의 VirtualAddress 와 PointerToRawDate 라는값에 offset 되어있다. 각각 VirtualAddress 는메모리상에서, PointerToRawDate 는디스크상에서의 offset 값이다. 즉, VirtualAddress 와 PointerToRawDate 값을알아보고섹션의크기을 알아보면각섹션들의위치와정보를알아낼수있다. typedef struct _IMAGE_SECTION_HEADER{ BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union{ PhsicalAddress; VirtualSize; } Misc; WORD WORD VirtualAddress; SizeOfRawData; PointerToRawData; PointerToRelocations; PointerToLinenumbers; NumberOfRelocations; NumberOfLinenumbers; Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; VirtualAddress : 로드될섹션의가상주소. RAV 값 SizeOfRawData : 파일상태에서의섹션의사이즈값. file alignment 의배수이어야한다. PointerToRawData : 파일상태에서의섹션시작위치. Characteristics : 섹션의속성값. ( 읽기, 쓰기, 읽기쓰기, 실행등과같은속성 ) -17-
속성값계산은부록 <2> 의표에나와있는 Flag 속성을보고적절히계산하여사용하면된다. 예를들어속성 INITIALIZED, MEM_READ,MEM_ WRITE 속성을적용하고싶다면 0x00000040 + 0x40000000 + 0x80000000 = 0xC0000040 값을 Characteristics 에적용하면된다. -18-
8. 맺음말 0. 시작하면서에서설명했듯이본문서는파일구조를좀더자세하게알고싶은사람, 역분석 ( 리버스 ) 공부를시작하는사람들에게도움이되고자하는동기로시작된문서이다. 하지만좀더역분석에대해공부한다면앞으로 OS 에관한전반적인지식을쌓는것을추천한다. 본문서에서설명하는 PE 구조와달리 ELF 구조역시기본적인지식에해당되므로가능한 ELF 관련지식도쌓을것을추천한다. 저자한마디 : 문서내용중잘못된정보나궁금한사항은 E-mail 을보내주세요. 확인후수정하도록하겠습니다. -19-
9. 참고자료 MSDN (search: PE) http://msdn.microsoft.com/en-us/magazine/cc301805.aspx Zesrever 의지식펌프 http://zesrever.xstone.org/category/ 지식뽐뿌질 %20II( 연재물 )/ 조립하면서 %20 배우는 %20PE PE 헤더에서사용하는구조체 http://www.openrce.org/reference_library/files/reference/pe%20format.pdf -20-
10. 부록 부록 <1> Binding -21-
부록 <2> Characteristics The characteristics of the image. The following values are defined. Flag 0x00000000 0x00000001 0x00000002 0x00000004 IMAGE_SCN_TYPE_NO_PAD 0x00000008 0x00000010 IMAGE_SCN_CNT_CODE 0x00000020 IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 IMAGE_SCN_LNK_OTHER 0x00000100 IMAGE_SCN_LNK_INFO 0x00000200 0x00000400 IMAGE_SCN_LNK_REMOVE 0x00000800 IMAGE_SCN_LNK_COMDAT 0x00001000 0x00002000 IMAGE_SCN_NO_DEFER_SPEC_EXC 0x00004000 IMAGE_SCN_GPREL 0x00008000 0x00010000 IMAGE_SCN_MEM_PURGEABLE 0x00020000 IMAGE_SCN_MEM_LOCKED 0x00040000 IMAGE_SCN_MEM_PRELOAD 0x00080000 IMAGE_SCN_ALIGN_1BYTES 0x00100000 IMAGE_SCN_ALIGN_2BYTES 0x00200000 IMAGE_SCN_ALIGN_4BYTES 0x00300000 IMAGE_SCN_ALIGN_8BYTES 0x00400000 Meaning The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. The section contains executable code. The section contains initialized data. The section contains uninitialized data. The section contains comments or other information. This is valid only for object files. The section will not become part of the image. This is valid only for object files. The section contains COMDAT data. This is valid only for object files. Reset speculative exceptions handling bits in the TLB entries for this section. The section contains data referenced through the global pointer. Align data on a 1-byte boundary. This is valid only for object files. Align data on a 2-byte boundary. This is valid only for object files. Align data on a 4-byte boundary. This is valid only for object files. Align data on a 8-byte boundary. This is valid only for object files. -22-
IMAGE_SCN_ALIGN_16BYTES 0x00500000 IMAGE_SCN_ALIGN_32BYTES 0x00600000 Align data on a 16-byte boundary. This is valid only for object files. Align data on a 32-byte boundary. This is valid only for object files. IMAGE_SCN_ALIGN_64BYTES 0x00700000 Align data on a 64-byte boundary. This is valid only for object files. IMAGE_SCN_ALIGN_128BYTES 0x00800000 Align data on a 128-byte boundary. This is valid only for object files. IMAGE_SCN_ALIGN_256BYTES 0x00900000 Align data on a 256-byte boundary. This is valid only for object files. IMAGE_SCN_ALIGN_512BYTES 0x00A00000 Align data on a 512-byte boundary. This is valid only for object files. IMAGE_SCN_ALIGN_1024BYTES 0x00B00000 Align data on a 1024-byte boundary. This is valid only for object files. IMAGE_SCN_ALIGN_2048BYTES 0x00C00000 Align data on a 2048-byte boundary. This is valid only for object files. IMAGE_SCN_ALIGN_4096BYTES 0x00D00000 Align data on a 4096-byte boundary. This is valid only for object files. IMAGE_SCN_ALIGN_8192BYTES 0x00E00000 Align data on a 8192-byte boundary. This is valid only for object files. IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 The section contains extended relocations. The count of relocations for the section exceeds the 16 bits that is reserved for it in the section header. If the NumberOfRelocations field in the section header is 0xffff, the actual relocation count is stored in the VirtualAddress field of the first relocation. It is an error if IMAGE_SCN_LNK_NRELOC_OVFL is set and there are fewer than 0xffff relocations in the section. IMAGE_SCN_MEM_DISCARDABLE 0x02000000 The section can be discarded as needed. IMAGE_SCN_MEM_NOT_CACHED 0x04000000 The section cannot be cached. IMAGE_SCN_MEM_NOT_PAGED 0x08000000 The section cannot be paged. IMAGE_SCN_MEM_SHARED 0x10000000 The section can be shared in memory. IMAGE_SCN_MEM_EXECUTE 0x20000000 The section can be executed as code. IMAGE_SCN_MEM_READ 0x40000000 The section can be read. IMAGE_SCN_MEM_WRITE 0x80000000 The section can be written to. -23-