| /* |
| * assembly parser |
| * |
| * Copyright 2008 James Hawkins |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winuser.h" |
| #include "winver.h" |
| #include "wincrypt.h" |
| #include "dbghelp.h" |
| #include "ole2.h" |
| #include "fusion.h" |
| #include "corhdr.h" |
| |
| #include "fusionpriv.h" |
| #include "wine/debug.h" |
| #include "wine/unicode.h" |
| |
| #define TableFromToken(tk) (TypeFromToken(tk) >> 24) |
| #define TokenFromTable(idx) (idx << 24) |
| |
| #define MAX_CLR_TABLES 64 |
| |
| #define MD_STRINGS_BIT 0x1 |
| #define MD_GUIDS_BIT 0x2 |
| #define MD_BLOBS_BIT 0x4 |
| |
| typedef struct tagCLRTABLE |
| { |
| INT rows; |
| DWORD offset; |
| } CLRTABLE; |
| |
| struct tagASSEMBLY |
| { |
| LPWSTR path; |
| |
| HANDLE hfile; |
| HANDLE hmap; |
| BYTE *data; |
| |
| IMAGE_NT_HEADERS *nthdr; |
| IMAGE_COR20_HEADER *corhdr; |
| |
| METADATAHDR *metadatahdr; |
| |
| METADATATABLESHDR *tableshdr; |
| DWORD numtables; |
| DWORD *numrows; |
| CLRTABLE tables[MAX_CLR_TABLES]; |
| |
| DWORD stringsz; |
| DWORD guidsz; |
| DWORD blobsz; |
| |
| BYTE *strings; |
| BYTE *blobs; |
| }; |
| |
| static DWORD rva_to_offset(IMAGE_NT_HEADERS *nthdrs, DWORD rva) |
| { |
| DWORD offset = rva, limit; |
| IMAGE_SECTION_HEADER *img; |
| WORD i; |
| |
| img = IMAGE_FIRST_SECTION(nthdrs); |
| |
| if (rva < img->PointerToRawData) |
| return rva; |
| |
| for (i = 0; i < nthdrs->FileHeader.NumberOfSections; i++) |
| { |
| if (img[i].SizeOfRawData) |
| limit = img[i].SizeOfRawData; |
| else |
| limit = img[i].Misc.VirtualSize; |
| |
| if (rva >= img[i].VirtualAddress && |
| rva < (img[i].VirtualAddress + limit)) |
| { |
| if (img[i].PointerToRawData != 0) |
| { |
| offset -= img[i].VirtualAddress; |
| offset += img[i].PointerToRawData; |
| } |
| |
| return offset; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static BYTE *GetData(BYTE *pData, ULONG *pLength) |
| { |
| if ((*pData & 0x80) == 0x00) |
| { |
| *pLength = (*pData & 0x7f); |
| return pData + 1; |
| } |
| |
| if ((*pData & 0xC0) == 0x80) |
| { |
| *pLength = ((*pData & 0x3f) << 8 | *(pData + 1)); |
| return pData + 2; |
| } |
| |
| if ((*pData & 0xE0) == 0xC0) |
| { |
| *pLength = ((*pData & 0x1f) << 24 | *(pData + 1) << 16 | |
| *(pData + 2) << 8 | *(pData + 3)); |
| return pData + 4; |
| } |
| |
| *pLength = (ULONG)-1; |
| return 0; |
| } |
| |
| static VOID *assembly_data_offset(ASSEMBLY *assembly, ULONG offset) |
| { |
| return &assembly->data[offset]; |
| } |
| |
| #define MAX_TABLES_WORD 0xFFFF |
| #define MAX_TABLES_1BIT_ENCODE 32767 |
| #define MAX_TABLES_2BIT_ENCODE 16383 |
| #define MAX_TABLES_3BIT_ENCODE 8191 |
| #define MAX_TABLES_5BIT_ENCODE 2047 |
| |
| static inline ULONG get_table_size(const ASSEMBLY *assembly, DWORD index) |
| { |
| DWORD size; |
| INT tables; |
| |
| switch (TokenFromTable(index)) |
| { |
| case mdtModule: |
| { |
| size = sizeof(MODULETABLE) + (assembly->stringsz - sizeof(WORD)) + |
| 2 * (assembly->guidsz - sizeof(WORD)); |
| break; |
| } |
| case mdtTypeRef: |
| { |
| size = sizeof(TYPEREFTABLE) + 2 * (assembly->stringsz - sizeof(WORD)); |
| |
| /* ResolutionScope:ResolutionScope */ |
| tables = max(assembly->tables[TableFromToken(mdtModule)].rows, |
| assembly->tables[TableFromToken(mdtModuleRef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtAssemblyRef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtTypeRef)].rows); |
| size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0; |
| break; |
| } |
| case mdtTypeDef: |
| { |
| size = sizeof(TYPEDEFTABLE) + 2 * (assembly->stringsz - sizeof(WORD)); |
| |
| /* Extends:TypeDefOrRef */ |
| tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows, |
| assembly->tables[TableFromToken(mdtTypeRef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows); |
| size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0; |
| |
| size += (assembly->tables[TableFromToken(mdtFieldDef)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| size += (assembly->tables[TableFromToken(mdtMethodDef)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| break; |
| } |
| case mdtFieldDef: |
| { |
| size = sizeof(FIELDTABLE) + (assembly->stringsz - sizeof(WORD)) + |
| (assembly->blobsz - sizeof(WORD)); |
| break; |
| } |
| case mdtMethodDef: |
| { |
| size = sizeof(METHODDEFTABLE) + (assembly->stringsz - sizeof(WORD)) + |
| (assembly->blobsz - sizeof(WORD)); |
| |
| size += (assembly->tables[TableFromToken(mdtParamDef)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| break; |
| } |
| case mdtParamDef: |
| { |
| size = sizeof(PARAMTABLE) + (assembly->stringsz - sizeof(WORD)); |
| break; |
| } |
| case mdtInterfaceImpl: |
| { |
| size = sizeof(INTERFACEIMPLTABLE); |
| |
| /* Interface:TypeDefOrRef */ |
| tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows, |
| assembly->tables[TableFromToken(mdtTypeRef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows); |
| size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0; |
| break; |
| } |
| case mdtMemberRef: |
| { |
| size = sizeof(MEMBERREFTABLE) + (assembly->stringsz - sizeof(WORD)) + |
| (assembly->blobsz - sizeof(WORD)); |
| |
| /* Class:MemberRefParent */ |
| tables = max(assembly->tables[TableFromToken(mdtTypeRef)].rows, |
| assembly->tables[TableFromToken(mdtModuleRef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtMethodDef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtTypeDef)].rows); |
| size += (tables > MAX_TABLES_3BIT_ENCODE) ? sizeof(WORD) : 0; |
| break; |
| } |
| case 0x0B000000: /* FIXME */ |
| { |
| size = sizeof(CONSTANTTABLE) + (assembly->blobsz - sizeof(WORD)); |
| |
| /* Parent:HasConstant */ |
| tables = max(assembly->tables[TableFromToken(mdtParamDef)].rows, |
| assembly->tables[TableFromToken(mdtFieldDef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtProperty)].rows); |
| size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0; |
| break; |
| } |
| case mdtCustomAttribute: |
| { |
| size = sizeof(CUSTOMATTRIBUTETABLE) + (assembly->blobsz - sizeof(WORD)); |
| |
| /* Parent:HasCustomAttribute */ |
| tables = max(assembly->tables[TableFromToken(mdtMethodDef)].rows, |
| assembly->tables[TableFromToken(mdtFieldDef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtTypeRef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtTypeDef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtParamDef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtInterfaceImpl)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtMemberRef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtPermission)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtProperty)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtEvent)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtSignature)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtModuleRef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtAssembly)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtFile)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtExportedType)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtManifestResource)].rows); |
| size += (tables > MAX_TABLES_5BIT_ENCODE) ? sizeof(WORD) : 0; |
| |
| /* Type:CustomAttributeType */ |
| tables = max(assembly->tables[TableFromToken(mdtMethodDef)].rows, |
| assembly->tables[TableFromToken(mdtMemberRef)].rows); |
| size += (tables > MAX_TABLES_3BIT_ENCODE) ? sizeof(WORD) : 0; |
| break; |
| } |
| case 0x0D000000: /* FIXME */ |
| { |
| size = sizeof(FIELDMARSHALTABLE) + (assembly->blobsz - sizeof(WORD)); |
| |
| /* Parent:HasFieldMarshal */ |
| tables = max(assembly->tables[TableFromToken(mdtFieldDef)].rows, |
| assembly->tables[TableFromToken(mdtParamDef)].rows); |
| size += (tables > MAX_TABLES_1BIT_ENCODE) ? sizeof(WORD) : 0; |
| break; |
| } |
| case mdtPermission: |
| { |
| size = sizeof(DECLSECURITYTABLE) + (assembly->blobsz - sizeof(WORD)); |
| |
| /* Parent:HasDeclSecurity */ |
| tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows, |
| assembly->tables[TableFromToken(mdtMethodDef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtAssembly)].rows); |
| size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0; |
| break; |
| } |
| case 0x0F000000: /* FIXME */ |
| { |
| size = sizeof(CLASSLAYOUTTABLE); |
| size += (assembly->tables[TableFromToken(mdtTypeDef)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| break; |
| } |
| case 0x10000000: /* FIXME */ |
| { |
| size = sizeof(FIELDLAYOUTTABLE); |
| size += (assembly->tables[TableFromToken(mdtFieldDef)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| break; |
| } |
| case mdtSignature: |
| { |
| size = sizeof(STANDALONESIGTABLE) + (assembly->blobsz - sizeof(WORD)); |
| break; |
| } |
| case 0x12000000: /* FIXME */ |
| { |
| size = sizeof(EVENTMAPTABLE); |
| size += (assembly->tables[TableFromToken(mdtTypeDef)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| size += (assembly->tables[TableFromToken(mdtEvent)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| break; |
| } |
| case mdtEvent: |
| { |
| size = sizeof(EVENTTABLE) + (assembly->stringsz - sizeof(WORD)); |
| |
| /* EventType:TypeDefOrRef */ |
| tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows, |
| assembly->tables[TableFromToken(mdtTypeRef)].rows); |
| tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows); |
| size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0; |
| break; |
| } |
| case 0x15000000:/* FIXME */ |
| { |
| size = sizeof(PROPERTYMAPTABLE); |
| size += (assembly->tables[TableFromToken(mdtTypeDef)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| size += (assembly->tables[TableFromToken(mdtProperty)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| break; |
| } |
| case mdtProperty: |
| { |
| size = sizeof(PROPERTYTABLE) + (assembly->stringsz - sizeof(WORD)) + |
| (assembly->blobsz - sizeof(WORD)); |
| break; |
| } |
| case 0x18000000: /* FIXME */ |
| { |
| size = sizeof(METHODSEMANTICSTABLE); |
| |
| /* Association:HasSemantics */ |
| tables = max(assembly->tables[TableFromToken(mdtEvent)].rows, |
| assembly->tables[TableFromToken(mdtProperty)].rows); |
| size += (tables > MAX_TABLES_1BIT_ENCODE) ? sizeof(WORD) : 0; |
| |
| size += (assembly->tables[TableFromToken(mdtMethodDef)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| break; |
| } |
| case 0x19000000: /* FIXME */ |
| { |
| size = sizeof(METHODIMPLTABLE); |
| |
| /* MethodBody:MethodDefOrRef, MethodDeclaration:MethodDefOrRef */ |
| tables = max(assembly->tables[TableFromToken(mdtMethodDef)].rows, |
| assembly->tables[TableFromToken(mdtMemberRef)].rows); |
| size += (tables > MAX_TABLES_1BIT_ENCODE) ? 2 * sizeof(WORD) : 0; |
| |
| size += (assembly->tables[TableFromToken(mdtTypeDef)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| break; |
| } |
| case mdtModuleRef: |
| { |
| size = sizeof(MODULEREFTABLE) + (assembly->stringsz - sizeof(WORD)); |
| break; |
| } |
| case mdtTypeSpec: |
| { |
| size = sizeof(TYPESPECTABLE) + (assembly->blobsz - sizeof(WORD)); |
| break; |
| } |
| case 0x1C000000: /* FIXME */ |
| { |
| size = sizeof(IMPLMAPTABLE) + (assembly->stringsz - sizeof(WORD)); |
| |
| /* MemberForwarded:MemberForwarded */ |
| tables = max(assembly->tables[TableFromToken(mdtFieldDef)].rows, |
| assembly->tables[TableFromToken(mdtMethodDef)].rows); |
| size += (tables > MAX_TABLES_1BIT_ENCODE) ? sizeof(WORD) : 0; |
| |
| size += (assembly->tables[TableFromToken(mdtModuleRef)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| break; |
| } |
| case 0x1D000000: /* FIXME */ |
| { |
| size = sizeof(FIELDRVATABLE); |
| size += (assembly->tables[TableFromToken(mdtFieldDef)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| break; |
| } |
| case mdtAssembly: |
| { |
| size = sizeof(ASSEMBLYTABLE) + 2 * (assembly->stringsz - sizeof(WORD)) + |
| (assembly->blobsz - sizeof(WORD)); |
| break; |
| } |
| case 0x20000001: /* FIXME */ |
| { |
| size = sizeof(ASSEMBLYPROCESSORTABLE); |
| break; |
| } |
| case 0x22000000: /* FIXME */ |
| { |
| size = sizeof(ASSEMBLYOSTABLE); |
| break; |
| } |
| case mdtAssemblyRef: |
| { |
| size = sizeof(ASSEMBLYREFTABLE) + 2 * (assembly->stringsz - sizeof(WORD)) + |
| 2 * (assembly->blobsz - sizeof(WORD)); |
| break; |
| } |
| case 0x24000000: /* FIXME */ |
| { |
| size = sizeof(ASSEMBLYREFPROCESSORTABLE); |
| size += (assembly->tables[TableFromToken(mdtAssemblyRef)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| break; |
| } |
| case 0x25000000: /* FIXME */ |
| { |
| size = sizeof(ASSEMBLYREFOSTABLE); |
| size += (assembly->tables[TableFromToken(mdtAssemblyRef)].rows > |
| MAX_TABLES_WORD) ? sizeof(WORD) : 0; |
| break; |
| } |
| case mdtFile: |
| { |
| size = sizeof(FILETABLE) + (assembly->stringsz - sizeof(WORD)) + |
| (assembly->blobsz - sizeof(WORD)); |
| break; |
| } |
| case mdtExportedType: |
| { |
| size = sizeof(EXPORTEDTYPETABLE) + 2 * (assembly->stringsz - sizeof(WORD)); |
| |
| /* Implementation:Implementation */ |
| tables = max(assembly->tables[TableFromToken(mdtFile)].rows, |
| assembly->tables[TableFromToken(mdtMethodDef)].rows); |
| size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0; |
| break; |
| } |
| case mdtManifestResource: |
| { |
| size = sizeof(MANIFESTRESTABLE) + (assembly->stringsz - sizeof(WORD)); |
| |
| /* Implementation:Implementation */ |
| tables = max(assembly->tables[TableFromToken(mdtFile)].rows, |
| assembly->tables[TableFromToken(mdtAssemblyRef)].rows); |
| size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0; |
| break; |
| } |
| case 0x29000000: /* FIXME */ |
| { |
| size = sizeof(NESTEDCLASSTABLE); |
| size += (assembly->tables[TableFromToken(mdtTypeDef)].rows > |
| MAX_TABLES_WORD) ? 2 * sizeof(WORD) : 0; |
| break; |
| } |
| default: |
| return 0; |
| } |
| |
| return size; |
| } |
| |
| static HRESULT parse_clr_tables(ASSEMBLY *assembly, ULONG offset) |
| { |
| DWORD i, count; |
| ULONG currofs; |
| ULONGLONG mask; |
| |
| currofs = offset; |
| assembly->tableshdr = assembly_data_offset(assembly, currofs); |
| if (!assembly->tableshdr) |
| return E_FAIL; |
| |
| assembly->stringsz = (assembly->tableshdr->HeapOffsetSizes & MD_STRINGS_BIT) ? |
| sizeof(DWORD) : sizeof(WORD); |
| assembly->guidsz = (assembly->tableshdr->HeapOffsetSizes & MD_GUIDS_BIT) ? |
| sizeof(DWORD) : sizeof(WORD); |
| assembly->blobsz = (assembly->tableshdr->HeapOffsetSizes & MD_BLOBS_BIT) ? |
| sizeof(DWORD) : sizeof(WORD); |
| |
| currofs += sizeof(METADATATABLESHDR); |
| assembly->numrows = assembly_data_offset(assembly, currofs); |
| if (!assembly->numrows) |
| return E_FAIL; |
| |
| memset(assembly->tables, -1, MAX_CLR_TABLES * sizeof(CLRTABLE)); |
| |
| for (i = count = 0, mask = 1; i < MAX_CLR_TABLES; i++, mask <<= 1) |
| { |
| if (assembly->tableshdr->MaskValid.QuadPart & mask) |
| assembly->tables[i].rows = assembly->numrows[count++]; |
| } |
| assembly->numtables = count; |
| currofs += assembly->numtables * sizeof(DWORD); |
| |
| for (i = 0, mask = 1; i < MAX_CLR_TABLES; i++, mask <<= 1) |
| { |
| if (assembly->tableshdr->MaskValid.QuadPart & mask) |
| { |
| assembly->tables[i].offset = currofs; |
| currofs += get_table_size(assembly, i) * assembly->tables[i].rows; |
| } |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT parse_metadata_header(ASSEMBLY *assembly, DWORD *hdrsz) |
| { |
| METADATAHDR *metadatahdr; |
| BYTE *ptr, *dest; |
| DWORD size, ofs; |
| ULONG rva; |
| |
| rva = assembly->corhdr->MetaData.VirtualAddress; |
| ptr = ImageRvaToVa(assembly->nthdr, assembly->data, rva, NULL); |
| if (!ptr) |
| return E_FAIL; |
| |
| metadatahdr = (METADATAHDR *)ptr; |
| |
| assembly->metadatahdr = HeapAlloc(GetProcessHeap(), 0, sizeof(METADATAHDR)); |
| if (!assembly->metadatahdr) |
| return E_OUTOFMEMORY; |
| |
| size = FIELD_OFFSET(METADATAHDR, Version); |
| memcpy(assembly->metadatahdr, metadatahdr, size); |
| |
| assembly->metadatahdr->Version = (LPSTR)&metadatahdr->Version; |
| |
| ofs = FIELD_OFFSET(METADATAHDR, Flags); |
| ptr += FIELD_OFFSET(METADATAHDR, Version) + metadatahdr->VersionLength + 1; |
| dest = (BYTE *)assembly->metadatahdr + ofs; |
| memcpy(dest, ptr, sizeof(METADATAHDR) - ofs); |
| |
| *hdrsz = sizeof(METADATAHDR) - sizeof(LPSTR) + metadatahdr->VersionLength + 1; |
| |
| return S_OK; |
| } |
| |
| static HRESULT parse_clr_metadata(ASSEMBLY *assembly) |
| { |
| METADATASTREAMHDR *streamhdr; |
| ULONG rva, i, ofs; |
| LPSTR stream; |
| HRESULT hr; |
| DWORD hdrsz; |
| BYTE *ptr; |
| |
| hr = parse_metadata_header(assembly, &hdrsz); |
| if (FAILED(hr)) |
| return hr; |
| |
| rva = assembly->corhdr->MetaData.VirtualAddress; |
| ptr = ImageRvaToVa(assembly->nthdr, assembly->data, rva + hdrsz, NULL); |
| if (!ptr) |
| return E_FAIL; |
| |
| for (i = 0; i < assembly->metadatahdr->Streams; i++) |
| { |
| streamhdr = (METADATASTREAMHDR *)ptr; |
| ofs = rva_to_offset(assembly->nthdr, rva + streamhdr->Offset); |
| |
| ptr += sizeof(METADATASTREAMHDR); |
| stream = (LPSTR)ptr; |
| |
| if (!lstrcmpA(stream, "#~")) |
| { |
| hr = parse_clr_tables(assembly, ofs); |
| if (FAILED(hr)) |
| return hr; |
| } |
| else if (!lstrcmpA(stream, "#Strings") || !lstrcmpA(stream, "Strings")) |
| assembly->strings = assembly_data_offset(assembly, ofs); |
| else if (!lstrcmpA(stream, "#Blob") || !lstrcmpA(stream, "Blob")) |
| assembly->blobs = assembly_data_offset(assembly, ofs); |
| |
| ptr += ((lstrlenA(stream) + 1) + 3) & ~3; /* align on DWORD boundary */ |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT parse_pe_header(ASSEMBLY *assembly) |
| { |
| IMAGE_DATA_DIRECTORY *datadirs; |
| |
| assembly->nthdr = ImageNtHeader(assembly->data); |
| if (!assembly->nthdr) |
| return E_FAIL; |
| |
| if (assembly->nthdr->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) |
| { |
| IMAGE_OPTIONAL_HEADER64 *opthdr = |
| (IMAGE_OPTIONAL_HEADER64 *)&assembly->nthdr->OptionalHeader; |
| datadirs = opthdr->DataDirectory; |
| } |
| else |
| { |
| IMAGE_OPTIONAL_HEADER32 *opthdr = |
| (IMAGE_OPTIONAL_HEADER32 *)&assembly->nthdr->OptionalHeader; |
| datadirs = opthdr->DataDirectory; |
| } |
| |
| if (!datadirs) |
| return E_FAIL; |
| |
| if (!datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress || |
| !datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size) |
| { |
| return E_FAIL; |
| } |
| |
| assembly->corhdr = ImageRvaToVa(assembly->nthdr, assembly->data, |
| datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress, NULL); |
| if (!assembly->corhdr) |
| return E_FAIL; |
| |
| return S_OK; |
| } |
| |
| HRESULT assembly_create(ASSEMBLY **out, LPCWSTR file) |
| { |
| ASSEMBLY *assembly; |
| HRESULT hr; |
| |
| *out = NULL; |
| |
| assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY)); |
| if (!assembly) |
| return E_OUTOFMEMORY; |
| |
| assembly->path = strdupW(file); |
| if (!assembly->path) |
| { |
| hr = E_OUTOFMEMORY; |
| goto failed; |
| } |
| |
| assembly->hfile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, |
| NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
| if (assembly->hfile == INVALID_HANDLE_VALUE) |
| { |
| hr = HRESULT_FROM_WIN32(GetLastError()); |
| goto failed; |
| } |
| |
| assembly->hmap = CreateFileMappingW(assembly->hfile, NULL, PAGE_READONLY, |
| 0, 0, NULL); |
| if (!assembly->hmap) |
| { |
| hr = HRESULT_FROM_WIN32(GetLastError()); |
| goto failed; |
| } |
| |
| assembly->data = MapViewOfFile(assembly->hmap, FILE_MAP_READ, 0, 0, 0); |
| if (!assembly->data) |
| { |
| hr = HRESULT_FROM_WIN32(GetLastError()); |
| goto failed; |
| } |
| |
| hr = parse_pe_header(assembly); |
| if (FAILED(hr)) goto failed; |
| |
| hr = parse_clr_metadata(assembly); |
| if (FAILED(hr)) goto failed; |
| |
| *out = assembly; |
| return S_OK; |
| |
| failed: |
| assembly_release(assembly); |
| return hr; |
| } |
| |
| HRESULT assembly_release(ASSEMBLY *assembly) |
| { |
| if (!assembly) |
| return S_OK; |
| |
| HeapFree(GetProcessHeap(), 0, assembly->metadatahdr); |
| HeapFree(GetProcessHeap(), 0, assembly->path); |
| UnmapViewOfFile(assembly->data); |
| CloseHandle(assembly->hmap); |
| CloseHandle(assembly->hfile); |
| HeapFree(GetProcessHeap(), 0, assembly); |
| |
| return S_OK; |
| } |
| |
| static LPWSTR assembly_dup_str(const ASSEMBLY *assembly, DWORD index) |
| { |
| int len; |
| LPWSTR cpy; |
| LPCSTR str = (LPCSTR)&assembly->strings[index]; |
| |
| len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); |
| |
| if ((cpy = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)))) |
| MultiByteToWideChar(CP_ACP, 0, str, -1, cpy, len); |
| |
| return cpy; |
| } |
| |
| HRESULT assembly_get_name(ASSEMBLY *assembly, LPWSTR *name) |
| { |
| BYTE *ptr; |
| LONG offset; |
| DWORD stridx; |
| |
| offset = assembly->tables[TableFromToken(mdtAssembly)].offset; |
| if (offset == -1) |
| return E_FAIL; |
| |
| ptr = assembly_data_offset(assembly, offset); |
| if (!ptr) |
| return E_FAIL; |
| |
| ptr += FIELD_OFFSET(ASSEMBLYTABLE, PublicKey) + assembly->blobsz; |
| if (assembly->stringsz == sizeof(DWORD)) |
| stridx = *(DWORD *)ptr; |
| else |
| stridx = *(WORD *)ptr; |
| |
| *name = assembly_dup_str(assembly, stridx); |
| if (!*name) |
| return E_OUTOFMEMORY; |
| |
| return S_OK; |
| } |
| |
| HRESULT assembly_get_path(const ASSEMBLY *assembly, LPWSTR *path) |
| { |
| LPWSTR cpy = HeapAlloc(GetProcessHeap(), 0, (strlenW(assembly->path) + 1) * sizeof(WCHAR)); |
| *path = cpy; |
| if (cpy) |
| strcpyW(cpy, assembly->path); |
| else |
| return E_OUTOFMEMORY; |
| |
| return S_OK; |
| } |
| |
| HRESULT assembly_get_version(ASSEMBLY *assembly, LPWSTR *version) |
| { |
| static const WCHAR format[] = {'%','u','.','%','u','.','%','u','.','%','u',0}; |
| |
| ASSEMBLYTABLE *asmtbl; |
| LONG offset; |
| |
| *version = NULL; |
| |
| offset = assembly->tables[TableFromToken(mdtAssembly)].offset; |
| if (offset == -1) |
| return E_FAIL; |
| |
| asmtbl = assembly_data_offset(assembly, offset); |
| if (!asmtbl) |
| return E_FAIL; |
| |
| *version = HeapAlloc(GetProcessHeap(), 0, sizeof(format) + 4 * strlen("65535") * sizeof(WCHAR)); |
| if (!*version) |
| return E_OUTOFMEMORY; |
| |
| sprintfW(*version, format, asmtbl->MajorVersion, asmtbl->MinorVersion, |
| asmtbl->BuildNumber, asmtbl->RevisionNumber); |
| |
| return S_OK; |
| } |
| |
| PEKIND assembly_get_architecture(ASSEMBLY *assembly) |
| { |
| if ((assembly->corhdr->MajorRuntimeVersion == 2) && (assembly->corhdr->MinorRuntimeVersion == 0)) |
| return peNone; /* .NET 1.x assembly */ |
| |
| if (assembly->nthdr->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) |
| return peAMD64; /* AMD64/IA64 assembly */ |
| |
| if ((assembly->corhdr->Flags & COMIMAGE_FLAGS_ILONLY) && !(assembly->corhdr->Flags & COMIMAGE_FLAGS_32BITREQUIRED)) |
| return peMSIL; /* MSIL assembly */ |
| |
| return peI386; /* x86 assembly */ |
| } |
| |
| static BYTE *assembly_get_blob(ASSEMBLY *assembly, DWORD index, ULONG *size) |
| { |
| return GetData(&assembly->blobs[index], size); |
| } |
| |
| HRESULT assembly_get_pubkey_token(ASSEMBLY *assembly, LPWSTR *token) |
| { |
| ULONG i, size; |
| LONG offset; |
| BYTE *hashdata, *pubkey, *ptr; |
| HCRYPTPROV crypt; |
| HCRYPTHASH hash; |
| BYTE tokbytes[BYTES_PER_TOKEN]; |
| HRESULT hr = E_FAIL; |
| LPWSTR tok; |
| DWORD idx; |
| |
| *token = NULL; |
| |
| offset = assembly->tables[TableFromToken(mdtAssembly)].offset; |
| if (offset == -1) |
| return E_FAIL; |
| |
| ptr = assembly_data_offset(assembly, offset); |
| if (!ptr) |
| return E_FAIL; |
| |
| ptr += FIELD_OFFSET(ASSEMBLYTABLE, PublicKey); |
| if (assembly->blobsz == sizeof(DWORD)) |
| idx = *(DWORD *)ptr; |
| else |
| idx = *(WORD *)ptr; |
| |
| pubkey = assembly_get_blob(assembly, idx, &size); |
| |
| if (!CryptAcquireContextA(&crypt, NULL, NULL, PROV_RSA_FULL, |
| CRYPT_VERIFYCONTEXT)) |
| return E_FAIL; |
| |
| if (!CryptCreateHash(crypt, CALG_SHA1, 0, 0, &hash)) |
| return E_FAIL; |
| |
| if (!CryptHashData(hash, pubkey, size, 0)) |
| return E_FAIL; |
| |
| size = 0; |
| if (!CryptGetHashParam(hash, HP_HASHVAL, NULL, &size, 0)) |
| return E_FAIL; |
| |
| hashdata = HeapAlloc(GetProcessHeap(), 0, size); |
| if (!hashdata) |
| { |
| hr = E_OUTOFMEMORY; |
| goto done; |
| } |
| |
| if (!CryptGetHashParam(hash, HP_HASHVAL, hashdata, &size, 0)) |
| goto done; |
| |
| for (i = size - 1; i >= size - 8; i--) |
| tokbytes[size - i - 1] = hashdata[i]; |
| |
| tok = HeapAlloc(GetProcessHeap(), 0, (TOKEN_LENGTH + 1) * sizeof(WCHAR)); |
| if (!tok) |
| { |
| hr = E_OUTOFMEMORY; |
| goto done; |
| } |
| |
| token_to_str(tokbytes, tok); |
| |
| *token = tok; |
| hr = S_OK; |
| |
| done: |
| HeapFree(GetProcessHeap(), 0, hashdata); |
| CryptDestroyHash(hash); |
| CryptReleaseContext(crypt, 0); |
| |
| return hr; |
| } |
| |
| HRESULT assembly_get_runtime_version(ASSEMBLY *assembly, LPSTR *version) |
| { |
| *version = assembly->metadatahdr->Version; |
| return S_OK; |
| } |
| |
| HRESULT assembly_get_external_files(ASSEMBLY *assembly, LPWSTR **files, DWORD *count) |
| { |
| LONG offset; |
| INT i, num_rows; |
| WCHAR **ret; |
| BYTE *ptr; |
| DWORD idx; |
| |
| *count = 0; |
| |
| offset = assembly->tables[TableFromToken(mdtFile)].offset; |
| if (offset == -1) |
| return S_OK; |
| |
| ptr = assembly_data_offset(assembly, offset); |
| if (!ptr) |
| return S_OK; |
| |
| num_rows = assembly->tables[TableFromToken(mdtFile)].rows; |
| if (num_rows <= 0) |
| return S_OK; |
| |
| ret = HeapAlloc(GetProcessHeap(), 0, num_rows * sizeof(WCHAR *)); |
| if (!ret) |
| return E_OUTOFMEMORY; |
| |
| for (i = 0; i < num_rows; i++) |
| { |
| ptr += sizeof(DWORD); /* skip Flags field */ |
| if (assembly->stringsz == sizeof(DWORD)) |
| idx = *(DWORD *)ptr; |
| else |
| idx = *(WORD *)ptr; |
| |
| ret[i] = assembly_dup_str(assembly, idx); |
| if (!ret[i]) |
| { |
| for (; i >= 0; i--) HeapFree(GetProcessHeap(), 0, ret[i]); |
| HeapFree(GetProcessHeap(), 0, ret); |
| return E_OUTOFMEMORY; |
| } |
| ptr += assembly->stringsz; /* skip Name field */ |
| ptr += assembly->blobsz; /* skip Hash field */ |
| } |
| *count = num_rows; |
| *files = ret; |
| return S_OK; |
| } |