winex11: add owned windows to taskbar if owner is not mapped
[wine] / dlls / fusion / assembly.c
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 <stdarg.h>
22 #include <stdio.h>
23
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"
32
33 #include "fusionpriv.h"
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36
37 #define MAX_CLR_TABLES  64
38
39 typedef struct tagCLRTABLE
40 {
41     DWORD rows;
42     DWORD offset;
43 } CLRTABLE;
44
45 struct tagASSEMBLY
46 {
47     LPSTR path;
48
49     HANDLE hfile;
50     HANDLE hmap;
51     BYTE *data;
52
53     IMAGE_NT_HEADERS32 *nthdr;
54     IMAGE_COR20_HEADER *corhdr;
55
56     METADATAHDR *metadatahdr;
57
58     METADATATABLESHDR *tableshdr;
59     DWORD numtables;
60     DWORD *numrows;
61     CLRTABLE tables[MAX_CLR_TABLES];
62
63     BYTE *strings;
64     BYTE *blobs;
65 };
66
67 /* FIXME: fill in */
68 const DWORD COR_TABLE_SIZES[64] =
69 {
70     sizeof(MODULETABLE),
71     sizeof(TYPEREFTABLE),
72     sizeof(TYPEDEFTABLE),
73     0,
74     0,
75     0,
76     0,
77     0,
78     0,
79     0,
80     0,
81     0,
82     0,
83     0,
84     0,
85     0,
86     0,
87     0,
88     0,
89     0,
90     0,
91     0,
92     0,
93     0,
94     0,
95     0,
96     0,
97     0,
98     0,
99     0,
100     0,
101     0,
102     sizeof(ASSEMBLYTABLE),
103     0,
104     0,
105     0,
106     0,
107     0,
108     0,
109     0,
110     sizeof(MANIFESTRESTABLE),
111     0,
112     0,
113     0,
114     0,
115     0,
116     0,
117     0,
118     0,
119     0,
120     0,
121     0,
122     0,
123     0,
124     0,
125     0,
126     0,
127     0,
128     0,
129     0,
130     0,
131     0,
132     0,
133     0
134 };
135
136 static LPSTR strdupWtoA(LPCWSTR str)
137 {
138     LPSTR ret = NULL;
139     DWORD len;
140
141     if (!str)
142         return ret;
143
144     len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
145     ret = HeapAlloc(GetProcessHeap(), 0, len);
146     if (ret)
147         WideCharToMultiByte(CP_ACP, 0, str, -1, ret, len, NULL, NULL);
148
149     return ret;
150 }
151
152 static DWORD rva_to_offset(IMAGE_NT_HEADERS *nthdrs, DWORD rva)
153 {
154     DWORD offset = rva, limit;
155     IMAGE_SECTION_HEADER *img;
156     WORD i;
157
158     img = IMAGE_FIRST_SECTION(nthdrs);
159
160     if (rva < img->PointerToRawData)
161         return rva;
162
163     for (i = 0; i < nthdrs->FileHeader.NumberOfSections; i++)
164     {
165         if (img[i].SizeOfRawData)
166             limit = img[i].SizeOfRawData;
167         else
168             limit = img[i].Misc.VirtualSize;
169
170         if (rva >= img[i].VirtualAddress &&
171             rva < (img[i].VirtualAddress + limit))
172         {
173             if (img[i].PointerToRawData != 0)
174             {
175                 offset -= img[i].VirtualAddress;
176                 offset += img[i].PointerToRawData;
177             }
178
179             return offset;
180         }
181     }
182
183     return 0;
184 }
185
186 static BYTE *GetData(BYTE *pData, ULONG *pLength)
187 {
188     if ((*pData & 0x80) == 0x00)
189     {
190         *pLength = (*pData & 0x7f);
191         return pData + 1;
192     }
193
194     if ((*pData & 0xC0) == 0x80)
195     {
196         *pLength = ((*pData & 0x3f) << 8 | *(pData + 1));
197         return pData + 2;
198     }
199
200     if ((*pData & 0xE0) == 0xC0)
201     {
202         *pLength = ((*pData & 0x1f) << 24 | *(pData + 1) << 16 |
203                     *(pData + 2) << 8 | *(pData + 3));
204         return pData + 4;
205     }
206
207     *pLength = (ULONG)-1;
208     return 0;
209 }
210
211 static VOID *assembly_data_offset(ASSEMBLY *assembly, ULONG offset)
212 {
213     return (VOID *)&assembly->data[offset];
214 }
215
216 static HRESULT parse_clr_tables(ASSEMBLY *assembly, ULONG offset)
217 {
218     DWORD i, previ, offidx;
219     ULONG currofs;
220
221     currofs = offset;
222     assembly->tableshdr = (METADATATABLESHDR *)assembly_data_offset(assembly, currofs);
223     if (!assembly->tableshdr)
224         return E_FAIL;
225
226     currofs += sizeof(METADATATABLESHDR);
227     assembly->numrows = (DWORD *)assembly_data_offset(assembly, currofs);
228     if (!assembly->numrows)
229         return E_FAIL;
230
231     assembly->numtables = 0;
232     for (i = 0; i < MAX_CLR_TABLES; i++)
233     {
234         if ((i < 32 && (assembly->tableshdr->MaskValid.LowPart >> i) & 1) ||
235             (i >= 32 && (assembly->tableshdr->MaskValid.HighPart >> i) & 1))
236         {
237             assembly->numtables++;
238         }
239     }
240
241     currofs += assembly->numtables * sizeof(DWORD);
242     memset(assembly->tables, -1, MAX_CLR_TABLES * sizeof(CLRTABLE));
243
244     if (assembly->tableshdr->MaskValid.LowPart & 1)
245     {
246         assembly->tables[0].offset = currofs;
247         assembly->tables[0].rows = assembly->numrows[0];
248     }
249
250     previ = 0;
251     offidx = 1;
252     for (i = 1; i < MAX_CLR_TABLES; i++)
253     {
254         if ((i < 32 && (assembly->tableshdr->MaskValid.LowPart >> i) & 1) ||
255             (i >= 32 && (assembly->tableshdr->MaskValid.HighPart >> i) & 1))
256         {
257             currofs += COR_TABLE_SIZES[previ] * assembly->numrows[offidx - 1];
258             assembly->tables[i].offset = currofs;
259             assembly->tables[i].rows = assembly->numrows[offidx];
260             offidx++;
261             previ = i;
262         }
263     }
264
265     return S_OK;
266 }
267
268 static HRESULT parse_clr_metadata(ASSEMBLY *assembly)
269 {
270     METADATASTREAMHDR *streamhdr;
271     ULONG rva, i, ofs;
272     LPSTR stream;
273     HRESULT hr;
274     BYTE *ptr;
275
276     rva = assembly->corhdr->MetaData.VirtualAddress;
277     assembly->metadatahdr = ImageRvaToVa(assembly->nthdr, assembly->data,
278                                          rva, NULL);
279     if (!assembly->metadatahdr)
280         return E_FAIL;
281
282     ptr = ImageRvaToVa(assembly->nthdr, assembly->data,
283                        rva + sizeof(METADATAHDR), NULL);
284     if (!ptr)
285         return E_FAIL;
286
287     for (i = 0; i < assembly->metadatahdr->Streams; i++)
288     {
289         streamhdr = (METADATASTREAMHDR *)ptr;
290         ofs = rva_to_offset(assembly->nthdr, rva + streamhdr->Offset);
291
292         ptr += sizeof(METADATASTREAMHDR);
293         stream = (LPSTR)ptr;
294
295         if (!lstrcmpA(stream, "#~"))
296         {
297             hr = parse_clr_tables(assembly, ofs);
298             if (FAILED(hr))
299                 return hr;
300         }
301         else if (!lstrcmpA(stream, "#Strings"))
302             assembly->strings = (BYTE *)assembly_data_offset(assembly, ofs);
303         else if (!lstrcmpA(stream, "#Blob"))
304             assembly->blobs = (BYTE *)assembly_data_offset(assembly, ofs);
305
306         ptr += lstrlenA(stream);
307         while (!*ptr) ptr++;
308     }
309
310     return S_OK;
311 }
312
313 static HRESULT parse_pe_header(ASSEMBLY *assembly)
314 {
315     IMAGE_DATA_DIRECTORY *datadirs;
316
317     assembly->nthdr = ImageNtHeader(assembly->data);
318     if (!assembly->nthdr)
319         return E_FAIL;
320
321     datadirs = assembly->nthdr->OptionalHeader.DataDirectory;
322     if (!datadirs)
323         return E_FAIL;
324
325     if (!datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress ||
326         !datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size)
327     {
328         return E_FAIL;
329     }
330
331     assembly->corhdr = ImageRvaToVa(assembly->nthdr, assembly->data,
332         datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress, NULL);
333     if (!assembly->corhdr)
334         return E_FAIL;
335
336     return S_OK;
337 }
338
339 HRESULT assembly_create(ASSEMBLY **out, LPCWSTR file)
340 {
341     ASSEMBLY *assembly;
342     HRESULT hr;
343
344     *out = NULL;
345
346     assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY));
347     if (!assembly)
348         return E_OUTOFMEMORY;
349
350     assembly->path = strdupWtoA(file);
351     if (!assembly->path)
352     {
353         hr = E_OUTOFMEMORY;
354         goto failed;
355     }
356
357     assembly->hfile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ,
358                                   NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
359     if (assembly->hfile == INVALID_HANDLE_VALUE)
360     {
361         hr = HRESULT_FROM_WIN32(GetLastError());
362         goto failed;
363     }
364
365     assembly->hmap = CreateFileMappingW(assembly->hfile, NULL, PAGE_READONLY,
366                                         0, 0, NULL);
367     if (!assembly->hmap)
368     {
369         hr = HRESULT_FROM_WIN32(GetLastError());
370         goto failed;
371     }
372
373     assembly->data = MapViewOfFile(assembly->hmap, FILE_MAP_READ, 0, 0, 0);
374     if (!assembly->data)
375     {
376         hr = HRESULT_FROM_WIN32(GetLastError());
377         goto failed;
378     }
379
380     hr = parse_pe_header(assembly);
381     if (FAILED(hr)) goto failed;
382
383     hr = parse_clr_metadata(assembly);
384     if (FAILED(hr)) goto failed;
385
386     *out = assembly;
387     return S_OK;
388
389 failed:
390     assembly_release( assembly );
391     return hr;
392 }
393
394 HRESULT assembly_release(ASSEMBLY *assembly)
395 {
396     if (!assembly)
397         return S_OK;
398
399     HeapFree(GetProcessHeap(), 0, assembly->path);
400     UnmapViewOfFile(assembly->data);
401     CloseHandle(assembly->hmap);
402     CloseHandle(assembly->hfile);
403     HeapFree(GetProcessHeap(), 0, assembly);
404
405     return S_OK;
406 }
407
408 static LPSTR assembly_dup_str(ASSEMBLY *assembly, WORD index)
409 {
410     return strdup((LPSTR)&assembly->strings[index]);
411 }
412
413 HRESULT assembly_get_name(ASSEMBLY *assembly, LPSTR *name)
414 {
415     ASSEMBLYTABLE *asmtbl;
416     ULONG offset;
417
418     offset = assembly->tables[0x20].offset; /* FIXME: add constants */
419     if (offset == -1)
420         return E_FAIL;
421
422     asmtbl = (ASSEMBLYTABLE *)assembly_data_offset(assembly, offset);
423     if (!asmtbl)
424         return E_FAIL;
425
426     *name = assembly_dup_str(assembly, asmtbl->Name);
427     if (!*name)
428         return E_OUTOFMEMORY;
429
430     return S_OK;
431 }
432
433 HRESULT assembly_get_path(ASSEMBLY *assembly, LPSTR *path)
434 {
435     *path = strdup(assembly->path);
436     if (!*path)
437         return E_OUTOFMEMORY;
438
439     return S_OK;
440 }
441
442 HRESULT assembly_get_version(ASSEMBLY *assembly, LPSTR *version)
443 {
444     LPSTR verdata;
445     VS_FIXEDFILEINFO *ffi;
446     HRESULT hr = S_OK;
447     DWORD size;
448
449     size = GetFileVersionInfoSizeA(assembly->path, NULL);
450     if (!size)
451         return HRESULT_FROM_WIN32(GetLastError());
452
453     verdata = HeapAlloc(GetProcessHeap(), 0, size);
454     if (!verdata)
455         return E_OUTOFMEMORY;
456
457     if (!GetFileVersionInfoA(assembly->path, 0, size, verdata))
458     {
459         hr = HRESULT_FROM_WIN32(GetLastError());
460         goto done;
461     }
462
463     if (!VerQueryValueA(verdata, "\\", (LPVOID *)&ffi, &size))
464     {
465         hr = HRESULT_FROM_WIN32(GetLastError());
466         goto done;
467     }
468
469     *version = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
470     if (!*version)
471     {
472         hr = E_OUTOFMEMORY;
473         goto done;
474     }
475
476     sprintf(*version, "%d.%d.%d.%d", HIWORD(ffi->dwFileVersionMS),
477             LOWORD(ffi->dwFileVersionMS), HIWORD(ffi->dwFileVersionLS),
478             LOWORD(ffi->dwFileVersionLS));
479
480 done:
481     HeapFree(GetProcessHeap(), 0, verdata);
482     return hr;
483 }
484
485 HRESULT assembly_get_architecture(ASSEMBLY *assembly, DWORD fixme)
486 {
487     /* FIXME */
488     return S_OK;
489 }
490
491 static BYTE *assembly_get_blob(ASSEMBLY *assembly, WORD index, ULONG *size)
492 {
493     return GetData(&assembly->blobs[index], size);
494 }
495
496 static void bytes_to_str(BYTE *bytes, DWORD len, LPSTR str)
497 {
498     int i;
499
500     static const char hexval[16] = {
501         '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
502     };
503
504     for(i = 0; i < len; i++)
505     {
506         str[i * 2] = hexval[((bytes[i] >> 4) & 0xF)];
507         str[i * 2 + 1] = hexval[(bytes[i]) & 0x0F];
508     }
509 }
510
511 #define BYTES_PER_TOKEN 8
512 #define CHARS_PER_BYTE 2
513 #define TOKEN_LENGTH (BYTES_PER_TOKEN * CHARS_PER_BYTE + 1)
514
515 HRESULT assembly_get_pubkey_token(ASSEMBLY *assembly, LPSTR *token)
516 {
517     ASSEMBLYTABLE *asmtbl;
518     ULONG i, offset, size;
519     BYTE *hashdata;
520     HCRYPTPROV crypt;
521     HCRYPTHASH hash;
522     BYTE *pubkey;
523     BYTE tokbytes[BYTES_PER_TOKEN];
524     HRESULT hr = E_FAIL;
525     LPSTR tok;
526
527     *token = NULL;
528
529     offset = assembly->tables[0x20].offset; /* FIXME: add constants */
530     if (offset == -1)
531         return E_FAIL;
532
533     asmtbl = (ASSEMBLYTABLE *)assembly_data_offset(assembly, offset);
534     if (!asmtbl)
535         return E_FAIL;
536
537     pubkey = assembly_get_blob(assembly, asmtbl->PublicKey, &size);
538
539     if (!CryptAcquireContextA(&crypt, NULL, NULL, PROV_RSA_FULL,
540                               CRYPT_VERIFYCONTEXT))
541         return E_FAIL;
542
543     if (!CryptCreateHash(crypt, CALG_SHA1, 0, 0, &hash))
544         return E_FAIL;
545
546     if (!CryptHashData(hash, pubkey, size, 0))
547         return E_FAIL;
548
549     size = 0;
550     if (!CryptGetHashParam(hash, HP_HASHVAL, NULL, &size, 0))
551         return E_FAIL;
552
553     hashdata = HeapAlloc(GetProcessHeap(), 0, size);
554     if (!hashdata)
555     {
556         hr = E_OUTOFMEMORY;
557         goto done;
558     }
559
560     if (!CryptGetHashParam(hash, HP_HASHVAL, hashdata, &size, 0))
561         goto done;
562
563     for (i = size - 1; i >= size - 8; i--)
564         tokbytes[size - i - 1] = hashdata[i];
565
566     tok = HeapAlloc(GetProcessHeap(), 0, TOKEN_LENGTH);
567     if (!tok)
568     {
569         hr = E_OUTOFMEMORY;
570         goto done;
571     }
572
573     bytes_to_str(tokbytes, BYTES_PER_TOKEN, tok);
574     tok[TOKEN_LENGTH - 1] = '\0';
575
576     *token = tok;
577     hr = S_OK;
578
579 done:
580     HeapFree(GetProcessHeap(), 0, hashdata);
581     CryptDestroyHash(hash);
582     CryptReleaseContext(crypt, 0);
583
584     return hr;
585 }