reactos/dll/appcompat/apphelp/sdbfileattr.c
2018-08-04 19:19:34 +02:00

377 lines
13 KiB
C

/*
* PROJECT: ReactOS Application compatibility module
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* PURPOSE: Query file attributes used to match exe's
* COPYRIGHT: Copyright 2011 André Hentschel
* Copyright 2013 Mislav Blaževic
* Copyright 2015-2017 Mark Jansen (mark.jansen@reactos.org)
*/
#define WIN32_NO_STATUS
#include "windef.h"
#include "apphelp.h"
#include "strsafe.h"
#include "winver.h"
#include "rtlfuncs.h"
#define NUM_ATTRIBUTES 28
enum APPHELP_MODULETYPE
{
MODTYPE_UNKNOWN = 0,
MODTYPE_DOS = 1,
MODTYPE_NE = 2,
MODTYPE_PE = 3,
};
static void WINAPI SdbpSetDWORDAttr(PATTRINFO attr, TAG tag, DWORD value)
{
attr->type = tag;
attr->flags = ATTRIBUTE_AVAILABLE;
attr->dwattr = value;
}
static void WINAPI SdbpSetQWORDAttr(PATTRINFO attr, TAG tag, QWORD value)
{
attr->type = tag;
attr->flags = ATTRIBUTE_AVAILABLE;
attr->qwattr = value;
}
static void WINAPI SdbpSetStringAttr(PATTRINFO attr, TAG tag, WCHAR *string)
{
if (!string)
{
attr->flags = ATTRIBUTE_FAILED;
return;
}
attr->type = tag;
attr->flags = ATTRIBUTE_AVAILABLE;
attr->lpattr = SdbpStrDup(string);
}
static void WINAPI SdbpSetAttrFail(PATTRINFO attr)
{
attr->flags = ATTRIBUTE_FAILED;
}
static WCHAR* WINAPI SdbpGetStringAttr(LPWSTR translation, LPCWSTR attr, PVOID file_info)
{
UINT size = 0;
PVOID buffer;
WCHAR value[128] = {0};
if (!file_info)
return NULL;
StringCchPrintfW(value, ARRAYSIZE(value), translation, attr);
if (VerQueryValueW(file_info, value, &buffer, &size) && size != 0)
return (WCHAR*)buffer;
return NULL;
}
static void WINAPI SdbpSetStringAttrFromAnsiString(PATTRINFO attr, TAG tag, PBYTE string, size_t len)
{
WCHAR* dest;
if (!string)
{
attr->flags = ATTRIBUTE_FAILED;
return;
}
attr->type = tag;
attr->flags = ATTRIBUTE_AVAILABLE;
dest = attr->lpattr = SdbAlloc((len+1) * sizeof(WCHAR));
while (len--)
*(dest++) = *(string++);
*dest = 0;
}
static void WINAPI SdbpSetStringAttrFromPascalString(PATTRINFO attr, TAG tag, PBYTE string)
{
if (!string)
{
attr->flags = ATTRIBUTE_FAILED;
return;
}
SdbpSetStringAttrFromAnsiString(attr, tag, string + 1, *string);
}
static void SdbpReadFileVersion(PATTRINFO attr_info, PVOID file_info)
{
static const WCHAR str_root[] = {'\\',0};
VS_FIXEDFILEINFO* fixed_info;
UINT size;
if (file_info && VerQueryValueW(file_info, str_root, (LPVOID*)&fixed_info, &size) && size)
{
if (fixed_info->dwSignature == VS_FFI_SIGNATURE)
{
LARGE_INTEGER version;
version.HighPart = fixed_info->dwFileVersionMS;
version.LowPart = fixed_info->dwFileVersionLS;
SdbpSetQWORDAttr(&attr_info[2], TAG_BIN_FILE_VERSION, version.QuadPart);
SdbpSetQWORDAttr(&attr_info[21], TAG_UPTO_BIN_FILE_VERSION, version.QuadPart);
version.HighPart = fixed_info->dwProductVersionMS;
version.LowPart = fixed_info->dwProductVersionLS;
SdbpSetQWORDAttr(&attr_info[3], TAG_BIN_PRODUCT_VERSION, version.QuadPart);
SdbpSetQWORDAttr(&attr_info[22], TAG_UPTO_BIN_PRODUCT_VERSION, version.QuadPart);
SdbpSetDWORDAttr(&attr_info[12], TAG_VERDATEHI, fixed_info->dwFileDateMS);
SdbpSetDWORDAttr(&attr_info[13], TAG_VERDATELO, fixed_info->dwFileDateLS);
SdbpSetDWORDAttr(&attr_info[14], TAG_VERFILEOS, fixed_info->dwFileOS); /* 0x000, 0x4, 0x40004, 0x40000, 0x10004, 0x10001*/
SdbpSetDWORDAttr(&attr_info[15], TAG_VERFILETYPE, fixed_info->dwFileType); /* VFT_APP, VFT_DLL, .... */
return;
}
}
SdbpSetAttrFail(&attr_info[2]);
SdbpSetAttrFail(&attr_info[3]);
SdbpSetAttrFail(&attr_info[12]);
SdbpSetAttrFail(&attr_info[13]);
SdbpSetAttrFail(&attr_info[14]);
SdbpSetAttrFail(&attr_info[15]);
SdbpSetAttrFail(&attr_info[21]);
SdbpSetAttrFail(&attr_info[22]);
}
static DWORD WINAPI SdbpCalculateFileChecksum(PMEMMAPPED mapping)
{
size_t n, size;
PDWORD data;
DWORD checks = 0, carry = 0;
if (mapping->size < 4)
return 0;
if (mapping->size >= 0x1000)
{
size = 0x1000;
if (mapping->size < 0x1200)
data = (PDWORD)(mapping->view + mapping->size - size);
else
data = (PDWORD)mapping->view + (0x200 / 4);
}
else
{
data = (PDWORD)mapping->view;
size = mapping->size;
}
for (n = 0; n < size / 4; ++n)
{
checks += *data;
carry = (checks & 1) ? 0x80000000 : 0;
checks >>= 1;
checks |= carry;
++data;
}
return checks;
}
static DWORD WINAPI SdbpGetModuleType(PMEMMAPPED mapping, PIMAGE_NT_HEADERS* nt_headers)
{
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)mapping->view;
PIMAGE_OS2_HEADER os2;
*nt_headers = NULL;
if (mapping->size < 2 || dos->e_magic != IMAGE_DOS_SIGNATURE)
return MODTYPE_UNKNOWN;
if (mapping->size < sizeof(IMAGE_DOS_HEADER) || mapping->size < (dos->e_lfanew+2))
return MODTYPE_DOS;
os2 = (PIMAGE_OS2_HEADER)((PBYTE)dos + dos->e_lfanew);
if (os2->ne_magic == IMAGE_OS2_SIGNATURE || os2->ne_magic == IMAGE_OS2_SIGNATURE_LE)
{
*nt_headers = (PIMAGE_NT_HEADERS)os2;
return MODTYPE_NE;
}
if (mapping->size >= (dos->e_lfanew + 4) && ((PIMAGE_NT_HEADERS)os2)->Signature == IMAGE_NT_SIGNATURE)
{
*nt_headers = (PIMAGE_NT_HEADERS)os2;
return MODTYPE_PE;
}
return MODTYPE_DOS;
}
/**
* Frees attribute data allocated by SdbGetFileAttributes.
*
* @note Unlike Windows, this implementation will not crash if attr_info is NULL.
*
* @param [in] attr_info Pointer to array of ATTRINFO which will be freed.
*
* @return TRUE if it succeeds, FALSE if it fails.
*/
BOOL WINAPI SdbFreeFileAttributes(PATTRINFO attr_info)
{
WORD i;
if (!attr_info)
return FALSE;
for (i = 0; i < NUM_ATTRIBUTES; i++)
if ((attr_info[i].type & TAG_TYPE_MASK) == TAG_TYPE_STRINGREF)
SdbFree(attr_info[i].lpattr);
SdbFree(attr_info);
return TRUE;
}
/**
* Retrieves attribute data shim database requires to match a file with database entry
*
* @note You must free the attr_info allocated by this function by calling SdbFreeFileAttributes.
*
* @param [in] path Path to the file.
* @param [out] attr_info_ret Pointer to array of ATTRINFO. Contains attribute data.
* @param [out] attr_count Number of attributes in attr_info.
*
* @return TRUE if it succeeds, FALSE if it fails.
*/
BOOL WINAPI SdbGetFileAttributes(LPCWSTR path, PATTRINFO *attr_info_ret, LPDWORD attr_count)
{
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};
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};
static const WCHAR str_CompanyName[] = {'C','o','m','p','a','n','y','N','a','m','e',0};
static const WCHAR str_FileDescription[] = {'F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0};
static const WCHAR str_FileVersion[] = {'F','i','l','e','V','e','r','s','i','o','n',0};
static const WCHAR str_InternalName[] = {'I','n','t','e','r','n','a','l','N','a','m','e',0};
static const WCHAR str_LegalCopyright[] = {'L','e','g','a','l','C','o','p','y','r','i','g','h','t',0};
static const WCHAR str_OriginalFilename[] = {'O','r','i','g','i','n','a','l','F','i','l','e','n','a','m','e',0};
static const WCHAR str_ProductName[] = {'P','r','o','d','u','c','t','N','a','m','e',0};
static const WCHAR str_ProductVersion[] = {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
PIMAGE_NT_HEADERS headers;
MEMMAPPED mapped;
PBYTE mapping_end;
PVOID file_info = 0;
DWORD module_type;
WCHAR translation[128] = {0};
PATTRINFO attr_info;
struct LANGANDCODEPAGE {
WORD language;
WORD code_page;
} *lang_page;
if (!SdbpOpenMemMappedFile(path, &mapped))
{
SHIM_ERR("Error retrieving FILEINFO structure\n");
return FALSE;
}
mapping_end = mapped.view + mapped.size;
attr_info = (PATTRINFO)SdbAlloc(NUM_ATTRIBUTES * sizeof(ATTRINFO));
SdbpSetDWORDAttr(&attr_info[0], TAG_SIZE, mapped.size);
if (mapped.size)
SdbpSetDWORDAttr(&attr_info[1], TAG_CHECKSUM, SdbpCalculateFileChecksum(&mapped));
else
SdbpSetAttrFail(&attr_info[1]);
module_type = SdbpGetModuleType(&mapped, &headers);
if (module_type != MODTYPE_UNKNOWN)
SdbpSetDWORDAttr(&attr_info[16], TAG_MODULE_TYPE, module_type);
else
SdbpSetAttrFail(&attr_info[16]); /* TAG_MODULE_TYPE */
if (headers && module_type == MODTYPE_PE && ((PBYTE)(headers+1) <= mapping_end))
{
DWORD info_size;
ULONG export_dir_size;
PIMAGE_EXPORT_DIRECTORY export_dir;
info_size = GetFileVersionInfoSizeW(path, NULL);
if (info_size != 0)
{
UINT page_size = 0;
file_info = SdbAlloc(info_size);
GetFileVersionInfoW(path, 0, info_size, file_info);
VerQueryValueW(file_info, str_tinfo, (LPVOID)&lang_page, &page_size);
StringCchPrintfW(translation, ARRAYSIZE(translation), str_trans, lang_page->language, lang_page->code_page);
}
/* Handles 2, 3, 12, 13, 14, 15, 21, 22 */
SdbpReadFileVersion(attr_info, file_info);
SdbpSetStringAttr(&attr_info[4], TAG_PRODUCT_VERSION, SdbpGetStringAttr(translation, str_ProductVersion, file_info));
SdbpSetStringAttr(&attr_info[5], TAG_FILE_DESCRIPTION, SdbpGetStringAttr(translation, str_FileDescription, file_info));
SdbpSetStringAttr(&attr_info[6], TAG_COMPANY_NAME, SdbpGetStringAttr(translation, str_CompanyName, file_info));
SdbpSetStringAttr(&attr_info[7], TAG_PRODUCT_NAME, SdbpGetStringAttr(translation, str_ProductName, file_info));
SdbpSetStringAttr(&attr_info[8], TAG_FILE_VERSION, SdbpGetStringAttr(translation, str_FileVersion, file_info));
SdbpSetStringAttr(&attr_info[9], TAG_ORIGINAL_FILENAME, SdbpGetStringAttr(translation, str_OriginalFilename, file_info));
SdbpSetStringAttr(&attr_info[10], TAG_INTERNAL_NAME, SdbpGetStringAttr(translation, str_InternalName, file_info));
SdbpSetStringAttr(&attr_info[11], TAG_LEGAL_COPYRIGHT, SdbpGetStringAttr(translation, str_LegalCopyright, file_info));
/* http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx */
SdbpSetDWORDAttr(&attr_info[17], TAG_PE_CHECKSUM, headers->OptionalHeader.CheckSum);
SdbpSetDWORDAttr(&attr_info[18], TAG_LINKER_VERSION, /* mislabeled! */
((DWORD)headers->OptionalHeader.MajorImageVersion) << 16 | headers->OptionalHeader.MinorImageVersion);
SdbpSetAttrFail(&attr_info[19]); /* TAG_16BIT_DESCRIPTION */
SdbpSetAttrFail(&attr_info[20]); /* TAG_16BIT_MODULE_NAME */
SdbpSetDWORDAttr(&attr_info[23], TAG_LINK_DATE, headers->FileHeader.TimeDateStamp);
SdbpSetDWORDAttr(&attr_info[24], TAG_UPTO_LINK_DATE, headers->FileHeader.TimeDateStamp);
export_dir = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(mapped.view, FALSE, IMAGE_DIRECTORY_ENTRY_EXPORT, &export_dir_size);
if (export_dir && ((PBYTE)(export_dir+1) <= mapping_end))
{
PIMAGE_SECTION_HEADER section = NULL;
PBYTE export_name = RtlImageRvaToVa(headers, mapped.view, export_dir->Name, &section);
if (export_name)
SdbpSetStringAttrFromAnsiString(&attr_info[25], TAG_EXPORT_NAME, export_name, strlen((char*)export_name));
else
SdbpSetAttrFail(&attr_info[25]); /* TAG_EXPORT_NAME */
}
else
{
SdbpSetAttrFail(&attr_info[25]); /* TAG_EXPORT_NAME */
}
if (info_size)
SdbpSetDWORDAttr(&attr_info[26], TAG_VER_LANGUAGE, lang_page->language);
SdbpSetDWORDAttr(&attr_info[27], TAG_EXE_WRAPPER, 0); /* boolean */
}
else
{
int n;
for (n = 2; n < NUM_ATTRIBUTES; ++n)
{
if (n != 16 && n != 26)
SdbpSetAttrFail(&attr_info[n]);
}
if (module_type == MODTYPE_NE)
{
PBYTE ptr;
PIMAGE_OS2_HEADER os2 = (PIMAGE_OS2_HEADER)headers;
if ((PBYTE)(os2 + 1) <= mapping_end)
{
ptr = mapped.view + os2->ne_nrestab;
if (ptr <= mapping_end && (ptr + 1 + *ptr) <= mapping_end)
SdbpSetStringAttrFromPascalString(&attr_info[19], TAG_16BIT_DESCRIPTION, ptr);
ptr = (PBYTE)os2 + os2->ne_restab;
if (ptr <= mapping_end && (ptr + 1 + *ptr) <= mapping_end)
SdbpSetStringAttrFromPascalString(&attr_info[20], TAG_16BIT_MODULE_NAME, ptr);
}
}
}
*attr_info_ret = attr_info;
*attr_count = NUM_ATTRIBUTES; /* As far as I know, this one is always 28 */
SdbFree(file_info);
SdbpCloseMemMappedFile(&mapped);
return TRUE;
}