quartz: Fix string buffer overflow.
[wine.git] / dlls / fusion / assembly.c
blob94ea5424e184cca1319f9809b9b542e36500e6cc
1 /*
2 * assembly parser
4 * Copyright 2008 James Hawkins
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.
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.
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
21 #include <stdarg.h>
22 #include <stdio.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "winver.h"
28 #include "wincrypt.h"
29 #include "dbghelp.h"
30 #include "ole2.h"
31 #include "fusion.h"
33 #include "fusionpriv.h"
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
37 #define MAX_CLR_TABLES 64
39 typedef struct tagCLRTABLE
41 DWORD rows;
42 DWORD offset;
43 } CLRTABLE;
45 struct tagASSEMBLY
47 LPSTR path;
49 HANDLE hfile;
50 HANDLE hmap;
51 BYTE *data;
53 IMAGE_NT_HEADERS32 *nthdr;
54 IMAGE_COR20_HEADER *corhdr;
56 METADATAHDR *metadatahdr;
58 METADATATABLESHDR *tableshdr;
59 DWORD numtables;
60 DWORD *numrows;
61 CLRTABLE tables[MAX_CLR_TABLES];
63 BYTE *strings;
64 BYTE *blobs;
67 const DWORD COR_TABLE_SIZES[64] =
69 sizeof(MODULETABLE),
70 sizeof(TYPEREFTABLE),
71 sizeof(TYPEDEFTABLE),
73 sizeof(FIELDTABLE),
75 sizeof(METHODDEFTABLE),
77 sizeof(PARAMTABLE),
78 sizeof(INTERFACEIMPLTABLE),
79 sizeof(MEMBERREFTABLE),
80 sizeof(CONSTANTTABLE),
81 sizeof(CUSTOMATTRIBUTETABLE),
82 sizeof(FIELDMARSHALTABLE),
83 sizeof(DECLSECURITYTABLE),
84 sizeof(CLASSLAYOUTTABLE),
85 sizeof(FIELDLAYOUTTABLE),
86 sizeof(STANDALONESIGTABLE),
87 sizeof(EVENTMAPTABLE),
89 sizeof(EVENTTABLE),
90 sizeof(PROPERTYMAPTABLE),
92 sizeof(PROPERTYTABLE),
93 sizeof(METHODSEMANTICSTABLE),
94 sizeof(METHODIMPLTABLE),
95 sizeof(MODULEREFTABLE),
96 sizeof(TYPESPECTABLE),
97 sizeof(IMPLMAPTABLE),
98 sizeof(FIELDRVATABLE),
101 sizeof(ASSEMBLYTABLE),
102 sizeof(ASSEMBLYPROCESSORTABLE),
103 sizeof(ASSEMBLYOSTABLE),
104 sizeof(ASSEMBLYREFTABLE),
105 sizeof(ASSEMBLYREFPROCESSORTABLE),
106 sizeof(ASSEMBLYREFOSTABLE),
107 sizeof(FILETABLE),
108 sizeof(EXPORTEDTYPETABLE),
109 sizeof(MANIFESTRESTABLE),
110 sizeof(NESTEDCLASSTABLE),
111 sizeof(GENERICPARAMTABLE),
112 sizeof(METHODSPECTABLE),
113 sizeof(GENERICPARAMCONSTRAINTTABLE),
135 static LPSTR strdupWtoA(LPCWSTR str)
137 LPSTR ret = NULL;
138 DWORD len;
140 if (!str)
141 return ret;
143 len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
144 ret = HeapAlloc(GetProcessHeap(), 0, len);
145 if (ret)
146 WideCharToMultiByte(CP_ACP, 0, str, -1, ret, len, NULL, NULL);
148 return ret;
151 static DWORD rva_to_offset(IMAGE_NT_HEADERS *nthdrs, DWORD rva)
153 DWORD offset = rva, limit;
154 IMAGE_SECTION_HEADER *img;
155 WORD i;
157 img = IMAGE_FIRST_SECTION(nthdrs);
159 if (rva < img->PointerToRawData)
160 return rva;
162 for (i = 0; i < nthdrs->FileHeader.NumberOfSections; i++)
164 if (img[i].SizeOfRawData)
165 limit = img[i].SizeOfRawData;
166 else
167 limit = img[i].Misc.VirtualSize;
169 if (rva >= img[i].VirtualAddress &&
170 rva < (img[i].VirtualAddress + limit))
172 if (img[i].PointerToRawData != 0)
174 offset -= img[i].VirtualAddress;
175 offset += img[i].PointerToRawData;
178 return offset;
182 return 0;
185 static BYTE *GetData(BYTE *pData, ULONG *pLength)
187 if ((*pData & 0x80) == 0x00)
189 *pLength = (*pData & 0x7f);
190 return pData + 1;
193 if ((*pData & 0xC0) == 0x80)
195 *pLength = ((*pData & 0x3f) << 8 | *(pData + 1));
196 return pData + 2;
199 if ((*pData & 0xE0) == 0xC0)
201 *pLength = ((*pData & 0x1f) << 24 | *(pData + 1) << 16 |
202 *(pData + 2) << 8 | *(pData + 3));
203 return pData + 4;
206 *pLength = (ULONG)-1;
207 return 0;
210 static VOID *assembly_data_offset(ASSEMBLY *assembly, ULONG offset)
212 return (VOID *)&assembly->data[offset];
215 static HRESULT parse_clr_tables(ASSEMBLY *assembly, ULONG offset)
217 DWORD i, previ, offidx;
218 ULONG currofs;
220 currofs = offset;
221 assembly->tableshdr = (METADATATABLESHDR *)assembly_data_offset(assembly, currofs);
222 if (!assembly->tableshdr)
223 return E_FAIL;
225 currofs += sizeof(METADATATABLESHDR);
226 assembly->numrows = (DWORD *)assembly_data_offset(assembly, currofs);
227 if (!assembly->numrows)
228 return E_FAIL;
230 assembly->numtables = 0;
231 for (i = 0; i < MAX_CLR_TABLES; i++)
233 if ((i < 32 && (assembly->tableshdr->MaskValid.u.LowPart >> i) & 1) ||
234 (i >= 32 && (assembly->tableshdr->MaskValid.u.HighPart >> i) & 1))
236 assembly->numtables++;
240 currofs += assembly->numtables * sizeof(DWORD);
241 memset(assembly->tables, -1, MAX_CLR_TABLES * sizeof(CLRTABLE));
243 if (assembly->tableshdr->MaskValid.u.LowPart & 1)
245 assembly->tables[0].offset = currofs;
246 assembly->tables[0].rows = assembly->numrows[0];
249 previ = 0;
250 offidx = 1;
251 for (i = 1; i < MAX_CLR_TABLES; i++)
253 if ((i < 32 && (assembly->tableshdr->MaskValid.u.LowPart >> i) & 1) ||
254 (i >= 32 && (assembly->tableshdr->MaskValid.u.HighPart >> i) & 1))
256 currofs += COR_TABLE_SIZES[previ] * assembly->numrows[offidx - 1];
257 assembly->tables[i].offset = currofs;
258 assembly->tables[i].rows = assembly->numrows[offidx];
259 offidx++;
260 previ = i;
264 return S_OK;
267 static HRESULT parse_clr_metadata(ASSEMBLY *assembly)
269 METADATASTREAMHDR *streamhdr;
270 ULONG rva, i, ofs;
271 LPSTR stream;
272 HRESULT hr;
273 BYTE *ptr;
275 rva = assembly->corhdr->MetaData.VirtualAddress;
276 assembly->metadatahdr = ImageRvaToVa(assembly->nthdr, assembly->data,
277 rva, NULL);
278 if (!assembly->metadatahdr)
279 return E_FAIL;
281 ptr = ImageRvaToVa(assembly->nthdr, assembly->data,
282 rva + sizeof(METADATAHDR), NULL);
283 if (!ptr)
284 return E_FAIL;
286 for (i = 0; i < assembly->metadatahdr->Streams; i++)
288 streamhdr = (METADATASTREAMHDR *)ptr;
289 ofs = rva_to_offset(assembly->nthdr, rva + streamhdr->Offset);
291 ptr += sizeof(METADATASTREAMHDR);
292 stream = (LPSTR)ptr;
294 if (!lstrcmpA(stream, "#~"))
296 hr = parse_clr_tables(assembly, ofs);
297 if (FAILED(hr))
298 return hr;
300 else if (!lstrcmpA(stream, "#Strings") || !lstrcmpA(stream, "Strings"))
301 assembly->strings = (BYTE *)assembly_data_offset(assembly, ofs);
302 else if (!lstrcmpA(stream, "#Blob"))
303 assembly->blobs = (BYTE *)assembly_data_offset(assembly, ofs);
305 ptr += lstrlenA(stream);
306 while (!*ptr) ptr++;
309 return S_OK;
312 static HRESULT parse_pe_header(ASSEMBLY *assembly)
314 IMAGE_DATA_DIRECTORY *datadirs;
316 assembly->nthdr = ImageNtHeader(assembly->data);
317 if (!assembly->nthdr)
318 return E_FAIL;
320 datadirs = assembly->nthdr->OptionalHeader.DataDirectory;
321 if (!datadirs)
322 return E_FAIL;
324 if (!datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress ||
325 !datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size)
327 return E_FAIL;
330 assembly->corhdr = ImageRvaToVa(assembly->nthdr, assembly->data,
331 datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress, NULL);
332 if (!assembly->corhdr)
333 return E_FAIL;
335 return S_OK;
338 HRESULT assembly_create(ASSEMBLY **out, LPCWSTR file)
340 ASSEMBLY *assembly;
341 HRESULT hr;
343 *out = NULL;
345 assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY));
346 if (!assembly)
347 return E_OUTOFMEMORY;
349 assembly->path = strdupWtoA(file);
350 if (!assembly->path)
352 hr = E_OUTOFMEMORY;
353 goto failed;
356 assembly->hfile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ,
357 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
358 if (assembly->hfile == INVALID_HANDLE_VALUE)
360 hr = HRESULT_FROM_WIN32(GetLastError());
361 goto failed;
364 assembly->hmap = CreateFileMappingW(assembly->hfile, NULL, PAGE_READONLY,
365 0, 0, NULL);
366 if (!assembly->hmap)
368 hr = HRESULT_FROM_WIN32(GetLastError());
369 goto failed;
372 assembly->data = MapViewOfFile(assembly->hmap, FILE_MAP_READ, 0, 0, 0);
373 if (!assembly->data)
375 hr = HRESULT_FROM_WIN32(GetLastError());
376 goto failed;
379 hr = parse_pe_header(assembly);
380 if (FAILED(hr)) goto failed;
382 hr = parse_clr_metadata(assembly);
383 if (FAILED(hr)) goto failed;
385 *out = assembly;
386 return S_OK;
388 failed:
389 assembly_release( assembly );
390 return hr;
393 HRESULT assembly_release(ASSEMBLY *assembly)
395 if (!assembly)
396 return S_OK;
398 HeapFree(GetProcessHeap(), 0, assembly->path);
399 UnmapViewOfFile(assembly->data);
400 CloseHandle(assembly->hmap);
401 CloseHandle(assembly->hfile);
402 HeapFree(GetProcessHeap(), 0, assembly);
404 return S_OK;
407 static LPSTR assembly_dup_str(ASSEMBLY *assembly, WORD index)
409 LPSTR str = (LPSTR)&assembly->strings[index];
410 LPSTR cpy = HeapAlloc(GetProcessHeap(), 0, strlen(str)+1);
411 if (cpy)
412 strcpy(cpy, str);
413 return cpy;
416 HRESULT assembly_get_name(ASSEMBLY *assembly, LPSTR *name)
418 ASSEMBLYTABLE *asmtbl;
419 ULONG offset;
421 offset = assembly->tables[0x20].offset; /* FIXME: add constants */
422 if (offset == -1)
423 return E_FAIL;
425 asmtbl = (ASSEMBLYTABLE *)assembly_data_offset(assembly, offset);
426 if (!asmtbl)
427 return E_FAIL;
429 *name = assembly_dup_str(assembly, asmtbl->Name);
430 if (!*name)
431 return E_OUTOFMEMORY;
433 return S_OK;
436 HRESULT assembly_get_path(ASSEMBLY *assembly, LPSTR *path)
438 LPSTR cpy = HeapAlloc(GetProcessHeap(), 0, strlen(assembly->path)+1);
439 *path = cpy;
440 if (cpy)
441 strcpy(cpy, assembly->path);
442 else
443 return E_OUTOFMEMORY;
445 return S_OK;
448 HRESULT assembly_get_version(ASSEMBLY *assembly, LPSTR *version)
450 LPSTR verdata;
451 VS_FIXEDFILEINFO *ffi;
452 HRESULT hr = S_OK;
453 DWORD size;
455 size = GetFileVersionInfoSizeA(assembly->path, NULL);
456 if (!size)
457 return HRESULT_FROM_WIN32(GetLastError());
459 verdata = HeapAlloc(GetProcessHeap(), 0, size);
460 if (!verdata)
461 return E_OUTOFMEMORY;
463 if (!GetFileVersionInfoA(assembly->path, 0, size, verdata))
465 hr = HRESULT_FROM_WIN32(GetLastError());
466 goto done;
469 if (!VerQueryValueA(verdata, "\\", (LPVOID *)&ffi, &size))
471 hr = HRESULT_FROM_WIN32(GetLastError());
472 goto done;
475 *version = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
476 if (!*version)
478 hr = E_OUTOFMEMORY;
479 goto done;
482 sprintf(*version, "%d.%d.%d.%d", HIWORD(ffi->dwFileVersionMS),
483 LOWORD(ffi->dwFileVersionMS), HIWORD(ffi->dwFileVersionLS),
484 LOWORD(ffi->dwFileVersionLS));
486 done:
487 HeapFree(GetProcessHeap(), 0, verdata);
488 return hr;
491 HRESULT assembly_get_architecture(ASSEMBLY *assembly, DWORD fixme)
493 /* FIXME */
494 return S_OK;
497 static BYTE *assembly_get_blob(ASSEMBLY *assembly, WORD index, ULONG *size)
499 return GetData(&assembly->blobs[index], size);
502 static void bytes_to_str(BYTE *bytes, DWORD len, LPSTR str)
504 int i;
506 static const char hexval[16] = {
507 '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
510 for(i = 0; i < len; i++)
512 str[i * 2] = hexval[((bytes[i] >> 4) & 0xF)];
513 str[i * 2 + 1] = hexval[(bytes[i]) & 0x0F];
517 #define BYTES_PER_TOKEN 8
518 #define CHARS_PER_BYTE 2
519 #define TOKEN_LENGTH (BYTES_PER_TOKEN * CHARS_PER_BYTE + 1)
521 HRESULT assembly_get_pubkey_token(ASSEMBLY *assembly, LPSTR *token)
523 ASSEMBLYTABLE *asmtbl;
524 ULONG i, offset, size;
525 BYTE *hashdata;
526 HCRYPTPROV crypt;
527 HCRYPTHASH hash;
528 BYTE *pubkey;
529 BYTE tokbytes[BYTES_PER_TOKEN];
530 HRESULT hr = E_FAIL;
531 LPSTR tok;
533 *token = NULL;
535 offset = assembly->tables[0x20].offset; /* FIXME: add constants */
536 if (offset == -1)
537 return E_FAIL;
539 asmtbl = (ASSEMBLYTABLE *)assembly_data_offset(assembly, offset);
540 if (!asmtbl)
541 return E_FAIL;
543 pubkey = assembly_get_blob(assembly, asmtbl->PublicKey, &size);
545 if (!CryptAcquireContextA(&crypt, NULL, NULL, PROV_RSA_FULL,
546 CRYPT_VERIFYCONTEXT))
547 return E_FAIL;
549 if (!CryptCreateHash(crypt, CALG_SHA1, 0, 0, &hash))
550 return E_FAIL;
552 if (!CryptHashData(hash, pubkey, size, 0))
553 return E_FAIL;
555 size = 0;
556 if (!CryptGetHashParam(hash, HP_HASHVAL, NULL, &size, 0))
557 return E_FAIL;
559 hashdata = HeapAlloc(GetProcessHeap(), 0, size);
560 if (!hashdata)
562 hr = E_OUTOFMEMORY;
563 goto done;
566 if (!CryptGetHashParam(hash, HP_HASHVAL, hashdata, &size, 0))
567 goto done;
569 for (i = size - 1; i >= size - 8; i--)
570 tokbytes[size - i - 1] = hashdata[i];
572 tok = HeapAlloc(GetProcessHeap(), 0, TOKEN_LENGTH);
573 if (!tok)
575 hr = E_OUTOFMEMORY;
576 goto done;
579 bytes_to_str(tokbytes, BYTES_PER_TOKEN, tok);
580 tok[TOKEN_LENGTH - 1] = '\0';
582 *token = tok;
583 hr = S_OK;
585 done:
586 HeapFree(GetProcessHeap(), 0, hashdata);
587 CryptDestroyHash(hash);
588 CryptReleaseContext(crypt, 0);
590 return hr;