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 <vladimir@thecybershadow.net> 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 struct PE 23 { 24 ubyte[] data; 25 26 PIMAGE_DOS_HEADER dosHeader; 27 PIMAGE_NT_HEADERS32 ntHeaders; 28 IMAGE_SECTION_HEADER[] sectionHeaders; 29 IMAGE_DATA_DIRECTORY[] dataDirectories; 30 31 this(void[] exe) 32 { 33 data = cast(ubyte[])exe; 34 35 enforce(data.length > IMAGE_DOS_HEADER.sizeof, "Not enough data for DOS header"); 36 dosHeader = cast(PIMAGE_DOS_HEADER)data.ptr; 37 enforce(dosHeader.e_magic == IMAGE_DOS_SIGNATURE, "Invalid DOS signature"); 38 39 enforce(data.length > dosHeader.e_lfanew + IMAGE_NT_HEADERS32.sizeof, "Not enough data for NT headers"); 40 ntHeaders = cast(PIMAGE_NT_HEADERS32)(data.ptr + dosHeader.e_lfanew); 41 enforce(ntHeaders.Signature == IMAGE_NT_SIGNATURE, "Invalid NT signature"); 42 enforce(ntHeaders.FileHeader.Machine == IMAGE_FILE_MACHINE_I386, "Not an x86 PE"); 43 44 dataDirectories = ntHeaders.OptionalHeader.DataDirectory.ptr[0..ntHeaders.OptionalHeader.NumberOfRvaAndSizes]; 45 enforce(cast(ubyte*)(dataDirectories.ptr + dataDirectories.length) <= data.ptr + data.length, "Not enough data for data directories"); 46 47 auto sectionsStart = dosHeader.e_lfanew + ntHeaders.OptionalHeader.offsetof + ntHeaders.FileHeader.SizeOfOptionalHeader; 48 auto sectionsEnd = sectionsStart + ntHeaders.FileHeader.NumberOfSections * IMAGE_SECTION_HEADER.sizeof; 49 enforce(sectionsEnd <= data.length, "Not enough data for section headers"); 50 sectionHeaders = cast(IMAGE_SECTION_HEADER[])(data[sectionsStart .. sectionsEnd]); 51 } 52 53 /// Translate a file offset to the relative virtual address 54 /// (address relative to the image base). 55 size_t fileToRva(size_t offset) 56 { 57 foreach (ref section; sectionHeaders) 58 if (offset >= section.PointerToRawData && offset < section.PointerToRawData + section.SizeOfRawData) 59 return offset - section.PointerToRawData + section.VirtualAddress; 60 throw new Exception("Unmapped file offset"); 61 } 62 63 /// Reverse of fileToRva 64 size_t rvaToFile(size_t offset) 65 { 66 foreach (ref section; sectionHeaders) 67 if (offset >= section.VirtualAddress && offset < section.VirtualAddress + section.SizeOfRawData) 68 return offset - section.VirtualAddress + section.PointerToRawData; 69 throw new Exception("Unmapped memory address"); 70 } 71 72 /// Translate a file offset to the corresponding virtual address 73 /// (the in-memory image address at the default image base). 74 size_t fileToImage(size_t offset) 75 { 76 return fileToRva(offset) + ntHeaders.OptionalHeader.ImageBase; 77 } 78 79 /// Reverse of fileToImage 80 size_t imageToFile(size_t offset) 81 { 82 return rvaToFile(offset - ntHeaders.OptionalHeader.ImageBase); 83 } 84 85 /// Provide an array-like view of the in-memory layout. 86 @property auto imageData() 87 { 88 static struct ImageData 89 { 90 PE* pe; 91 92 ref ubyte opIndex(size_t offset) 93 { 94 return pe.data[pe.imageToFile(offset)]; 95 } 96 97 ubyte[] opSlice(size_t start, size_t end) 98 { 99 return pe.data[pe.imageToFile(start) .. pe.imageToFile(end)]; 100 } 101 102 T interpretAs(T)(size_t offset) 103 { 104 return *cast(T*)(pe.data.ptr + pe.imageToFile(offset)); 105 } 106 } 107 108 return ImageData(&this); 109 } 110 111 /// Get the image data for the given section header. 112 ubyte[] sectionData(ref IMAGE_SECTION_HEADER section) 113 { 114 return data[section.PointerToRawData .. section.PointerToRawData + section.SizeOfRawData]; 115 } 116 117 /// Get the image data for the given directory entry. 118 ubyte[] directoryData(USHORT entry) 119 { 120 auto dir = ntHeaders.OptionalHeader.DataDirectory[entry]; 121 auto b = rvaToFile(dir.VirtualAddress); 122 return data[b .. b + dir.Size]; 123 } 124 } 125 126 // UFCS helper 127 T interpretAs(T)(ubyte[] data, size_t offset) 128 { 129 return *cast(T*)(data.ptr + offset); 130 } 131 132 /// Get the name of an IMAGE_SECTION_HEADER as a D string. 133 @property string name(ref IMAGE_SECTION_HEADER section) 134 { 135 auto n = cast(char[])section.Name[]; 136 auto p = n.indexOf(0); 137 return n[0 .. p<0 ? $ : p].idup; 138 }