1 /*
2 * PROJECT: ReactOS Application compatibility module
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: apphelp entrypoint / generic interface functions
5 * COPYRIGHT: Copyright 2011 André Hentschel
6 * Copyright 2013 Mislav Blaževic
7 * Copyright 2015-2019 Mark Jansen (mark.jansen@reactos.org)
8 */
9
10 #define WIN32_NO_STATUS
11 #include "windef.h"
12 #include "winbase.h"
13 #include "winreg.h"
14 #include "strsafe.h"
15 #include "apphelp.h"
16 #include <ndk/rtlfuncs.h>
17 #include <ndk/cmfuncs.h>
18 #include <ndk/obfuncs.h>
19 #include <ndk/kdtypes.h>
20
21
22 ACCESS_MASK Wow64QueryFlag(void);
23
24 const UNICODE_STRING InstalledSDBKeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\InstalledSDB");
25
26 /* from dpfilter.h */
27 #define DPFLTR_APPCOMPAT_ID 123
28
29 #define MAX_GUID_STRING_LEN RTL_NUMBER_OF("{12345678-1234-1234-0123-456789abcdef}")
30 C_ASSERT(MAX_GUID_STRING_LEN == 39); // See psdk/cfgmgr32.h
31
32 #ifndef NT_SUCCESS
33 #define NT_SUCCESS(StatCode) ((NTSTATUS)(StatCode) >= 0)
34 #endif
35
36 ULONG g_ShimDebugLevel = ~0;
37 HMODULE g_hInstance;
38
ApphelppInitDebugLevel(void)39 void ApphelppInitDebugLevel(void)
40 {
41 static const UNICODE_STRING DebugKey = RTL_CONSTANT_STRING(L"SHIM_DEBUG_LEVEL");
42 UNICODE_STRING DebugValue;
43 NTSTATUS Status;
44 ULONG NewLevel = SHIM_ERR;
45 WCHAR Buffer[40];
46
47 RtlInitEmptyUnicodeString(&DebugValue, Buffer, sizeof(Buffer));
48
49 Status = RtlQueryEnvironmentVariable_U(NULL, &DebugKey, &DebugValue);
50
51 if (NT_SUCCESS(Status))
52 {
53 if (!NT_SUCCESS(RtlUnicodeStringToInteger(&DebugValue, 10, &NewLevel)))
54 NewLevel = SHIM_ERR;
55 }
56 g_ShimDebugLevel = NewLevel;
57 }
58
59
DllMain(HINSTANCE hinst,DWORD reason,LPVOID reserved)60 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
61 {
62 switch (reason)
63 {
64 case DLL_PROCESS_ATTACH:
65 g_hInstance = hinst;
66 DisableThreadLibraryCalls(hinst);
67 SdbpHeapInit();
68 break;
69 case DLL_PROCESS_DETACH:
70 SdbpHeapDeinit();
71 break;
72 }
73 return TRUE;
74 }
75
ApphelpCheckInstallShieldPackage(void * ptr,LPCWSTR path)76 BOOL WINAPI ApphelpCheckInstallShieldPackage(void* ptr, LPCWSTR path)
77 {
78 SHIM_WARN("stub: ptr=%p, path='%S'\n", ptr, path);
79 return TRUE;
80 }
81
82
ApphelpCheckShellObject(REFCLSID ObjectCLSID,BOOL bShimIfNecessary,ULONGLONG * pullFlags)83 BOOL WINAPI ApphelpCheckShellObject(REFCLSID ObjectCLSID, BOOL bShimIfNecessary, ULONGLONG *pullFlags)
84 {
85 WCHAR GuidString[MAX_GUID_STRING_LEN];
86 if (!ObjectCLSID || !SdbGUIDToString(ObjectCLSID, GuidString, RTL_NUMBER_OF(GuidString)))
87 GuidString[0] = L'\0';
88 SHIM_WARN("stub: ObjectCLSID='%S', bShimIfNecessary=%d, pullFlags=%p)\n", GuidString, bShimIfNecessary, pullFlags);
89
90 if (pullFlags)
91 *pullFlags = 0;
92
93 return TRUE;
94 }
95
96 /**
97 * Outputs diagnostic info.
98 *
99 * @param [in] Level The level to log this message with, choose any of [SHIM_ERR,
100 * SHIM_WARN, SHIM_INFO].
101 * @param [in] FunctionName The function this log should be attributed to.
102 * @param [in] Format The format string.
103 * @param ... Variable arguments providing additional information.
104 *
105 * @return Success: TRUE Failure: FALSE.
106 */
ShimDbgPrint(SHIM_LOG_LEVEL Level,PCSTR FunctionName,PCSTR Format,...)107 BOOL WINAPIV ShimDbgPrint(SHIM_LOG_LEVEL Level, PCSTR FunctionName, PCSTR Format, ...)
108 {
109 char Buffer[512];
110 va_list ArgList;
111 char* Current = Buffer;
112 const char* LevelStr;
113 size_t Length = sizeof(Buffer);
114
115 if (g_ShimDebugLevel == ~0)
116 ApphelppInitDebugLevel();
117
118 if (Level > g_ShimDebugLevel)
119 return FALSE;
120
121 switch (Level)
122 {
123 case SHIM_ERR:
124 LevelStr = "Err ";
125 Level = DPFLTR_MASK | (1 << DPFLTR_ERROR_LEVEL);
126 break;
127 case SHIM_WARN:
128 LevelStr = "Warn";
129 Level = DPFLTR_MASK | (1 << DPFLTR_WARNING_LEVEL);
130 break;
131 case SHIM_INFO:
132 LevelStr = "Info";
133 Level = DPFLTR_MASK | (1 << DPFLTR_INFO_LEVEL);
134 break;
135 default:
136 LevelStr = "User";
137 Level = DPFLTR_MASK | (1 << DPFLTR_INFO_LEVEL);
138 break;
139 }
140 StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s][%-20s] ", LevelStr, FunctionName);
141
142 va_start(ArgList, Format);
143 StringCchVPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, Format, ArgList);
144 va_end(ArgList);
145
146 #if defined(APPCOMPAT_USE_DBGPRINTEX) && APPCOMPAT_USE_DBGPRINTEX
147 return NT_SUCCESS(DbgPrintEx(DPFLTR_APPCOMPAT_ID, Level, "%s", Buffer));
148 #else
149 DbgPrint("%s", Buffer);
150 return TRUE;
151 #endif
152 }
153
154
155 #define APPHELP_DONTWRITE_REASON 2
156 #define APPHELP_CLEARBITS 0x100 /* TODO: Investigate */
157 #define APPHELP_IGNORE_ENVIRONMENT 0x400
158
159 #define APPHELP_VALID_RESULT 0x10000
160 #define APPHELP_RESULT_NOTFOUND 0x20000
161 #define APPHELP_RESULT_FOUND 0x40000
162
163 /**
164 * Lookup Shims / Fixes for the specified application
165 *
166 * @param [in] FileHandle Handle to the file to check.
167 * @param [in] Unk1
168 * @param [in] Unk2
169 * @param [in] ApplicationName Exe to check
170 * @param [in] Environment The environment variables to use, or NULL to use the current environment.
171 * @param [in] ExeType Exe type (MACHINE_TYPE_XXXX)
172 * @param [in,out] Reason Input/output flags
173 * @param [in] SdbQueryAppCompatData The resulting data.
174 * @param [in] SdbQueryAppCompatDataSize The resulting data size.
175 * @param [in] SxsData TODO
176 * @param [in] SxsDataSize TODO
177 * @param [in] FusionFlags TODO
178 * @param [in] SomeFlag1 TODO
179 * @param [in] SomeFlag2 TODO
180 *
181 * @return TRUE if the application is allowed to run.
182 */
183 BOOL
184 WINAPI
ApphelpCheckRunAppEx(_In_ HANDLE FileHandle,_In_opt_ PVOID Unk1,_In_opt_ PVOID Unk2,_In_opt_z_ PCWSTR ApplicationName,_In_opt_ PVOID Environment,_In_opt_ USHORT ExeType,_Inout_opt_ PULONG Reason,_Out_opt_ PVOID * SdbQueryAppCompatData,_Out_opt_ PULONG SdbQueryAppCompatDataSize,_Out_opt_ PVOID * SxsData,_Out_opt_ PULONG SxsDataSize,_Out_opt_ PULONG FusionFlags,_Out_opt_ PULONG64 SomeFlag1,_Out_opt_ PULONG SomeFlag2)185 ApphelpCheckRunAppEx(
186 _In_ HANDLE FileHandle,
187 _In_opt_ PVOID Unk1,
188 _In_opt_ PVOID Unk2,
189 _In_opt_z_ PCWSTR ApplicationName,
190 _In_opt_ PVOID Environment,
191 _In_opt_ USHORT ExeType,
192 _Inout_opt_ PULONG Reason,
193 _Out_opt_ PVOID* SdbQueryAppCompatData,
194 _Out_opt_ PULONG SdbQueryAppCompatDataSize,
195 _Out_opt_ PVOID* SxsData,
196 _Out_opt_ PULONG SxsDataSize,
197 _Out_opt_ PULONG FusionFlags,
198 _Out_opt_ PULONG64 SomeFlag1,
199 _Out_opt_ PULONG SomeFlag2)
200 {
201 SDBQUERYRESULT* result = NULL;
202 HSDB hsdb = NULL;
203 DWORD dwFlags = 0;
204
205 if (SxsData)
206 *SxsData = NULL;
207 if (SxsDataSize)
208 *SxsDataSize = 0;
209 if (FusionFlags)
210 *FusionFlags = 0;
211 if (SomeFlag1)
212 *SomeFlag1 = 0;
213 if (SomeFlag2)
214 *SomeFlag2 = 0;
215 if (Reason)
216 dwFlags = *Reason;
217
218 dwFlags &= ~APPHELP_CLEARBITS;
219
220 *SdbQueryAppCompatData = result = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SDBQUERYRESULT));
221 if (SdbQueryAppCompatDataSize)
222 *SdbQueryAppCompatDataSize = sizeof(*result);
223
224
225 hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL);
226 if (hsdb)
227 {
228 BOOL FoundMatch;
229 DWORD MatchingExeFlags = 0;
230
231 if (dwFlags & APPHELP_IGNORE_ENVIRONMENT)
232 MatchingExeFlags |= SDBGMEF_IGNORE_ENVIRONMENT;
233
234 FoundMatch = SdbGetMatchingExe(hsdb, ApplicationName, NULL, Environment, MatchingExeFlags, result);
235 if (FileHandle != INVALID_HANDLE_VALUE)
236 {
237 dwFlags |= APPHELP_VALID_RESULT;
238 dwFlags |= (FoundMatch ? APPHELP_RESULT_FOUND : APPHELP_RESULT_NOTFOUND);
239 }
240
241 SdbReleaseDatabase(hsdb);
242 }
243
244 if (Reason && !(dwFlags & APPHELP_DONTWRITE_REASON))
245 *Reason = dwFlags;
246
247
248 /* We should _ALWAYS_ return TRUE here, unless we want to block an application from starting! */
249 return TRUE;
250 }
251
252
253 /**
254 * @name SdbRegisterDatabaseEx
255 * Register an application compatibility database
256 *
257 * @param pszDatabasePath The database. Required
258 * @param dwDatabaseType The database type. SDB_DATABASE_*
259 * @param pTimeStamp The timestamp. When this argument is not provided, the system time is used.
260 * @return TRUE on success, or FALSE on failure.
261 */
SdbRegisterDatabaseEx(_In_ LPCWSTR pszDatabasePath,_In_ DWORD dwDatabaseType,_In_opt_ const PULONGLONG pTimeStamp)262 BOOL WINAPI SdbRegisterDatabaseEx(
263 _In_ LPCWSTR pszDatabasePath,
264 _In_ DWORD dwDatabaseType,
265 _In_opt_ const PULONGLONG pTimeStamp)
266 {
267 PDB pdb;
268 DB_INFORMATION Information;
269 WCHAR GuidBuffer[MAX_GUID_STRING_LEN];
270 UNICODE_STRING KeyName;
271 ACCESS_MASK KeyAccess;
272 OBJECT_ATTRIBUTES ObjectKey = RTL_INIT_OBJECT_ATTRIBUTES(&KeyName, OBJ_CASE_INSENSITIVE);
273 NTSTATUS Status;
274 HANDLE InstalledSDBKey;
275
276 pdb = SdbOpenDatabase(pszDatabasePath, DOS_PATH);
277 if (!pdb)
278 {
279 SHIM_ERR("Unable to open DB %S\n", pszDatabasePath);
280 return FALSE;
281 }
282
283 if (!SdbGetDatabaseInformation(pdb, &Information) ||
284 !(Information.dwFlags & DB_INFO_FLAGS_VALID_GUID))
285 {
286 SHIM_ERR("Unable to retrieve DB info\n");
287 SdbCloseDatabase(pdb);
288 return FALSE;
289 }
290
291 if (!SdbGUIDToString(&Information.Id, GuidBuffer, RTL_NUMBER_OF(GuidBuffer)))
292 {
293 SHIM_ERR("Unable to Convert GUID to string\n");
294 SdbFreeDatabaseInformation(&Information);
295 SdbCloseDatabase(pdb);
296 return FALSE;
297 }
298
299 KeyName = InstalledSDBKeyName;
300 KeyAccess = Wow64QueryFlag() | KEY_WRITE | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS;
301 Status = NtCreateKey(&InstalledSDBKey, KeyAccess, &ObjectKey, 0, NULL, 0, NULL);
302 if (NT_SUCCESS(Status))
303 {
304 HANDLE DbKey;
305
306 RtlInitUnicodeString(&KeyName, GuidBuffer);
307 ObjectKey.RootDirectory = InstalledSDBKey;
308
309 Status = NtCreateKey(&DbKey, KeyAccess, &ObjectKey, 0, NULL, 0, NULL);
310 if (NT_SUCCESS(Status))
311 {
312 UNICODE_STRING DatabasePathKey = RTL_CONSTANT_STRING(L"DatabasePath");
313 UNICODE_STRING DatabaseInstallTimeStampKey = RTL_CONSTANT_STRING(L"DatabaseInstallTimeStamp");
314 UNICODE_STRING DatabaseTypeKey = RTL_CONSTANT_STRING(L"DatabaseType");
315 UNICODE_STRING DatabaseDescriptionKey = RTL_CONSTANT_STRING(L"DatabaseDescription");
316
317 Status = NtSetValueKey(DbKey, &DatabasePathKey, 0, REG_SZ,
318 (PVOID)pszDatabasePath, ((ULONG)wcslen(pszDatabasePath) + 1) * sizeof(WCHAR));
319 if (!NT_SUCCESS(Status))
320 SHIM_ERR("Unable to write %wZ\n", &DatabasePathKey);
321
322 if (NT_SUCCESS(Status))
323 {
324 ULARGE_INTEGER ulTimeStamp;
325 if (pTimeStamp)
326 {
327 ulTimeStamp.QuadPart = *pTimeStamp;
328 }
329 else
330 {
331 FILETIME fi;
332 GetSystemTimeAsFileTime(&fi);
333 ulTimeStamp.LowPart = fi.dwLowDateTime;
334 ulTimeStamp.HighPart = fi.dwHighDateTime;
335 }
336 Status = NtSetValueKey(DbKey, &DatabaseInstallTimeStampKey, 0, REG_QWORD,
337 &ulTimeStamp.QuadPart, sizeof(ulTimeStamp));
338 if (!NT_SUCCESS(Status))
339 SHIM_ERR("Unable to write %wZ\n", &DatabaseInstallTimeStampKey);
340 }
341
342 if (NT_SUCCESS(Status))
343 {
344 Status = NtSetValueKey(DbKey, &DatabaseTypeKey, 0, REG_DWORD,
345 &dwDatabaseType, sizeof(dwDatabaseType));
346 if (!NT_SUCCESS(Status))
347 SHIM_ERR("Unable to write %wZ\n", &DatabaseTypeKey);
348 }
349
350 if (NT_SUCCESS(Status) && Information.Description)
351 {
352 Status = NtSetValueKey(DbKey, &DatabaseDescriptionKey, 0, REG_SZ,
353 (PVOID)Information.Description, ((ULONG)wcslen(Information.Description) + 1) * sizeof(WCHAR));
354 if (!NT_SUCCESS(Status))
355 SHIM_ERR("Unable to write %wZ\n", &DatabaseDescriptionKey);
356 }
357
358 NtClose(DbKey);
359
360 if (NT_SUCCESS(Status))
361 {
362 SHIM_INFO("Installed %wS as %wZ\n", pszDatabasePath, &KeyName);
363 }
364 }
365 else
366 {
367 SHIM_ERR("Unable to create key %wZ\n", &KeyName);
368 }
369
370 NtClose(InstalledSDBKey);
371 }
372 else
373 {
374 SHIM_ERR("Unable to create key %wZ\n", &KeyName);
375 }
376
377 SdbFreeDatabaseInformation(&Information);
378 SdbCloseDatabase(pdb);
379
380 return NT_SUCCESS(Status);
381 }
382
383
384 /**
385 * @name SdbRegisterDatabase
386 * Register an application compatibility database
387 *
388 * @param pszDatabasePath The database. Required
389 * @param dwDatabaseType The database type. SDB_DATABASE_*
390 * @return TRUE on success, or FALSE on failure.
391 */
SdbRegisterDatabase(_In_ LPCWSTR pszDatabasePath,_In_ DWORD dwDatabaseType)392 BOOL WINAPI SdbRegisterDatabase(
393 _In_ LPCWSTR pszDatabasePath,
394 _In_ DWORD dwDatabaseType)
395 {
396 return SdbRegisterDatabaseEx(pszDatabasePath, dwDatabaseType, NULL);
397 }
398
399
400 /**
401 * @name SdbUnregisterDatabase
402 *
403 *
404 * @param pguidDB
405 * @return
406 */
SdbUnregisterDatabase(_In_ const GUID * pguidDB)407 BOOL WINAPI SdbUnregisterDatabase(_In_ const GUID *pguidDB)
408 {
409 WCHAR KeyBuffer[MAX_PATH], GuidBuffer[MAX_GUID_STRING_LEN];
410 UNICODE_STRING KeyName;
411 ACCESS_MASK KeyAccess;
412 OBJECT_ATTRIBUTES ObjectKey = RTL_INIT_OBJECT_ATTRIBUTES(&KeyName, OBJ_CASE_INSENSITIVE);
413 NTSTATUS Status;
414 HANDLE DbKey;
415
416 if (!SdbGUIDToString(pguidDB, GuidBuffer, RTL_NUMBER_OF(GuidBuffer)))
417 {
418 SHIM_ERR("Unable to Convert GUID to string\n");
419 return FALSE;
420 }
421
422 RtlInitEmptyUnicodeString(&KeyName, KeyBuffer, sizeof(KeyBuffer));
423 RtlAppendUnicodeStringToString(&KeyName, &InstalledSDBKeyName);
424 RtlAppendUnicodeToString(&KeyName, L"\\");
425 RtlAppendUnicodeToString(&KeyName, GuidBuffer);
426
427 KeyAccess = Wow64QueryFlag() | DELETE;
428 Status = NtCreateKey(&DbKey, KeyAccess, &ObjectKey, 0, NULL, 0, NULL);
429 if (!NT_SUCCESS(Status))
430 {
431 SHIM_ERR("Unable to open %wZ\n", &KeyName);
432 return FALSE;
433 }
434
435 Status = NtDeleteKey(DbKey);
436 if (!NT_SUCCESS(Status))
437 SHIM_ERR("Unable to delete %wZ\n", &KeyName);
438
439 NtClose(DbKey);
440 return NT_SUCCESS(Status);
441 }
442
443
444 /* kernel32.dll */
445 BOOL WINAPI BaseDumpAppcompatCache(VOID);
446 BOOL WINAPI BaseFlushAppcompatCache(VOID);
447
448
449 /**
450 * @name ShimDumpCache
451 * Dump contents of the shim cache.
452 *
453 * @param hwnd Unused, pass 0
454 * @param hInstance Unused, pass 0
455 * @param lpszCmdLine Unused, pass 0
456 * @param nCmdShow Unused, pass 0
457 * @return
458 */
ShimDumpCache(HWND hwnd,HINSTANCE hInstance,LPCSTR lpszCmdLine,int nCmdShow)459 BOOL WINAPI ShimDumpCache(HWND hwnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, int nCmdShow)
460 {
461 return BaseDumpAppcompatCache();
462 }
463
464 /**
465 * @name ShimFlushCache
466 * Flush the shim cache. Call this after installing a new shim database
467 *
468 * @param hwnd Unused, pass 0
469 * @param hInstance Unused, pass 0
470 * @param lpszCmdLine Unused, pass 0
471 * @param nCmdShow Unused, pass 0
472 * @return
473 */
ShimFlushCache(HWND hwnd,HINSTANCE hInstance,LPCSTR lpszCmdLine,int nCmdShow)474 BOOL WINAPI ShimFlushCache(HWND hwnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, int nCmdShow)
475 {
476 return BaseFlushAppcompatCache();
477 }
478