1 /** 2 * Support for the Windows PE executable format. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.sys.windows.pe.pe; 15 16 import std.exception; 17 import std..string; 18 19 import ae.sys.windows.imports; 20 mixin(importWin32!q{winnt}); 21 22 /// Parses a Portable Executable file. 23 struct PE 24 { 25 ubyte[] data; /// File data bytes. 26 27 PIMAGE_DOS_HEADER dosHeader; /// Extracted headers. 28 PIMAGE_NT_HEADERS32 ntHeaders; /// ditto 29 IMAGE_SECTION_HEADER[] sectionHeaders; /// ditto 30 IMAGE_DATA_DIRECTORY[] dataDirectories; /// ditto 31 32 this(void[] exe) 33 { 34 data = cast(ubyte[])exe; 35 36 enforce(data.length > IMAGE_DOS_HEADER.sizeof, "Not enough data for DOS header"); 37 dosHeader = cast(PIMAGE_DOS_HEADER)data.ptr; 38 enforce(dosHeader.e_magic == IMAGE_DOS_SIGNATURE, "Invalid DOS signature"); 39 40 enforce(data.length > dosHeader.e_lfanew + IMAGE_NT_HEADERS32.sizeof, "Not enough data for NT headers"); 41 ntHeaders = cast(PIMAGE_NT_HEADERS32)(data.ptr + dosHeader.e_lfanew); 42 enforce(ntHeaders.Signature == IMAGE_NT_SIGNATURE, "Invalid NT signature"); 43 enforce(ntHeaders.FileHeader.Machine == IMAGE_FILE_MACHINE_I386, "Not an x86 PE"); 44 45 dataDirectories = ntHeaders.OptionalHeader.DataDirectory.ptr[0..ntHeaders.OptionalHeader.NumberOfRvaAndSizes]; 46 enforce(cast(ubyte*)(dataDirectories.ptr + dataDirectories.length) <= data.ptr + data.length, "Not enough data for data directories"); 47 48 auto sectionsStart = dosHeader.e_lfanew + ntHeaders.OptionalHeader.offsetof + ntHeaders.FileHeader.SizeOfOptionalHeader; 49 auto sectionsEnd = sectionsStart + ntHeaders.FileHeader.NumberOfSections * IMAGE_SECTION_HEADER.sizeof; 50 enforce(sectionsEnd <= data.length, "Not enough data for section headers"); 51 sectionHeaders = cast(IMAGE_SECTION_HEADER[])(data[sectionsStart .. sectionsEnd]); 52 } /// 53 54 /// Translate a file offset to the relative virtual address 55 /// (address relative to the image base). 56 size_t fileToRva(size_t offset) 57 { 58 foreach (ref section; sectionHeaders) 59 if (offset >= section.PointerToRawData && offset < section.PointerToRawData + section.SizeOfRawData) 60 return offset - section.PointerToRawData + section.VirtualAddress; 61 throw new Exception("Unmapped file offset"); 62 } 63 64 /// Reverse of fileToRva 65 size_t rvaToFile(size_t offset) 66 { 67 foreach (ref section; sectionHeaders) 68 if (offset >= section.VirtualAddress && offset < section.VirtualAddress + section.SizeOfRawData) 69 return offset - section.VirtualAddress + section.PointerToRawData; 70 throw new Exception("Unmapped memory address"); 71 } 72 73 /// Translate a file offset to the corresponding virtual address 74 /// (the in-memory image address at the default image base). 75 size_t fileToImage(size_t offset) 76 { 77 return fileToRva(offset) + ntHeaders.OptionalHeader.ImageBase; 78 } 79 80 /// Reverse of fileToImage 81 size_t imageToFile(size_t offset) 82 { 83 return rvaToFile(offset - ntHeaders.OptionalHeader.ImageBase); 84 } 85 86 /// Provide an array-like view of the in-memory layout. 87 @property auto imageData() 88 { 89 static struct ImageData 90 { 91 PE* pe; 92 93 ref ubyte opIndex(size_t offset) 94 { 95 return pe.data[pe.imageToFile(offset)]; 96 } 97 98 ubyte[] opSlice(size_t start, size_t end) 99 { 100 return pe.data[pe.imageToFile(start) .. pe.imageToFile(end)]; 101 } 102 103 T interpretAs(T)(size_t offset) 104 { 105 return *cast(T*)(pe.data.ptr + pe.imageToFile(offset)); 106 } 107 } 108 109 return ImageData(&this); 110 } 111 112 /// Get the image data for the given section header. 113 ubyte[] sectionData(ref IMAGE_SECTION_HEADER section) 114 { 115 return data[section.PointerToRawData .. section.PointerToRawData + section.SizeOfRawData]; 116 } 117 118 /// Get the image data for the given directory entry. 119 ubyte[] directoryData(USHORT entry) 120 { 121 auto dir = ntHeaders.OptionalHeader.DataDirectory[entry]; 122 auto b = rvaToFile(dir.VirtualAddress); 123 return data[b .. b + dir.Size]; 124 } 125 } 126 127 // UFCS helper 128 private T interpretAs(T)(ubyte[] data, size_t offset) 129 { 130 return *cast(T*)(data.ptr + offset); 131 } 132 133 /// Get the name of an IMAGE_SECTION_HEADER as a D string. 134 @property string name(ref IMAGE_SECTION_HEADER section) 135 { 136 auto n = cast(char[])section.Name[]; 137 auto p = n.indexOf(0); 138 return n[0 .. p<0 ? $ : p].idup; 139 }