1 /** 2 * Support for Windows PE resources. 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.resources; 15 16 import std.conv; 17 import std.exception; 18 19 import ae.sys.windows.imports; 20 mixin(importWin32!q{winnt}); 21 22 /// Parses Windows PE resources. 23 struct ResourceParser 24 { 25 ubyte[] data; /// All data. 26 uint rva; /// RVA of data. 27 28 this(void[] data, uint rva) 29 { 30 this.data = cast(ubyte[])data; 31 this.rva = rva; 32 root = readDirectory(0); 33 } /// 34 35 /// Resource directory. 36 struct Directory 37 { 38 /// Directory properties. 39 uint characteristics, timestamp; 40 /// ditto 41 ushort majorVersion, minorVersion; 42 /// Directory entries. 43 DirectoryEntry[] entries; 44 45 /// Find an entry by ID. 46 ref DirectoryEntry opIndex(uint id) 47 { 48 foreach (ref entry; entries) 49 if (entry.name is null && entry.id == id) 50 return entry; 51 throw new Exception("Can't find directory with this ID"); 52 } 53 54 /// Find an entry by name. 55 ref DirectoryEntry opIndex(string name) 56 { 57 foreach (ref entry; entries) 58 if (entry.name !is null && entry.name == name) 59 return entry; 60 throw new Exception("Can't find directory with this name"); 61 } 62 } 63 Directory root; /// The root directory. 64 65 /// An entry in a `Directory`. 66 struct DirectoryEntry 67 { 68 string name; /// Entry name (if it is identified by a name). 69 uint id; /// Entry ID (if it is identified by an ID). 70 bool isDirectory; /// True if this entry is another directory. 71 private union Contents 72 { 73 Directory directory; 74 DirectoryData data; 75 } 76 private Contents contents; 77 ref @property Directory directory() return { assert(isDirectory); return contents.directory; } /// Return the contents as a subdirectory. 78 ref @property DirectoryData data() return { assert(!isDirectory); return contents.data; } /// Return the contents as data. 79 } 80 81 /// Data contents of a `DirectoryEntry` with `!isDirectory`. 82 struct DirectoryData 83 { 84 uint codePage; /// 85 void[] data; /// 86 } 87 88 private: 89 Directory readDirectory(uint offset) 90 { 91 enforce(offset + IMAGE_RESOURCE_DIRECTORY.sizeof <= data.length, "Out-of-bounds directory offset"); 92 auto winDir = cast(IMAGE_RESOURCE_DIRECTORY*)(data.ptr + offset); 93 Directory dir; 94 dir.characteristics = winDir.Characteristics; 95 dir.timestamp = winDir.TimeDateStamp; 96 dir.majorVersion = winDir.MajorVersion; 97 dir.minorVersion = winDir.MinorVersion; 98 dir.entries.length = winDir.NumberOfNamedEntries + winDir.NumberOfIdEntries; 99 100 offset += IMAGE_RESOURCE_DIRECTORY.sizeof; 101 enforce(offset + dir.entries.length * IMAGE_RESOURCE_DIRECTORY_ENTRY.sizeof <= data.length, "Not enough data for directory entries"); 102 auto winEntries = cast(IMAGE_RESOURCE_DIRECTORY_ENTRY*)(data.ptr + offset)[0..dir.entries.length]; 103 104 foreach (n; 0..dir.entries.length) 105 { 106 auto winEntry = &winEntries[n]; 107 auto entry = &dir.entries[n]; 108 109 if (winEntry.NameIsString) 110 entry.name = readString(winEntry.NameOffset).to!string; 111 else 112 entry.id = winEntry.Id; 113 114 entry.isDirectory = winEntry.DataIsDirectory; 115 if (entry.isDirectory) 116 entry.directory = readDirectory(winEntry.OffsetToDirectory); 117 else 118 entry.data = readDirectoryData(winEntry.OffsetToData); 119 } 120 121 return dir; 122 } 123 124 DirectoryData readDirectoryData(uint offset) 125 { 126 enforce(offset + IMAGE_RESOURCE_DATA_ENTRY.sizeof <= data.length, "Out-of-bounds directory data header offset"); 127 auto winDirData = cast(IMAGE_RESOURCE_DATA_ENTRY*)(data.ptr + offset); 128 DirectoryData dirData; 129 dirData.codePage = winDirData.CodePage; 130 auto start = winDirData.OffsetToData - rva; 131 enforce(start + winDirData.Size <= data.length, "Out-of-bounds directory data offset"); 132 dirData.data = data[start .. start + winDirData.Size]; 133 134 return dirData; 135 } 136 137 WCHAR[] readString(uint offset) 138 { 139 enforce(offset + typeof(IMAGE_RESOURCE_DIR_STRING_U.Length).sizeof <= data.length, "Out-of-bounds string offset"); 140 auto winStr = cast(IMAGE_RESOURCE_DIR_STRING_U*)(data.ptr + offset); 141 offset += typeof(IMAGE_RESOURCE_DIR_STRING_U.Length).sizeof; 142 enforce(offset + winStr.Length * WCHAR.sizeof <= data.length, "Out-of-bounds string offset"); 143 auto firstChar = &winStr._NameString; 144 return firstChar[0..winStr.Length]; 145 } 146 }