1 /* 2 * assembly parser 3 * 4 * Copyright 2008 James Hawkins 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include "mscoree_private.h" 22 23 #include <winver.h> 24 #include <dbghelp.h> 25 26 typedef struct 27 { 28 ULONG Signature; 29 USHORT MajorVersion; 30 USHORT MinorVersion; 31 ULONG Reserved; 32 ULONG VersionLength; 33 LPSTR Version; 34 BYTE Flags; 35 WORD Streams; 36 } METADATAHDR; 37 38 typedef struct 39 { 40 DWORD Offset; 41 DWORD Size; 42 } METADATASTREAMHDR; 43 44 typedef struct tagCLRTABLE 45 { 46 INT rows; 47 DWORD offset; 48 } CLRTABLE; 49 50 struct tagASSEMBLY 51 { 52 int is_mapped_file; 53 54 /* mapped files */ 55 LPWSTR path; 56 HANDLE hfile; 57 HANDLE hmap; 58 59 BYTE *data; 60 61 IMAGE_NT_HEADERS *nthdr; 62 IMAGE_COR20_HEADER *corhdr; 63 64 METADATAHDR *metadatahdr; 65 }; 66 67 static inline LPWSTR strdupW(LPCWSTR src) 68 { 69 LPWSTR dest; 70 71 if (!src) 72 return NULL; 73 74 dest = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(src) + 1) * sizeof(WCHAR)); 75 if (dest) 76 lstrcpyW(dest, src); 77 78 return dest; 79 } 80 81 static void* assembly_rva_to_va(ASSEMBLY *assembly, ULONG rva) 82 { 83 if (assembly->is_mapped_file) 84 return ImageRvaToVa(assembly->nthdr, assembly->data, rva, NULL); 85 else 86 return assembly->data + rva; 87 } 88 89 static ULONG assembly_datadir_get_data(ASSEMBLY *assembly, 90 IMAGE_DATA_DIRECTORY *datadir, void **data) 91 { 92 if (!datadir->VirtualAddress || !datadir->Size) 93 { 94 *data = NULL; 95 return 0; 96 } 97 else 98 { 99 *data = assembly_rva_to_va(assembly, datadir->VirtualAddress); 100 return datadir->Size; 101 } 102 } 103 104 static HRESULT parse_metadata_header(ASSEMBLY *assembly, DWORD *hdrsz) 105 { 106 METADATAHDR *metadatahdr; 107 BYTE *ptr, *dest; 108 DWORD size, ofs; 109 ULONG rva; 110 111 rva = assembly->corhdr->MetaData.VirtualAddress; 112 ptr = assembly_rva_to_va(assembly, rva); 113 if (!ptr) 114 return E_FAIL; 115 116 metadatahdr = (METADATAHDR *)ptr; 117 118 assembly->metadatahdr = HeapAlloc(GetProcessHeap(), 0, sizeof(METADATAHDR)); 119 if (!assembly->metadatahdr) 120 return E_OUTOFMEMORY; 121 122 size = FIELD_OFFSET(METADATAHDR, Version); 123 memcpy(assembly->metadatahdr, metadatahdr, size); 124 125 assembly->metadatahdr->Version = (LPSTR)&metadatahdr->Version; 126 127 ofs = FIELD_OFFSET(METADATAHDR, Flags); 128 ptr += FIELD_OFFSET(METADATAHDR, Version) + metadatahdr->VersionLength + 1; 129 dest = (BYTE *)assembly->metadatahdr + ofs; 130 memcpy(dest, ptr, sizeof(METADATAHDR) - ofs); 131 132 *hdrsz = sizeof(METADATAHDR) - sizeof(LPSTR) + metadatahdr->VersionLength + 1; 133 134 return S_OK; 135 } 136 137 static HRESULT parse_clr_metadata(ASSEMBLY *assembly) 138 { 139 HRESULT hr; 140 DWORD hdrsz; 141 142 hr = parse_metadata_header(assembly, &hdrsz); 143 if (FAILED(hr)) 144 return hr; 145 146 return S_OK; 147 } 148 149 static HRESULT parse_pe_header(ASSEMBLY *assembly) 150 { 151 IMAGE_DATA_DIRECTORY *datadirs; 152 153 assembly->nthdr = ImageNtHeader(assembly->data); 154 if (!assembly->nthdr) 155 return E_FAIL; 156 157 if (assembly->nthdr->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) 158 { 159 IMAGE_OPTIONAL_HEADER64 *opthdr = 160 (IMAGE_OPTIONAL_HEADER64 *)&assembly->nthdr->OptionalHeader; 161 datadirs = opthdr->DataDirectory; 162 } 163 else 164 { 165 IMAGE_OPTIONAL_HEADER32 *opthdr = 166 (IMAGE_OPTIONAL_HEADER32 *)&assembly->nthdr->OptionalHeader; 167 datadirs = opthdr->DataDirectory; 168 } 169 170 if (!datadirs) 171 return E_FAIL; 172 173 if (!assembly_datadir_get_data(assembly, &datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR], (void**)&assembly->corhdr)) 174 return E_FAIL; 175 176 return S_OK; 177 } 178 179 static HRESULT parse_headers(ASSEMBLY *assembly) 180 { 181 HRESULT hr; 182 183 hr = parse_pe_header(assembly); 184 185 if (SUCCEEDED(hr)) 186 hr = parse_clr_metadata(assembly); 187 188 return hr; 189 } 190 191 HRESULT assembly_create(ASSEMBLY **out, LPCWSTR file) 192 { 193 ASSEMBLY *assembly; 194 HRESULT hr; 195 196 *out = NULL; 197 198 assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY)); 199 if (!assembly) 200 return E_OUTOFMEMORY; 201 202 assembly->is_mapped_file = 1; 203 204 assembly->path = strdupW(file); 205 if (!assembly->path) 206 { 207 hr = E_OUTOFMEMORY; 208 goto failed; 209 } 210 211 assembly->hfile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, 212 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 213 if (assembly->hfile == INVALID_HANDLE_VALUE) 214 { 215 hr = HRESULT_FROM_WIN32(GetLastError()); 216 goto failed; 217 } 218 219 assembly->hmap = CreateFileMappingW(assembly->hfile, NULL, PAGE_READONLY, 220 0, 0, NULL); 221 if (!assembly->hmap) 222 { 223 hr = HRESULT_FROM_WIN32(GetLastError()); 224 goto failed; 225 } 226 227 assembly->data = MapViewOfFile(assembly->hmap, FILE_MAP_READ, 0, 0, 0); 228 if (!assembly->data) 229 { 230 hr = HRESULT_FROM_WIN32(GetLastError()); 231 goto failed; 232 } 233 234 hr = parse_headers(assembly); 235 if (FAILED(hr)) goto failed; 236 237 *out = assembly; 238 return S_OK; 239 240 failed: 241 assembly_release(assembly); 242 return hr; 243 } 244 245 HRESULT assembly_from_hmodule(ASSEMBLY **out, HMODULE hmodule) 246 { 247 ASSEMBLY *assembly; 248 HRESULT hr; 249 250 *out = NULL; 251 252 assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY)); 253 if (!assembly) 254 return E_OUTOFMEMORY; 255 256 assembly->is_mapped_file = 0; 257 258 assembly->data = (BYTE*)hmodule; 259 260 hr = parse_headers(assembly); 261 if (SUCCEEDED(hr)) 262 *out = assembly; 263 else 264 assembly_release(assembly); 265 266 return hr; 267 } 268 269 HRESULT assembly_release(ASSEMBLY *assembly) 270 { 271 if (!assembly) 272 return S_OK; 273 274 if (assembly->is_mapped_file) 275 { 276 UnmapViewOfFile(assembly->data); 277 CloseHandle(assembly->hmap); 278 CloseHandle(assembly->hfile); 279 } 280 HeapFree(GetProcessHeap(), 0, assembly->metadatahdr); 281 HeapFree(GetProcessHeap(), 0, assembly->path); 282 HeapFree(GetProcessHeap(), 0, assembly); 283 284 return S_OK; 285 } 286 287 HRESULT assembly_get_runtime_version(ASSEMBLY *assembly, LPSTR *version) 288 { 289 *version = assembly->metadatahdr->Version; 290 291 return S_OK; 292 } 293 294 HRESULT assembly_get_vtable_fixups(ASSEMBLY *assembly, VTableFixup **fixups, DWORD *count) 295 { 296 ULONG size; 297 298 size = assembly_datadir_get_data(assembly, &assembly->corhdr->VTableFixups, (void**)fixups); 299 *count = size / sizeof(VTableFixup); 300 301 return S_OK; 302 } 303