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 }