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; 15 version (Windows): 16 17 import std.exception; 18 import std.string; 19 20 import ae.sys.windows.imports; 21 mixin(importWin32!q{winnt}); 22 23 struct PE 24 { 25 ubyte[] data; 26 27 PIMAGE_DOS_HEADER dosHeader; 28 PIMAGE_NT_HEADERS ntHeaders; 29 IMAGE_SECTION_HEADER[] sectionHeaders; 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_HEADERS.sizeof, "Not enough data for NT headers"); 40 ntHeaders = cast(PIMAGE_NT_HEADERS)(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 auto sectionOffset = dosHeader.e_lfanew + ntHeaders.OptionalHeader.offsetof + ntHeaders.FileHeader.SizeOfOptionalHeader; 45 sectionHeaders = cast(IMAGE_SECTION_HEADER[])(data[sectionOffset .. sectionOffset + ntHeaders.FileHeader.NumberOfSections * IMAGE_SECTION_HEADER.sizeof]); 46 } 47 48 /// Translate a file offset to the relative virtual address 49 /// (address relative to the image base). 50 size_t fileToRva(size_t offset) 51 { 52 foreach (ref section; sectionHeaders) 53 if (offset >= section.PointerToRawData && offset < section.PointerToRawData + section.SizeOfRawData) 54 return offset - section.PointerToRawData + section.VirtualAddress; 55 throw new Exception("Unmapped file offset"); 56 } 57 58 /// Reverse of fileToRva 59 size_t rvaToFile(size_t offset) 60 { 61 foreach (ref section; sectionHeaders) 62 if (offset >= section.VirtualAddress && offset < section.VirtualAddress + section.SizeOfRawData) 63 return offset - section.VirtualAddress + section.PointerToRawData; 64 throw new Exception("Unmapped memory address"); 65 } 66 67 /// Translate a file offset to the corresponding virtual address 68 /// (the in-memory image address at the default image base). 69 size_t fileToImage(size_t offset) 70 { 71 return fileToRva(offset) + ntHeaders.OptionalHeader.ImageBase; 72 } 73 74 /// Reverse of fileToImage 75 size_t imageToFile(size_t offset) 76 { 77 return rvaToFile(offset - ntHeaders.OptionalHeader.ImageBase); 78 } 79 80 /// Provide an array-like view of the in-memory layout. 81 @property auto imageData() 82 { 83 static struct ImageData 84 { 85 PE* pe; 86 87 ubyte opIndex(size_t offset) 88 { 89 return pe.data[pe.imageToFile(offset)]; 90 } 91 92 ubyte[] opSlice(size_t start, size_t end) 93 { 94 return pe.data[pe.imageToFile(start) .. pe.imageToFile(end)]; 95 } 96 97 T interpretAs(T)(size_t offset) 98 { 99 return *cast(T*)(pe.data.ptr + pe.imageToFile(offset)); 100 } 101 } 102 103 return ImageData(&this); 104 } 105 106 /// Get the image data for the given section header. 107 ubyte[] sectionData(ref IMAGE_SECTION_HEADER section) 108 { 109 return data[section.PointerToRawData .. section.PointerToRawData + section.SizeOfRawData]; 110 } 111 112 /// Get the image data for the given directory entry. 113 ubyte[] directoryData(USHORT entry) 114 { 115 auto dir = ntHeaders.OptionalHeader.DataDirectory[entry]; 116 auto b = rvaToFile(dir.VirtualAddress); 117 return data[b .. b + dir.Size]; 118 } 119 } 120 121 // UFCS helper 122 T interpretAs(T)(ubyte[] data, size_t offset) 123 { 124 return *cast(T*)(data.ptr + offset); 125 } 126 127 /// Get the name of an IMAGE_SECTION_HEADER as a D string. 128 @property string name(ref IMAGE_SECTION_HEADER section) 129 { 130 auto n = cast(char[])section.Name[]; 131 auto p = n.indexOf(0); 132 return n[0 .. p<0 ? $ : p].idup; 133 }