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