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 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 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 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 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 474 BOOL WINAPI ShimFlushCache(HWND hwnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, int nCmdShow) 475 { 476 return BaseFlushAppcompatCache(); 477 } 478