1 /* 2 * PROJECT: ReactOS Application compatibility module 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Query file attributes used to match exe's 5 * COPYRIGHT: Copyright 2011 André Hentschel 6 * Copyright 2013 Mislav Blaževic 7 * Copyright 2015-2017 Mark Jansen (mark.jansen@reactos.org) 8 */ 9 10 #define WIN32_NO_STATUS 11 #include "windef.h" 12 #include "apphelp.h" 13 #include "strsafe.h" 14 #include "winver.h" 15 #include "rtlfuncs.h" 16 17 18 #define NUM_ATTRIBUTES 28 19 enum APPHELP_MODULETYPE 20 { 21 MODTYPE_UNKNOWN = 0, 22 MODTYPE_DOS = 1, 23 MODTYPE_NE = 2, 24 MODTYPE_PE = 3, 25 }; 26 27 28 static void WINAPI SdbpSetDWORDAttr(PATTRINFO attr, TAG tag, DWORD value) 29 { 30 attr->type = tag; 31 attr->flags = ATTRIBUTE_AVAILABLE; 32 attr->dwattr = value; 33 } 34 35 static void WINAPI SdbpSetQWORDAttr(PATTRINFO attr, TAG tag, QWORD value) 36 { 37 attr->type = tag; 38 attr->flags = ATTRIBUTE_AVAILABLE; 39 attr->qwattr = value; 40 } 41 42 static void WINAPI SdbpSetStringAttr(PATTRINFO attr, TAG tag, WCHAR *string) 43 { 44 if (!string) 45 { 46 attr->flags = ATTRIBUTE_FAILED; 47 return; 48 } 49 50 attr->type = tag; 51 attr->flags = ATTRIBUTE_AVAILABLE; 52 attr->lpattr = SdbpStrDup(string); 53 } 54 55 static void WINAPI SdbpSetAttrFail(PATTRINFO attr) 56 { 57 attr->flags = ATTRIBUTE_FAILED; 58 } 59 60 static WCHAR* WINAPI SdbpGetStringAttr(LPWSTR translation, LPCWSTR attr, PVOID file_info) 61 { 62 UINT size = 0; 63 PVOID buffer; 64 WCHAR value[128] = {0}; 65 66 if (!file_info) 67 return NULL; 68 69 StringCchPrintfW(value, ARRAYSIZE(value), translation, attr); 70 if (VerQueryValueW(file_info, value, &buffer, &size) && size != 0) 71 return (WCHAR*)buffer; 72 73 return NULL; 74 } 75 76 static void WINAPI SdbpSetStringAttrFromAnsiString(PATTRINFO attr, TAG tag, PBYTE string, size_t len) 77 { 78 WCHAR* dest; 79 if (!string) 80 { 81 attr->flags = ATTRIBUTE_FAILED; 82 return; 83 } 84 85 attr->type = tag; 86 attr->flags = ATTRIBUTE_AVAILABLE; 87 dest = attr->lpattr = SdbAlloc((len+1) * sizeof(WCHAR)); 88 while (len--) 89 *(dest++) = *(string++); 90 *dest = 0; 91 } 92 93 static void WINAPI SdbpSetStringAttrFromPascalString(PATTRINFO attr, TAG tag, PBYTE string) 94 { 95 if (!string) 96 { 97 attr->flags = ATTRIBUTE_FAILED; 98 return; 99 } 100 101 SdbpSetStringAttrFromAnsiString(attr, tag, string + 1, *string); 102 } 103 104 static void SdbpReadFileVersion(PATTRINFO attr_info, PVOID file_info) 105 { 106 static const WCHAR str_root[] = {'\\',0}; 107 108 VS_FIXEDFILEINFO* fixed_info; 109 UINT size; 110 if (file_info && VerQueryValueW(file_info, str_root, (LPVOID*)&fixed_info, &size) && size) 111 { 112 if (fixed_info->dwSignature == VS_FFI_SIGNATURE) 113 { 114 LARGE_INTEGER version; 115 version.HighPart = fixed_info->dwFileVersionMS; 116 version.LowPart = fixed_info->dwFileVersionLS; 117 SdbpSetQWORDAttr(&attr_info[2], TAG_BIN_FILE_VERSION, version.QuadPart); 118 SdbpSetQWORDAttr(&attr_info[21], TAG_UPTO_BIN_FILE_VERSION, version.QuadPart); 119 version.HighPart = fixed_info->dwProductVersionMS; 120 version.LowPart = fixed_info->dwProductVersionLS; 121 SdbpSetQWORDAttr(&attr_info[3], TAG_BIN_PRODUCT_VERSION, version.QuadPart); 122 SdbpSetQWORDAttr(&attr_info[22], TAG_UPTO_BIN_PRODUCT_VERSION, version.QuadPart); 123 124 SdbpSetDWORDAttr(&attr_info[12], TAG_VERDATEHI, fixed_info->dwFileDateMS); 125 SdbpSetDWORDAttr(&attr_info[13], TAG_VERDATELO, fixed_info->dwFileDateLS); 126 SdbpSetDWORDAttr(&attr_info[14], TAG_VERFILEOS, fixed_info->dwFileOS); /* 0x000, 0x4, 0x40004, 0x40000, 0x10004, 0x10001*/ 127 SdbpSetDWORDAttr(&attr_info[15], TAG_VERFILETYPE, fixed_info->dwFileType); /* VFT_APP, VFT_DLL, .... */ 128 return; 129 } 130 } 131 132 SdbpSetAttrFail(&attr_info[2]); 133 SdbpSetAttrFail(&attr_info[3]); 134 SdbpSetAttrFail(&attr_info[12]); 135 SdbpSetAttrFail(&attr_info[13]); 136 SdbpSetAttrFail(&attr_info[14]); 137 SdbpSetAttrFail(&attr_info[15]); 138 SdbpSetAttrFail(&attr_info[21]); 139 SdbpSetAttrFail(&attr_info[22]); 140 } 141 142 static DWORD WINAPI SdbpCalculateFileChecksum(PMEMMAPPED mapping) 143 { 144 size_t n, size; 145 PDWORD data; 146 DWORD checks = 0, carry = 0; 147 148 if (mapping->size < 4) 149 return 0; 150 151 if (mapping->size >= 0x1000) 152 { 153 size = 0x1000; 154 if (mapping->size < 0x1200) 155 data = (PDWORD)(mapping->view + mapping->size - size); 156 else 157 data = (PDWORD)mapping->view + (0x200 / 4); 158 } 159 else 160 { 161 data = (PDWORD)mapping->view; 162 size = mapping->size; 163 } 164 165 for (n = 0; n < size / 4; ++n) 166 { 167 checks += *data; 168 carry = (checks & 1) ? 0x80000000 : 0; 169 checks >>= 1; 170 checks |= carry; 171 ++data; 172 } 173 return checks; 174 } 175 176 static DWORD WINAPI SdbpGetModuleType(PMEMMAPPED mapping, PIMAGE_NT_HEADERS* nt_headers) 177 { 178 PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)mapping->view; 179 PIMAGE_OS2_HEADER os2; 180 181 *nt_headers = NULL; 182 183 if (mapping->size < 2 || dos->e_magic != IMAGE_DOS_SIGNATURE) 184 return MODTYPE_UNKNOWN; 185 186 if (mapping->size < sizeof(IMAGE_DOS_HEADER) || mapping->size < (dos->e_lfanew+2)) 187 return MODTYPE_DOS; 188 189 os2 = (PIMAGE_OS2_HEADER)((PBYTE)dos + dos->e_lfanew); 190 if (os2->ne_magic == IMAGE_OS2_SIGNATURE || os2->ne_magic == IMAGE_OS2_SIGNATURE_LE) 191 { 192 *nt_headers = (PIMAGE_NT_HEADERS)os2; 193 return MODTYPE_NE; 194 } 195 196 if (mapping->size >= (dos->e_lfanew + 4) && ((PIMAGE_NT_HEADERS)os2)->Signature == IMAGE_NT_SIGNATURE) 197 { 198 *nt_headers = (PIMAGE_NT_HEADERS)os2; 199 return MODTYPE_PE; 200 } 201 202 return MODTYPE_DOS; 203 } 204 205 /** 206 * Frees attribute data allocated by SdbGetFileAttributes. 207 * 208 * @note Unlike Windows, this implementation will not crash if attr_info is NULL. 209 * 210 * @param [in] attr_info Pointer to array of ATTRINFO which will be freed. 211 * 212 * @return TRUE if it succeeds, FALSE if it fails. 213 */ 214 BOOL WINAPI SdbFreeFileAttributes(PATTRINFO attr_info) 215 { 216 WORD i; 217 218 if (!attr_info) 219 return FALSE; 220 221 for (i = 0; i < NUM_ATTRIBUTES; i++) 222 if ((attr_info[i].type & TAG_TYPE_MASK) == TAG_TYPE_STRINGREF) 223 SdbFree(attr_info[i].lpattr); 224 SdbFree(attr_info); 225 return TRUE; 226 } 227 228 /** 229 * Retrieves attribute data shim database requires to match a file with database entry 230 * 231 * @note You must free the attr_info allocated by this function by calling SdbFreeFileAttributes. 232 * 233 * @param [in] path Path to the file. 234 * @param [out] attr_info_ret Pointer to array of ATTRINFO. Contains attribute data. 235 * @param [out] attr_count Number of attributes in attr_info. 236 * 237 * @return TRUE if it succeeds, FALSE if it fails. 238 */ 239 BOOL WINAPI SdbGetFileAttributes(LPCWSTR path, PATTRINFO *attr_info_ret, LPDWORD attr_count) 240 { 241 static const WCHAR str_tinfo[] = {'\\','V','a','r','F','i','l','e','I','n','f','o','\\','T','r','a','n','s','l','a','t','i','o','n',0}; 242 static const WCHAR str_trans[] = {'\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o','\\','%','0','4','x','%','0','4','x','\\','%','%','s',0}; 243 static const WCHAR str_CompanyName[] = {'C','o','m','p','a','n','y','N','a','m','e',0}; 244 static const WCHAR str_FileDescription[] = {'F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0}; 245 static const WCHAR str_FileVersion[] = {'F','i','l','e','V','e','r','s','i','o','n',0}; 246 static const WCHAR str_InternalName[] = {'I','n','t','e','r','n','a','l','N','a','m','e',0}; 247 static const WCHAR str_LegalCopyright[] = {'L','e','g','a','l','C','o','p','y','r','i','g','h','t',0}; 248 static const WCHAR str_OriginalFilename[] = {'O','r','i','g','i','n','a','l','F','i','l','e','n','a','m','e',0}; 249 static const WCHAR str_ProductName[] = {'P','r','o','d','u','c','t','N','a','m','e',0}; 250 static const WCHAR str_ProductVersion[] = {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0}; 251 252 PIMAGE_NT_HEADERS headers; 253 MEMMAPPED mapped; 254 PBYTE mapping_end; 255 PVOID file_info = 0; 256 DWORD module_type; 257 WCHAR translation[128] = {0}; 258 PATTRINFO attr_info; 259 260 struct LANGANDCODEPAGE { 261 WORD language; 262 WORD code_page; 263 } *lang_page; 264 265 if (!SdbpOpenMemMappedFile(path, &mapped)) 266 { 267 SHIM_ERR("Error retrieving FILEINFO structure\n"); 268 return FALSE; 269 } 270 mapping_end = mapped.view + mapped.size; 271 272 attr_info = (PATTRINFO)SdbAlloc(NUM_ATTRIBUTES * sizeof(ATTRINFO)); 273 274 SdbpSetDWORDAttr(&attr_info[0], TAG_SIZE, mapped.size); 275 if (mapped.size) 276 SdbpSetDWORDAttr(&attr_info[1], TAG_CHECKSUM, SdbpCalculateFileChecksum(&mapped)); 277 else 278 SdbpSetAttrFail(&attr_info[1]); 279 module_type = SdbpGetModuleType(&mapped, &headers); 280 281 if (module_type != MODTYPE_UNKNOWN) 282 SdbpSetDWORDAttr(&attr_info[16], TAG_MODULE_TYPE, module_type); 283 else 284 SdbpSetAttrFail(&attr_info[16]); /* TAG_MODULE_TYPE */ 285 286 if (headers && module_type == MODTYPE_PE && ((PBYTE)(headers+1) <= mapping_end)) 287 { 288 DWORD info_size; 289 SIZE_T export_dir_size; 290 PIMAGE_EXPORT_DIRECTORY export_dir; 291 292 info_size = GetFileVersionInfoSizeW(path, NULL); 293 if (info_size != 0) 294 { 295 UINT page_size = 0; 296 file_info = SdbAlloc(info_size); 297 GetFileVersionInfoW(path, 0, info_size, file_info); 298 VerQueryValueW(file_info, str_tinfo, (LPVOID)&lang_page, &page_size); 299 StringCchPrintfW(translation, ARRAYSIZE(translation), str_trans, lang_page->language, lang_page->code_page); 300 } 301 302 /* Handles 2, 3, 12, 13, 14, 15, 21, 22 */ 303 SdbpReadFileVersion(attr_info, file_info); 304 305 SdbpSetStringAttr(&attr_info[4], TAG_PRODUCT_VERSION, SdbpGetStringAttr(translation, str_ProductVersion, file_info)); 306 SdbpSetStringAttr(&attr_info[5], TAG_FILE_DESCRIPTION, SdbpGetStringAttr(translation, str_FileDescription, file_info)); 307 SdbpSetStringAttr(&attr_info[6], TAG_COMPANY_NAME, SdbpGetStringAttr(translation, str_CompanyName, file_info)); 308 SdbpSetStringAttr(&attr_info[7], TAG_PRODUCT_NAME, SdbpGetStringAttr(translation, str_ProductName, file_info)); 309 SdbpSetStringAttr(&attr_info[8], TAG_FILE_VERSION, SdbpGetStringAttr(translation, str_FileVersion, file_info)); 310 SdbpSetStringAttr(&attr_info[9], TAG_ORIGINAL_FILENAME, SdbpGetStringAttr(translation, str_OriginalFilename, file_info)); 311 SdbpSetStringAttr(&attr_info[10], TAG_INTERNAL_NAME, SdbpGetStringAttr(translation, str_InternalName, file_info)); 312 SdbpSetStringAttr(&attr_info[11], TAG_LEGAL_COPYRIGHT, SdbpGetStringAttr(translation, str_LegalCopyright, file_info)); 313 314 /* http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx */ 315 316 SdbpSetDWORDAttr(&attr_info[17], TAG_PE_CHECKSUM, headers->OptionalHeader.CheckSum); 317 318 SdbpSetDWORDAttr(&attr_info[18], TAG_LINKER_VERSION, /* mislabeled! */ 319 ((DWORD)headers->OptionalHeader.MajorImageVersion) << 16 | headers->OptionalHeader.MinorImageVersion); 320 SdbpSetAttrFail(&attr_info[19]); /* TAG_16BIT_DESCRIPTION */ 321 SdbpSetAttrFail(&attr_info[20]); /* TAG_16BIT_MODULE_NAME */ 322 323 SdbpSetDWORDAttr(&attr_info[23], TAG_LINK_DATE, headers->FileHeader.TimeDateStamp); 324 SdbpSetDWORDAttr(&attr_info[24], TAG_UPTO_LINK_DATE, headers->FileHeader.TimeDateStamp); 325 326 export_dir = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(mapped.view, FALSE, IMAGE_DIRECTORY_ENTRY_EXPORT, &export_dir_size); 327 if (export_dir && ((PBYTE)(export_dir+1) <= mapping_end)) 328 { 329 PIMAGE_SECTION_HEADER section = NULL; 330 PBYTE export_name = RtlImageRvaToVa(headers, mapped.view, export_dir->Name, §ion); 331 if (export_name) 332 SdbpSetStringAttrFromAnsiString(&attr_info[25], TAG_EXPORT_NAME, export_name, strlen((char*)export_name)); 333 else 334 SdbpSetAttrFail(&attr_info[25]); /* TAG_EXPORT_NAME */ 335 } 336 else 337 { 338 SdbpSetAttrFail(&attr_info[25]); /* TAG_EXPORT_NAME */ 339 } 340 341 if (info_size) 342 SdbpSetDWORDAttr(&attr_info[26], TAG_VER_LANGUAGE, lang_page->language); 343 344 SdbpSetDWORDAttr(&attr_info[27], TAG_EXE_WRAPPER, 0); /* boolean */ 345 } 346 else 347 { 348 int n; 349 for (n = 2; n < NUM_ATTRIBUTES; ++n) 350 { 351 if (n != 16 && n != 26) 352 SdbpSetAttrFail(&attr_info[n]); 353 } 354 if (module_type == MODTYPE_NE) 355 { 356 PBYTE ptr; 357 PIMAGE_OS2_HEADER os2 = (PIMAGE_OS2_HEADER)headers; 358 if ((PBYTE)(os2 + 1) <= mapping_end) 359 { 360 ptr = mapped.view + os2->ne_nrestab; 361 if (ptr <= mapping_end && (ptr + 1 + *ptr) <= mapping_end) 362 SdbpSetStringAttrFromPascalString(&attr_info[19], TAG_16BIT_DESCRIPTION, ptr); 363 ptr = (PBYTE)os2 + os2->ne_restab; 364 if (ptr <= mapping_end && (ptr + 1 + *ptr) <= mapping_end) 365 SdbpSetStringAttrFromPascalString(&attr_info[20], TAG_16BIT_MODULE_NAME, ptr); 366 } 367 } 368 } 369 370 *attr_info_ret = attr_info; 371 *attr_count = NUM_ATTRIBUTES; /* As far as I know, this one is always 28 */ 372 373 SdbFree(file_info); 374 SdbpCloseMemMappedFile(&mapped); 375 return TRUE; 376 } 377