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 <vladimir@thecybershadow.net> 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 struct ResourceParser 23 { 24 ubyte[] data; 25 uint rva; 26 27 this(void[] data, uint rva) 28 { 29 this.data = cast(ubyte[])data; 30 this.rva = rva; 31 root = readDirectory(0); 32 } 33 34 struct Directory 35 { 36 uint characteristics, timestamp; 37 ushort majorVersion, minorVersion; 38 DirectoryEntry[] entries; 39 40 ref DirectoryEntry opIndex(uint id) 41 { 42 foreach (ref entry; entries) 43 if (entry.name is null && entry.id == id) 44 return entry; 45 throw new Exception("Can't find directory with this ID"); 46 } 47 48 ref DirectoryEntry opIndex(string name) 49 { 50 foreach (ref entry; entries) 51 if (entry.name !is null && entry.name == name) 52 return entry; 53 throw new Exception("Can't find directory with this name"); 54 } 55 } 56 Directory root; 57 58 struct DirectoryEntry 59 { 60 string name; 61 uint id; 62 bool isDirectory; 63 union Contents 64 { 65 Directory directory; 66 DirectoryData data; 67 } 68 Contents contents; 69 ref @property Directory directory() return { assert(isDirectory); return contents.directory; } 70 ref @property DirectoryData data() return { assert(!isDirectory); return contents.data; } 71 } 72 73 struct DirectoryData 74 { 75 uint codePage; 76 void[] data; 77 } 78 79 Directory readDirectory(uint offset) 80 { 81 enforce(offset + IMAGE_RESOURCE_DIRECTORY.sizeof <= data.length, "Out-of-bounds directory offset"); 82 auto winDir = cast(IMAGE_RESOURCE_DIRECTORY*)(data.ptr + offset); 83 Directory dir; 84 dir.characteristics = winDir.Characteristics; 85 dir.timestamp = winDir.TimeDateStamp; 86 dir.majorVersion = winDir.MajorVersion; 87 dir.minorVersion = winDir.MinorVersion; 88 dir.entries.length = winDir.NumberOfNamedEntries + winDir.NumberOfIdEntries; 89 90 offset += IMAGE_RESOURCE_DIRECTORY.sizeof; 91 enforce(offset + dir.entries.length * IMAGE_RESOURCE_DIRECTORY_ENTRY.sizeof <= data.length, "Not enough data for directory entries"); 92 auto winEntries = cast(IMAGE_RESOURCE_DIRECTORY_ENTRY*)(data.ptr + offset)[0..dir.entries.length]; 93 94 foreach (n; 0..dir.entries.length) 95 { 96 auto winEntry = &winEntries[n]; 97 auto entry = &dir.entries[n]; 98 99 if (winEntry.NameIsString) 100 entry.name = readString(winEntry.NameOffset).to!string; 101 else 102 entry.id = winEntry.Id; 103 104 entry.isDirectory = winEntry.DataIsDirectory; 105 if (entry.isDirectory) 106 entry.directory = readDirectory(winEntry.OffsetToDirectory); 107 else 108 entry.data = readDirectoryData(winEntry.OffsetToData); 109 } 110 111 return dir; 112 } 113 114 DirectoryData readDirectoryData(uint offset) 115 { 116 enforce(offset + IMAGE_RESOURCE_DATA_ENTRY.sizeof <= data.length, "Out-of-bounds directory data header offset"); 117 auto winDirData = cast(IMAGE_RESOURCE_DATA_ENTRY*)(data.ptr + offset); 118 DirectoryData dirData; 119 dirData.codePage = winDirData.CodePage; 120 auto start = winDirData.OffsetToData - rva; 121 enforce(start + winDirData.Size <= data.length, "Out-of-bounds directory data offset"); 122 dirData.data = data[start .. start + winDirData.Size]; 123 124 return dirData; 125 } 126 127 WCHAR[] readString(uint offset) 128 { 129 enforce(offset + typeof(IMAGE_RESOURCE_DIR_STRING_U.Length).sizeof <= data.length, "Out-of-bounds string offset"); 130 auto winStr = cast(IMAGE_RESOURCE_DIR_STRING_U*)(data.ptr + offset); 131 offset += typeof(IMAGE_RESOURCE_DIR_STRING_U.Length).sizeof; 132 enforce(offset + winStr.Length * WCHAR.sizeof <= data.length, "Out-of-bounds string offset"); 133 auto firstChar = &winStr._NameString; 134 return firstChar[0..winStr.Length]; 135 } 136 }