xref: /reactos/dll/appcompat/apphelp/sdbfileattr.c (revision c2c66aff)
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, &section);
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