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 }