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 union Contents 72 { 73 Directory directory; 74 DirectoryData data; 75 } 76 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 Directory readDirectory(uint offset) 89 { 90 enforce(offset + IMAGE_RESOURCE_DIRECTORY.sizeof <= data.length, "Out-of-bounds directory offset"); 91 auto winDir = cast(IMAGE_RESOURCE_DIRECTORY*)(data.ptr + offset); 92 Directory dir; 93 dir.characteristics = winDir.Characteristics; 94 dir.timestamp = winDir.TimeDateStamp; 95 dir.majorVersion = winDir.MajorVersion; 96 dir.minorVersion = winDir.MinorVersion; 97 dir.entries.length = winDir.NumberOfNamedEntries + winDir.NumberOfIdEntries; 98 99 offset += IMAGE_RESOURCE_DIRECTORY.sizeof; 100 enforce(offset + dir.entries.length * IMAGE_RESOURCE_DIRECTORY_ENTRY.sizeof <= data.length, "Not enough data for directory entries"); 101 auto winEntries = cast(IMAGE_RESOURCE_DIRECTORY_ENTRY*)(data.ptr + offset)[0..dir.entries.length]; 102 103 foreach (n; 0..dir.entries.length) 104 { 105 auto winEntry = &winEntries[n]; 106 auto entry = &dir.entries[n]; 107 108 if (winEntry.NameIsString) 109 entry.name = readString(winEntry.NameOffset).to!string; 110 else 111 entry.id = winEntry.Id; 112 113 entry.isDirectory = winEntry.DataIsDirectory; 114 if (entry.isDirectory) 115 entry.directory = readDirectory(winEntry.OffsetToDirectory); 116 else 117 entry.data = readDirectoryData(winEntry.OffsetToData); 118 } 119 120 return dir; 121 } 122 123 DirectoryData readDirectoryData(uint offset) 124 { 125 enforce(offset + IMAGE_RESOURCE_DATA_ENTRY.sizeof <= data.length, "Out-of-bounds directory data header offset"); 126 auto winDirData = cast(IMAGE_RESOURCE_DATA_ENTRY*)(data.ptr + offset); 127 DirectoryData dirData; 128 dirData.codePage = winDirData.CodePage; 129 auto start = winDirData.OffsetToData - rva; 130 enforce(start + winDirData.Size <= data.length, "Out-of-bounds directory data offset"); 131 dirData.data = data[start .. start + winDirData.Size]; 132 133 return dirData; 134 } 135 136 WCHAR[] readString(uint offset) 137 { 138 enforce(offset + typeof(IMAGE_RESOURCE_DIR_STRING_U.Length).sizeof <= data.length, "Out-of-bounds string offset"); 139 auto winStr = cast(IMAGE_RESOURCE_DIR_STRING_U*)(data.ptr + offset); 140 offset += typeof(IMAGE_RESOURCE_DIR_STRING_U.Length).sizeof; 141 enforce(offset + winStr.Length * WCHAR.sizeof <= data.length, "Out-of-bounds string offset"); 142 auto firstChar = &winStr._NameString; 143 return firstChar[0..winStr.Length]; 144 } 145 }