1 /* 2 * PROJECT: ReactOS Application compatibility module 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Shim engine core 5 * COPYRIGHT: Copyright 2015-2019 Mark Jansen (mark.jansen@reactos.org) 6 */ 7 8 #define WIN32_NO_STATUS 9 #include "ntndk.h" 10 #define IN_APPHELP 11 #include "shimlib.h" 12 #include <strsafe.h> 13 /* Make sure we don't include apphelp logging */ 14 #define APPHELP_NOSDBPAPI 15 #include "apphelp.h" 16 #include "shimeng.h" 17 18 19 20 FARPROC WINAPI StubGetProcAddress(HINSTANCE hModule, LPCSTR lpProcName); 21 BOOL WINAPI SE_IsShimDll(PVOID BaseAddress); 22 23 static const UNICODE_STRING Ntdll = RTL_CONSTANT_STRING(L"ntdll.dll"); 24 static const UNICODE_STRING Kernel32 = RTL_CONSTANT_STRING(L"kernel32.dll"); 25 static const UNICODE_STRING Verifier = RTL_CONSTANT_STRING(L"verifier.dll"); 26 27 extern HMODULE g_hInstance; 28 static UNICODE_STRING g_WindowsDirectory; 29 static UNICODE_STRING g_System32Directory; 30 static UNICODE_STRING g_SxsDirectory; 31 static UNICODE_STRING g_LoadingShimDll; 32 ULONG g_ShimEngDebugLevel = 0xffffffff; 33 BOOL g_bComPlusImage = FALSE; 34 BOOL g_bShimDuringInit = FALSE; 35 BOOL g_bShimEngInitialized = FALSE; 36 BOOL g_bInternalHooksUsed = FALSE; 37 static ARRAY g_pShimInfo; /* PSHIMMODULE */ 38 static ARRAY g_pHookArray; /* HOOKMODULEINFO */ 39 static ARRAY g_InExclude; /* INEXCLUDE */ 40 41 typedef FARPROC(WINAPI* GETPROCADDRESSPROC)(HINSTANCE, LPCSTR); 42 /* If we have setup a hook for a function, we should also redirect GetProcAddress for this function */ 43 HOOKAPIEX g_IntHookEx[] = 44 { 45 { 46 "kernel32.dll", /* LibraryName */ 47 "GetProcAddress", /* FunctionName */ 48 StubGetProcAddress, /* ReplacementFunction*/ 49 NULL, /* OriginalFunction */ 50 NULL, /* pShimInfo */ 51 NULL /* ApiLink */ 52 }, 53 }; 54 55 static inline BOOL ARRAY_InitWorker(PARRAY Array, DWORD ItemSize) 56 { 57 Array->Data__ = NULL; 58 Array->Size__ = Array->MaxSize__ = 0; 59 Array->ItemSize__ = ItemSize; 60 61 return TRUE; 62 } 63 64 static inline BOOL ARRAY_EnsureSize(PARRAY Array, DWORD ItemSize, DWORD GrowWith) 65 { 66 PVOID pNewData; 67 DWORD Count; 68 69 ASSERT(Array); 70 ASSERT(ItemSize == Array->ItemSize__); 71 72 if (Array->MaxSize__ > Array->Size__) 73 return TRUE; 74 75 Count = Array->Size__ + GrowWith; 76 pNewData = SeiAlloc(Count * ItemSize); 77 78 if (!pNewData) 79 { 80 SHIMENG_FAIL("Failed to allocate %d bytes\n", Count * ItemSize); 81 return FALSE; 82 } 83 Array->MaxSize__ = Count; 84 85 if (Array->Data__) 86 { 87 memcpy(pNewData, Array->Data__, Array->Size__ * ItemSize); 88 SeiFree(Array->Data__); 89 } 90 Array->Data__ = pNewData; 91 92 return TRUE; 93 } 94 95 static inline PVOID ARRAY_AppendWorker(PARRAY Array, DWORD ItemSize, DWORD GrowWith) 96 { 97 PBYTE pData; 98 99 if (!ARRAY_EnsureSize(Array, ItemSize, GrowWith)) 100 return NULL; 101 102 pData = Array->Data__; 103 pData += (Array->Size__ * ItemSize); 104 Array->Size__++; 105 106 return pData; 107 } 108 109 static inline PVOID ARRAY_AtWorker(PARRAY Array, DWORD ItemSize, DWORD n) 110 { 111 PBYTE pData; 112 113 ASSERT(Array); 114 ASSERT(ItemSize == Array->ItemSize__); 115 ASSERT(n < Array->Size__); 116 117 pData = Array->Data__; 118 return pData + (n * ItemSize); 119 } 120 121 122 #define ARRAY_Init(Array, TypeOfArray) ARRAY_InitWorker((Array), sizeof(TypeOfArray)) 123 #define ARRAY_Append(Array, TypeOfArray) (TypeOfArray*)ARRAY_AppendWorker((Array), sizeof(TypeOfArray), 5) 124 #define ARRAY_At(Array, TypeOfArray, at) (TypeOfArray*)ARRAY_AtWorker((Array), sizeof(TypeOfArray), at) 125 #define ARRAY_Size(Array) (Array)->Size__ 126 127 128 VOID SeiInitDebugSupport(VOID) 129 { 130 static const UNICODE_STRING DebugKey = RTL_CONSTANT_STRING(L"SHIMENG_DEBUG_LEVEL"); 131 UNICODE_STRING DebugValue; 132 NTSTATUS Status; 133 ULONG NewLevel = SEI_MSG; /* Show some basic info in the logs, unless configured different */ 134 WCHAR Buffer[40]; 135 136 RtlInitEmptyUnicodeString(&DebugValue, Buffer, sizeof(Buffer)); 137 138 Status = RtlQueryEnvironmentVariable_U(NULL, &DebugKey, &DebugValue); 139 140 if (NT_SUCCESS(Status)) 141 { 142 if (!NT_SUCCESS(RtlUnicodeStringToInteger(&DebugValue, 10, &NewLevel))) 143 NewLevel = 0; 144 } 145 g_ShimEngDebugLevel = NewLevel; 146 } 147 148 149 /** 150 * Outputs diagnostic info. 151 * 152 * @param [in] Level The level to log this message with, choose any of [SHIM_ERR, 153 * SHIM_WARN, SHIM_INFO]. 154 * @param [in] FunctionName The function this log should be attributed to. 155 * @param [in] Format The format string. 156 * @param ... Variable arguments providing additional information. 157 * 158 * @return Success: TRUE Failure: FALSE. 159 */ 160 BOOL WINAPIV SeiDbgPrint(SEI_LOG_LEVEL Level, PCSTR Function, PCSTR Format, ...) 161 { 162 char Buffer[512]; 163 char* Current = Buffer; 164 const char* LevelStr; 165 size_t Length = sizeof(Buffer); 166 va_list ArgList; 167 HRESULT hr; 168 169 if (g_ShimEngDebugLevel == 0xffffffff) 170 SeiInitDebugSupport(); 171 172 if (Level > g_ShimEngDebugLevel) 173 return FALSE; 174 175 switch (Level) 176 { 177 case SEI_MSG: 178 LevelStr = "MSG "; 179 break; 180 case SEI_FAIL: 181 LevelStr = "FAIL"; 182 break; 183 case SEI_WARN: 184 LevelStr = "WARN"; 185 break; 186 case SEI_INFO: 187 LevelStr = "INFO"; 188 break; 189 default: 190 LevelStr = "USER"; 191 break; 192 } 193 194 if (Function) 195 hr = StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s] [%s] ", LevelStr, Function); 196 else 197 hr = StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s] ", LevelStr); 198 199 if (!SUCCEEDED(hr)) 200 return FALSE; 201 202 va_start(ArgList, Format); 203 hr = StringCchVPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, Format, ArgList); 204 va_end(ArgList); 205 if (!SUCCEEDED(hr)) 206 return FALSE; 207 208 DbgPrint("%s", Buffer); 209 return TRUE; 210 } 211 212 static 213 BOOL SeiIsOrdinalName(LPCSTR lpProcName) 214 { 215 return (ULONG_PTR)lpProcName <= MAXUSHORT; 216 } 217 218 LPCSTR SeiPrintFunctionName(LPCSTR lpProcName, char szOrdProcFmt[10]) 219 { 220 if (SeiIsOrdinalName(lpProcName)) 221 { 222 StringCchPrintfA(szOrdProcFmt, 10, "#%Iu", (ULONG_PTR)lpProcName); 223 return szOrdProcFmt; 224 } 225 return lpProcName; 226 } 227 228 int SeiCompareFunctionName(LPCSTR lpProcName1, LPCSTR lpProcName2) 229 { 230 BOOL Ord1 = SeiIsOrdinalName(lpProcName1); 231 BOOL Ord2 = SeiIsOrdinalName(lpProcName2); 232 233 /* One is an ordinal, the other not */ 234 if (Ord1 != Ord2) 235 return 1; 236 237 /* Compare ordinals */ 238 if (Ord1) 239 return (ULONG_PTR)lpProcName1 != (ULONG_PTR)lpProcName2; 240 241 /* Compare names */ 242 return strcmp(lpProcName1, lpProcName2); 243 } 244 245 246 PVOID SeiGetModuleFromAddress(PVOID addr) 247 { 248 PVOID hModule = NULL; 249 RtlPcToFileHeader(addr, &hModule); 250 return hModule; 251 } 252 253 254 /* TODO: Guard against recursive calling / calling init multiple times! */ 255 VOID NotifyShims(DWORD dwReason, PVOID Info) 256 { 257 DWORD n; 258 259 for (n = 0; n < ARRAY_Size(&g_pShimInfo); ++n) 260 { 261 PSHIMMODULE pShimModule = *ARRAY_At(&g_pShimInfo, PSHIMMODULE, n); 262 if (!pShimModule->pNotifyShims) 263 continue; 264 265 pShimModule->pNotifyShims(dwReason, Info); 266 } 267 } 268 269 270 271 VOID SeiCheckComPlusImage(PVOID BaseAddress) 272 { 273 ULONG ComSectionSize; 274 g_bComPlusImage = RtlImageDirectoryEntryToData(BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, &ComSectionSize) != NULL; 275 276 SHIMENG_INFO("COM+ executable %s\n", g_bComPlusImage ? "TRUE" : "FALSE"); 277 } 278 279 280 PSHIMMODULE SeiGetShimModuleInfo(PVOID BaseAddress) 281 { 282 DWORD n; 283 284 for (n = 0; n < ARRAY_Size(&g_pShimInfo); ++n) 285 { 286 PSHIMMODULE pShimModule = *ARRAY_At(&g_pShimInfo, PSHIMMODULE, n); 287 288 if (pShimModule->BaseAddress == BaseAddress) 289 return pShimModule; 290 } 291 return NULL; 292 } 293 294 PSHIMMODULE SeiCreateShimModuleInfo(PCWSTR DllName, PVOID BaseAddress) 295 { 296 static const ANSI_STRING GetHookAPIs = RTL_CONSTANT_STRING("GetHookAPIs"); 297 static const ANSI_STRING NotifyShims = RTL_CONSTANT_STRING("NotifyShims"); 298 PSHIMMODULE* pData, Data; 299 PVOID pGetHookAPIs, pNotifyShims; 300 301 if (!NT_SUCCESS(LdrGetProcedureAddress(BaseAddress, (PANSI_STRING)&GetHookAPIs, 0, &pGetHookAPIs)) || 302 !NT_SUCCESS(LdrGetProcedureAddress(BaseAddress, (PANSI_STRING)&NotifyShims, 0, &pNotifyShims))) 303 { 304 SHIMENG_WARN("Failed to resolve entry points for %S\n", DllName); 305 return NULL; 306 } 307 308 pData = ARRAY_Append(&g_pShimInfo, PSHIMMODULE); 309 if (!pData) 310 return NULL; 311 312 *pData = SeiAlloc(sizeof(SHIMMODULE)); 313 314 Data = *pData; 315 316 RtlCreateUnicodeString(&Data->Name, DllName); 317 Data->BaseAddress = BaseAddress; 318 319 Data->pGetHookAPIs = pGetHookAPIs; 320 Data->pNotifyShims = pNotifyShims; 321 322 ARRAY_Init(&Data->EnabledShims, PSHIMINFO); 323 324 return Data; 325 } 326 327 PSHIMINFO SeiAppendHookInfo(PSHIMMODULE pShimModuleInfo, PHOOKAPIEX pHookApi, DWORD dwHookCount, PCWSTR ShimName) 328 { 329 PSHIMINFO* pData, Data; 330 331 pData = ARRAY_Append(&pShimModuleInfo->EnabledShims, PSHIMINFO); 332 if (!pData) 333 return NULL; 334 335 *pData = SeiAlloc(sizeof(SHIMINFO)); 336 Data = *pData; 337 338 if (!Data) 339 return NULL; 340 341 Data->ShimName = SdbpStrDup(ShimName); 342 if (!Data->ShimName) 343 return NULL; 344 345 Data->pHookApi = pHookApi; 346 Data->dwHookCount = dwHookCount; 347 Data->pShimModule = pShimModuleInfo; 348 ARRAY_Init(&Data->InExclude, INEXCLUDE); 349 return Data; 350 } 351 352 PHOOKMODULEINFO SeiFindHookModuleInfo(PUNICODE_STRING ModuleName, PVOID BaseAddress) 353 { 354 DWORD n; 355 356 if (ModuleName == NULL && BaseAddress == NULL) 357 { 358 BaseAddress = NtCurrentPeb()->ImageBaseAddress; 359 } 360 361 for (n = 0; n < ARRAY_Size(&g_pHookArray); ++n) 362 { 363 PHOOKMODULEINFO pModuleInfo = ARRAY_At(&g_pHookArray, HOOKMODULEINFO, n); 364 365 if (BaseAddress && BaseAddress == pModuleInfo->BaseAddress) 366 return pModuleInfo; 367 368 if (!BaseAddress && RtlEqualUnicodeString(ModuleName, &pModuleInfo->Name, TRUE)) 369 return pModuleInfo; 370 } 371 372 return NULL; 373 } 374 375 PHOOKMODULEINFO SeiFindHookModuleInfoForImportDescriptor(PBYTE DllBase, PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor) 376 { 377 UNICODE_STRING DllName; 378 PVOID DllHandle; 379 NTSTATUS Success; 380 381 if (!RtlCreateUnicodeStringFromAsciiz(&DllName, (PCSZ)(DllBase + ImportDescriptor->Name))) 382 { 383 SHIMENG_FAIL("Unable to convert dll name to unicode\n"); 384 return NULL; 385 } 386 387 Success = LdrGetDllHandle(NULL, NULL, &DllName, &DllHandle); 388 389 if (!NT_SUCCESS(Success)) 390 { 391 SHIMENG_FAIL("Unable to get module handle for %wZ (%p)\n", &DllName, DllBase); 392 RtlFreeUnicodeString(&DllName); 393 394 return NULL; 395 } 396 RtlFreeUnicodeString(&DllName); 397 398 return SeiFindHookModuleInfo(NULL, DllHandle); 399 } 400 401 static LPCWSTR SeiGetStringPtr(PDB pdb, TAGID tag, TAG type) 402 { 403 TAGID tagEntry = SdbFindFirstTag(pdb, tag, type); 404 if (tagEntry == TAGID_NULL) 405 return NULL; 406 407 return SdbGetStringTagPtr(pdb, tagEntry); 408 } 409 410 static DWORD SeiGetDWORD(PDB pdb, TAGID tag, TAG type) 411 { 412 TAGID tagEntry = SdbFindFirstTag(pdb, tag, type); 413 if (tagEntry == TAGID_NULL) 414 return 0; 415 416 return SdbReadDWORDTag(pdb, tagEntry, 0); 417 } 418 419 static QWORD SeiGetQWORD(PDB pdb, TAGID tag, TAG type) 420 { 421 TAGID tagEntry = SdbFindFirstTag(pdb, tag, type); 422 if (tagEntry == TAGID_NULL) 423 return 0; 424 425 return SdbReadQWORDTag(pdb, tagEntry, 0); 426 } 427 428 static VOID SeiAddShim(TAGREF trShimRef, PARRAY pShimRef) 429 { 430 TAGREF* Data; 431 432 Data = ARRAY_Append(pShimRef, TAGREF); 433 if (!Data) 434 return; 435 436 *Data = trShimRef; 437 } 438 439 static VOID SeiAddFlag(PDB pdb, TAGID tiFlagRef, PFLAGINFO pFlagInfo) 440 { 441 ULARGE_INTEGER Flag; 442 443 /* Resolve the FLAG_REF to the real FLAG node */ 444 TAGID FlagTag = SeiGetDWORD(pdb, tiFlagRef, TAG_FLAG_TAGID); 445 446 if (FlagTag == TAGID_NULL) 447 return; 448 449 pFlagInfo->AppCompatFlags.QuadPart |= SeiGetQWORD(pdb, FlagTag, TAG_FLAG_MASK_KERNEL); 450 pFlagInfo->AppCompatFlagsUser.QuadPart |= SeiGetQWORD(pdb, FlagTag, TAG_FLAG_MASK_USER); 451 Flag.QuadPart = SeiGetQWORD(pdb, FlagTag, TAG_FLAG_PROCESSPARAM); 452 pFlagInfo->ProcessParameters_Flags |= Flag.LowPart; 453 } 454 455 /* Propagate layers to child processes */ 456 static VOID SeiSetLayerEnvVar(LPCWSTR wszLayer) 457 { 458 NTSTATUS Status; 459 UNICODE_STRING VarName = RTL_CONSTANT_STRING(L"__COMPAT_LAYER"); 460 UNICODE_STRING Value; 461 462 RtlInitUnicodeString(&Value, wszLayer); 463 464 Status = RtlSetEnvironmentVariable(NULL, &VarName, &Value); 465 if (NT_SUCCESS(Status)) 466 SHIMENG_INFO("%wZ=%wZ\n", &VarName, &Value); 467 else 468 SHIMENG_FAIL("Failed to set %wZ: 0x%x\n", &VarName, Status); 469 } 470 471 #define MAX_LAYER_LENGTH 256 472 473 /* Translate all Exe and Layer entries to Shims, and propagate all layers */ 474 static VOID SeiBuildShimRefArray(HSDB hsdb, SDBQUERYRESULT* pQuery, PARRAY pShimRef, PFLAGINFO pFlagInfo) 475 { 476 WCHAR wszLayerEnvVar[MAX_LAYER_LENGTH] = { 0 }; 477 DWORD n; 478 479 for (n = 0; n < pQuery->dwExeCount; ++n) 480 { 481 PDB pdb; 482 TAGID tag; 483 if (SdbTagRefToTagID(hsdb, pQuery->atrExes[n], &pdb, &tag)) 484 { 485 LPCWSTR ExeName = SeiGetStringPtr(pdb, tag, TAG_NAME); 486 TAGID ShimRef = SdbFindFirstTag(pdb, tag, TAG_SHIM_REF); 487 TAGID FlagRef = SdbFindFirstTag(pdb, tag, TAG_FLAG_REF); 488 489 if (ExeName) 490 SeiDbgPrint(SEI_MSG, NULL, "ShimInfo(Exe(%S))\n", ExeName); 491 492 while (ShimRef != TAGID_NULL) 493 { 494 TAGREF trShimRef; 495 if (SdbTagIDToTagRef(hsdb, pdb, ShimRef, &trShimRef)) 496 SeiAddShim(trShimRef, pShimRef); 497 498 ShimRef = SdbFindNextTag(pdb, tag, ShimRef); 499 } 500 501 while (FlagRef != TAGID_NULL) 502 { 503 SeiAddFlag(pdb, FlagRef, pFlagInfo); 504 505 FlagRef = SdbFindNextTag(pdb, tag, FlagRef); 506 } 507 } 508 } 509 510 511 for (n = 0; n < pQuery->dwLayerCount; ++n) 512 { 513 PDB pdb; 514 TAGID tag; 515 if (SdbTagRefToTagID(hsdb, pQuery->atrLayers[n], &pdb, &tag)) 516 { 517 LPCWSTR LayerName = SeiGetStringPtr(pdb, tag, TAG_NAME); 518 TAGID ShimRef = SdbFindFirstTag(pdb, tag, TAG_SHIM_REF); 519 TAGID FlagRef = SdbFindFirstTag(pdb, tag, TAG_FLAG_REF); 520 521 if (LayerName) 522 { 523 HRESULT hr; 524 SeiDbgPrint(SEI_MSG, NULL, "ShimInfo(Layer(%S))\n", LayerName); 525 if (wszLayerEnvVar[0]) 526 StringCchCatW(wszLayerEnvVar, ARRAYSIZE(wszLayerEnvVar), L" "); 527 hr = StringCchCatW(wszLayerEnvVar, ARRAYSIZE(wszLayerEnvVar), LayerName); 528 if (!SUCCEEDED(hr)) 529 { 530 SHIMENG_FAIL("Unable to append %S\n", LayerName); 531 } 532 } 533 534 while (ShimRef != TAGID_NULL) 535 { 536 TAGREF trShimRef; 537 if (SdbTagIDToTagRef(hsdb, pdb, ShimRef, &trShimRef)) 538 SeiAddShim(trShimRef, pShimRef); 539 540 ShimRef = SdbFindNextTag(pdb, tag, ShimRef); 541 } 542 543 while (FlagRef != TAGID_NULL) 544 { 545 SeiAddFlag(pdb, FlagRef, pFlagInfo); 546 547 FlagRef = SdbFindNextTag(pdb, tag, FlagRef); 548 } 549 } 550 } 551 if (wszLayerEnvVar[0]) 552 SeiSetLayerEnvVar(wszLayerEnvVar); 553 } 554 555 /* Given the hooks from one shim, find the relevant modules and store the combination of module + hook */ 556 VOID SeiAddHooks(PHOOKAPIEX hooks, DWORD dwHookCount, PSHIMINFO pShim) 557 { 558 DWORD n, j; 559 UNICODE_STRING UnicodeModName; 560 WCHAR Buf[512]; 561 562 RtlInitEmptyUnicodeString(&UnicodeModName, Buf, sizeof(Buf)); 563 564 for (n = 0; n < dwHookCount; ++n) 565 { 566 ANSI_STRING AnsiString; 567 PVOID DllHandle; 568 PHOOKAPIEX hook = hooks + n; 569 PHOOKAPIEX* pHookApi; 570 PHOOKMODULEINFO HookModuleInfo; 571 572 RtlInitAnsiString(&AnsiString, hook->LibraryName); 573 if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeModName, &AnsiString, FALSE))) 574 { 575 SHIMENG_FAIL("Unable to convert %s to Unicode\n", hook->LibraryName); 576 continue; 577 } 578 579 if (NT_SUCCESS(LdrGetDllHandle(NULL, 0, &UnicodeModName, &DllHandle))) 580 { 581 HookModuleInfo = SeiFindHookModuleInfo(NULL, DllHandle); 582 } 583 else 584 { 585 HookModuleInfo = SeiFindHookModuleInfo(&UnicodeModName, NULL); 586 DllHandle = NULL; 587 } 588 589 if (!HookModuleInfo) 590 { 591 HookModuleInfo = ARRAY_Append(&g_pHookArray, HOOKMODULEINFO); 592 if (!HookModuleInfo) 593 continue; 594 595 HookModuleInfo->BaseAddress = DllHandle; 596 ARRAY_Init(&HookModuleInfo->HookApis, PHOOKAPIEX); 597 RtlCreateUnicodeString(&HookModuleInfo->Name, UnicodeModName.Buffer); 598 } 599 600 hook->pShimInfo = pShim; 601 602 for (j = 0; j < ARRAY_Size(&HookModuleInfo->HookApis); ++j) 603 { 604 PHOOKAPIEX HookApi = *ARRAY_At(&HookModuleInfo->HookApis, PHOOKAPIEX, j); 605 int CmpResult = SeiCompareFunctionName(hook->FunctionName, HookApi->FunctionName); 606 if (CmpResult == 0) 607 { 608 while (HookApi->ApiLink) 609 { 610 HookApi = HookApi->ApiLink; 611 } 612 HookApi->ApiLink = hook; 613 hook = NULL; 614 break; 615 } 616 } 617 /* No place found yet, append it */ 618 if (hook) 619 { 620 pHookApi = ARRAY_Append(&HookModuleInfo->HookApis, PHOOKAPIEX); 621 if (pHookApi) 622 *pHookApi = hook; 623 } 624 } 625 } 626 627 /* Check if we should fake the return from GetProcAddress (because we also redirected the iat for this module) */ 628 FARPROC WINAPI StubGetProcAddress(HINSTANCE hModule, LPCSTR lpProcName) 629 { 630 PVOID Addr = _ReturnAddress(); 631 PHOOKMODULEINFO HookModuleInfo; 632 FARPROC proc = ((GETPROCADDRESSPROC)g_IntHookEx[0].OriginalFunction)(hModule, lpProcName); 633 char szOrdProcFmt[10]; 634 635 Addr = SeiGetModuleFromAddress(Addr); 636 if (SE_IsShimDll(Addr)) 637 { 638 SHIMENG_MSG("Not touching GetProcAddress for shim dll (%p!%s)", hModule, SeiPrintFunctionName(lpProcName, szOrdProcFmt)); 639 return proc; 640 } 641 642 SHIMENG_INFO("(GetProcAddress(%p!%s) => %p\n", hModule, SeiPrintFunctionName(lpProcName, szOrdProcFmt), proc); 643 644 HookModuleInfo = SeiFindHookModuleInfo(NULL, hModule); 645 646 if (HookModuleInfo) 647 { 648 DWORD n; 649 for (n = 0; n < ARRAY_Size(&HookModuleInfo->HookApis); ++n) 650 { 651 PHOOKAPIEX HookApi = *ARRAY_At(&HookModuleInfo->HookApis, PHOOKAPIEX, n); 652 int CmpResult = SeiCompareFunctionName(lpProcName, HookApi->FunctionName); 653 if (CmpResult == 0) 654 { 655 SHIMENG_MSG("Redirecting %p to %p\n", proc, HookApi->ReplacementFunction); 656 proc = HookApi->ReplacementFunction; 657 break; 658 } 659 } 660 } 661 662 return proc; 663 } 664 665 VOID SeiResolveAPI(PHOOKMODULEINFO HookModuleInfo) 666 { 667 DWORD n; 668 ANSI_STRING AnsiString; 669 670 ASSERT(HookModuleInfo->BaseAddress != NULL); 671 672 for (n = 0; n < ARRAY_Size(&HookModuleInfo->HookApis); ++n) 673 { 674 NTSTATUS Status; 675 PVOID ProcAddress; 676 PHOOKAPIEX HookApi = *ARRAY_At(&HookModuleInfo->HookApis, PHOOKAPIEX, n); 677 678 if (!SeiIsOrdinalName(HookApi->FunctionName)) 679 { 680 RtlInitAnsiString(&AnsiString, HookApi->FunctionName); 681 Status = LdrGetProcedureAddress(HookModuleInfo->BaseAddress, &AnsiString, 0, &ProcAddress); 682 } 683 else 684 { 685 Status = LdrGetProcedureAddress(HookModuleInfo->BaseAddress, NULL, (ULONG_PTR)HookApi->FunctionName, &ProcAddress); 686 } 687 688 if (!NT_SUCCESS(Status)) 689 { 690 char szOrdProcFmt[10]; 691 LPCSTR lpFunctionName = SeiPrintFunctionName(HookApi->FunctionName, szOrdProcFmt); 692 SHIMENG_FAIL("Unable to retrieve %s!%s\n", HookApi->LibraryName, lpFunctionName); 693 continue; 694 } 695 696 HookApi->OriginalFunction = ProcAddress; 697 if (HookApi->ApiLink) 698 { 699 SHIMENG_MSG("TODO: Figure out how to handle conflicting In/Exports with ApiLink!\n"); 700 } 701 while (HookApi->ApiLink) 702 { 703 HookApi->ApiLink->OriginalFunction = HookApi->OriginalFunction; 704 HookApi->OriginalFunction = HookApi->ApiLink->ReplacementFunction; 705 HookApi = HookApi->ApiLink; 706 } 707 } 708 } 709 710 /* Walk all shim modules / enabled shims, and add their hooks */ 711 VOID SeiResolveAPIs(VOID) 712 { 713 DWORD n; 714 715 for (n = 0; n < ARRAY_Size(&g_pHookArray); ++n) 716 { 717 PHOOKMODULEINFO pModuleInfo = ARRAY_At(&g_pHookArray, HOOKMODULEINFO, n); 718 719 /* Is this module loaded? */ 720 if (pModuleInfo->BaseAddress) 721 { 722 SeiResolveAPI(pModuleInfo); 723 } 724 } 725 } 726 727 VOID SeiCombineHookInfo(VOID) 728 { 729 DWORD mod, n; 730 731 /* Enumerate all Shim modules */ 732 for (mod = 0; mod < ARRAY_Size(&g_pShimInfo); ++mod) 733 { 734 PSHIMMODULE pShimModule = *ARRAY_At(&g_pShimInfo, PSHIMMODULE, mod); 735 DWORD dwShimCount = ARRAY_Size(&pShimModule->EnabledShims); 736 737 /* Enumerate all Shims */ 738 for (n = 0; n < dwShimCount; ++n) 739 { 740 PSHIMINFO pShim = *ARRAY_At(&pShimModule->EnabledShims, PSHIMINFO, n); 741 742 PHOOKAPIEX hooks = pShim->pHookApi; 743 DWORD dwHookCount = pShim->dwHookCount; 744 745 SeiAddHooks(hooks, dwHookCount, pShim); 746 } 747 } 748 } 749 750 /* If we hooked something, we should also redirect GetProcAddress */ 751 VOID SeiAddInternalHooks(DWORD dwNumHooks) 752 { 753 if (dwNumHooks == 0) 754 { 755 g_bInternalHooksUsed = FALSE; 756 return; 757 } 758 759 SeiAddHooks(g_IntHookEx, ARRAYSIZE(g_IntHookEx), NULL); 760 g_bInternalHooksUsed = TRUE; 761 } 762 763 /* Patch one function in the iat */ 764 VOID SeiPatchNewImport(PIMAGE_THUNK_DATA FirstThunk, PHOOKAPIEX HookApi, PLDR_DATA_TABLE_ENTRY LdrEntry) 765 { 766 ULONG OldProtection = 0; 767 PVOID Ptr; 768 SIZE_T Size; 769 NTSTATUS Status; 770 char szOrdProcFmt[10]; 771 772 SHIMENG_INFO("Hooking API \"%s!%s\" for DLL \"%wZ\"\n", HookApi->LibraryName, SeiPrintFunctionName(HookApi->FunctionName, szOrdProcFmt), &LdrEntry->BaseDllName); 773 774 Ptr = &FirstThunk->u1.Function; 775 Size = sizeof(FirstThunk->u1.Function); 776 Status = NtProtectVirtualMemory(NtCurrentProcess(), &Ptr, &Size, PAGE_EXECUTE_READWRITE, &OldProtection); 777 778 if (!NT_SUCCESS(Status)) 779 { 780 SHIMENG_FAIL("Unable to unprotect 0x%p\n", &FirstThunk->u1.Function); 781 return; 782 } 783 784 SHIMENG_INFO("changing 0x%p to 0x%p\n", FirstThunk->u1.Function, HookApi->ReplacementFunction); 785 FirstThunk->u1.Function = (ULONG_PTR)HookApi->ReplacementFunction; 786 787 Size = sizeof(FirstThunk->u1.Function); 788 Status = NtProtectVirtualMemory(NtCurrentProcess(), &Ptr, &Size, OldProtection, &OldProtection); 789 790 if (!NT_SUCCESS(Status)) 791 { 792 SHIMENG_WARN("Unable to reprotect 0x%p\n", &FirstThunk->u1.Function); 793 } 794 } 795 796 797 PINEXCLUDE SeiFindInExclude(PARRAY InExclude, PCUNICODE_STRING DllName) 798 { 799 DWORD n; 800 801 for (n = 0; n < ARRAY_Size(InExclude); ++n) 802 { 803 PINEXCLUDE InEx = ARRAY_At(InExclude, INEXCLUDE, n); 804 805 if (RtlEqualUnicodeString(&InEx->Module, DllName, TRUE)) 806 return InEx; 807 } 808 809 return NULL; 810 } 811 812 BOOL SeiIsExcluded(PLDR_DATA_TABLE_ENTRY LdrEntry, PHOOKAPIEX HookApi) 813 { 814 PSHIMINFO pShimInfo = HookApi->pShimInfo; 815 PINEXCLUDE InExclude; 816 BOOL IsExcluded = FALSE; 817 char szOrdProcFmt[10]; 818 819 if (!pShimInfo) 820 { 821 /* Internal hook, do not exclude it */ 822 return FALSE; 823 } 824 825 /* By default, everything from System32 or WinSxs is excluded */ 826 if (RtlPrefixUnicodeString(&g_System32Directory, &LdrEntry->FullDllName, TRUE) || 827 RtlPrefixUnicodeString(&g_SxsDirectory, &LdrEntry->FullDllName, TRUE)) 828 IsExcluded = TRUE; 829 830 InExclude = SeiFindInExclude(&pShimInfo->InExclude, &LdrEntry->BaseDllName); 831 if (InExclude) 832 { 833 /* If it is on the 'exclude' list, bail out */ 834 if (!InExclude->Include) 835 { 836 SHIMENG_INFO("Module '%wZ' excluded for shim %S, API '%s!%s', because it on in the exclude list.\n", 837 &LdrEntry->BaseDllName, pShimInfo->ShimName, HookApi->LibraryName, SeiPrintFunctionName(HookApi->FunctionName, szOrdProcFmt)); 838 839 return TRUE; 840 } 841 /* If it is on the 'include' list, override System32 / Winsxs check. */ 842 if (IsExcluded) 843 { 844 SHIMENG_INFO("Module '%wZ' included for shim %S, API '%s!%s', because it is on the include list.\n", 845 &LdrEntry->BaseDllName, pShimInfo->ShimName, HookApi->LibraryName, SeiPrintFunctionName(HookApi->FunctionName, szOrdProcFmt)); 846 847 } 848 IsExcluded = FALSE; 849 } 850 851 if (IsExcluded) 852 { 853 SHIMENG_INFO("Module '%wZ' excluded for shim %S, API '%s!%s', because it is in System32/WinSXS.\n", 854 &LdrEntry->BaseDllName, pShimInfo->ShimName, HookApi->LibraryName, SeiPrintFunctionName(HookApi->FunctionName, szOrdProcFmt)); 855 } 856 857 return IsExcluded; 858 } 859 860 VOID SeiAppendInExclude(PARRAY dest, PCWSTR ModuleName, BOOL IsInclude) 861 { 862 PINEXCLUDE InExclude; 863 UNICODE_STRING ModuleNameU; 864 RtlInitUnicodeString(&ModuleNameU, ModuleName); 865 866 InExclude = SeiFindInExclude(dest, &ModuleNameU); 867 if (InExclude) 868 { 869 InExclude->Include = IsInclude; 870 return; 871 } 872 873 InExclude = ARRAY_Append(dest, INEXCLUDE); 874 if (InExclude) 875 { 876 PCWSTR ModuleNameCopy = SdbpStrDup(ModuleName); 877 RtlInitUnicodeString(&InExclude->Module, ModuleNameCopy); 878 InExclude->Include = IsInclude; 879 } 880 } 881 882 /* Read the INEXCLUD tags from a given parent tag 883 FIXME: 884 Some observed tags: 885 '*' with include 886 '$' with include, followed by '*' without include 887 Include list logging, referring to: (MODE: EA) 888 */ 889 VOID SeiReadInExclude(PDB pdb, TAGID parent, PARRAY dest) 890 { 891 TAGID InExcludeTag; 892 893 InExcludeTag = SdbFindFirstTag(pdb, parent, TAG_INEXCLUD); 894 895 while (InExcludeTag != TAGID_NULL) 896 { 897 PCWSTR ModuleName; 898 TAGID ModuleTag = SdbFindFirstTag(pdb, InExcludeTag, TAG_MODULE); 899 TAGID IncludeTag = SdbFindFirstTag(pdb, InExcludeTag, TAG_INCLUDE); 900 901 ModuleName = SdbGetStringTagPtr(pdb, ModuleTag); 902 if (ModuleName) 903 { 904 SeiAppendInExclude(dest, ModuleName, IncludeTag != TAGID_NULL); 905 } 906 else 907 { 908 SHIMENG_WARN("INEXCLUDE without Module: 0x%x\n", InExcludeTag); 909 } 910 911 InExcludeTag = SdbFindNextTag(pdb, parent, InExcludeTag); 912 } 913 } 914 915 VOID SeiBuildGlobalInclExclList(HSDB hsdb) 916 { 917 PDB pdb; 918 TAGREF tr = TAGREF_ROOT; 919 TAGID root, db, library; 920 921 if (!SdbTagRefToTagID(hsdb, tr, &pdb, &root)) 922 { 923 SHIMENG_WARN("Unable to resolve database root\n"); 924 return; 925 } 926 db = SdbFindFirstTag(pdb, root, TAG_DATABASE); 927 if (db == TAGID_NULL) 928 { 929 SHIMENG_WARN("Unable to resolve database\n"); 930 return; 931 } 932 library = SdbFindFirstTag(pdb, db, TAG_LIBRARY); 933 if (library == TAGID_NULL) 934 { 935 SHIMENG_WARN("Unable to resolve library\n"); 936 return; 937 } 938 939 SeiReadInExclude(pdb, library, &g_InExclude); 940 } 941 942 VOID SeiBuildInclExclList(PDB pdb, TAGID ShimTag, PSHIMINFO pShimInfo) 943 { 944 DWORD n; 945 946 /* First duplicate the global in/excludes */ 947 for (n = 0; n < ARRAY_Size(&g_InExclude); ++n) 948 { 949 PINEXCLUDE InEx = ARRAY_At(&g_InExclude, INEXCLUDE, n); 950 SeiAppendInExclude(&pShimInfo->InExclude, InEx->Module.Buffer, InEx->Include); 951 } 952 953 /* Now read this shim's in/excludes (possibly overriding the global ones) */ 954 SeiReadInExclude(pdb, ShimTag, &pShimInfo->InExclude); 955 } 956 957 /* Given one loaded module, redirect (hook) all functions from the iat that are registered by shims */ 958 VOID SeiHookImports(PLDR_DATA_TABLE_ENTRY LdrEntry) 959 { 960 ULONG Size; 961 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor; 962 PBYTE DllBase = LdrEntry->DllBase; 963 964 if (SE_IsShimDll(DllBase) || 965 g_hInstance == LdrEntry->DllBase || 966 RtlEqualUnicodeString(&g_LoadingShimDll, &LdrEntry->BaseDllName, TRUE)) 967 { 968 SHIMENG_INFO("Skipping shim module 0x%p \"%wZ\"\n", LdrEntry->DllBase, &LdrEntry->BaseDllName); 969 return; 970 } 971 972 if (LdrEntry->Flags & LDRP_COMPAT_DATABASE_PROCESSED) 973 { 974 SHIMENG_INFO("Skipping module 0x%p \"%wZ\" because it was already processed\n", LdrEntry->DllBase, &LdrEntry->BaseDllName); 975 return; 976 } 977 978 ImportDescriptor = RtlImageDirectoryEntryToData(DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &Size); 979 if (!ImportDescriptor) 980 { 981 SHIMENG_INFO("Skipping module 0x%p \"%wZ\" due to no iat found\n", LdrEntry->DllBase, &LdrEntry->BaseDllName); 982 return; 983 } 984 985 SHIMENG_INFO("Hooking module 0x%p \"%wZ\"\n", LdrEntry->DllBase, &LdrEntry->BaseDllName); 986 987 for ( ;ImportDescriptor->Name && ImportDescriptor->OriginalFirstThunk; ImportDescriptor++) 988 { 989 PHOOKMODULEINFO HookModuleInfo; 990 991 /* Do we have hooks for this module? */ 992 HookModuleInfo = SeiFindHookModuleInfoForImportDescriptor(DllBase, ImportDescriptor); 993 994 if (HookModuleInfo) 995 { 996 PIMAGE_THUNK_DATA OriginalThunk, FirstThunk; 997 DWORD n; 998 999 for (n = 0; n < ARRAY_Size(&HookModuleInfo->HookApis); ++n) 1000 { 1001 DWORD dwFound = 0; 1002 PHOOKAPIEX HookApi = *ARRAY_At(&HookModuleInfo->HookApis, PHOOKAPIEX, n); 1003 1004 /* Check if this module should be excluded from being hooked (system32/winsxs, global or shim exclude) */ 1005 if (SeiIsExcluded(LdrEntry, HookApi)) 1006 { 1007 continue; 1008 } 1009 1010 OriginalThunk = (PIMAGE_THUNK_DATA)(DllBase + ImportDescriptor->OriginalFirstThunk); 1011 FirstThunk = (PIMAGE_THUNK_DATA)(DllBase + ImportDescriptor->FirstThunk); 1012 1013 /* Walk all imports */ 1014 for (;OriginalThunk->u1.AddressOfData && FirstThunk->u1.Function; OriginalThunk++, FirstThunk++) 1015 { 1016 if (!IMAGE_SNAP_BY_ORDINAL(OriginalThunk->u1.Function)) 1017 { 1018 if (!SeiIsOrdinalName(HookApi->FunctionName)) 1019 { 1020 PIMAGE_IMPORT_BY_NAME ImportName; 1021 1022 ImportName = (PIMAGE_IMPORT_BY_NAME)(DllBase + OriginalThunk->u1.Function); 1023 if (!strcmp((PCSTR)ImportName->Name, HookApi->FunctionName)) 1024 { 1025 SeiPatchNewImport(FirstThunk, HookApi, LdrEntry); 1026 1027 /* Sadly, iat does not have to be sorted, and can even contain duplicate entries. */ 1028 dwFound++; 1029 } 1030 } 1031 } 1032 else 1033 { 1034 if (SeiIsOrdinalName(HookApi->FunctionName)) 1035 { 1036 if ((PCSTR)IMAGE_ORDINAL(OriginalThunk->u1.Function) == HookApi->FunctionName) 1037 { 1038 SeiPatchNewImport(FirstThunk, HookApi, LdrEntry); 1039 dwFound++; 1040 } 1041 } 1042 } 1043 } 1044 1045 if (dwFound != 1) 1046 { 1047 char szOrdProcFmt[10]; 1048 LPCSTR FuncName = SeiPrintFunctionName(HookApi->FunctionName, szOrdProcFmt); 1049 1050 /* One entry not found. */ 1051 if (!dwFound) 1052 SHIMENG_INFO("Entry \"%s!%s\" not found for \"%wZ\"\n", HookApi->LibraryName, FuncName, &LdrEntry->BaseDllName); 1053 else 1054 SHIMENG_INFO("Entry \"%s!%s\" found %d times for \"%wZ\"\n", HookApi->LibraryName, FuncName, dwFound, &LdrEntry->BaseDllName); 1055 } 1056 } 1057 } 1058 } 1059 1060 /* Mark this module as processed. */ 1061 LdrEntry->Flags |= LDRP_COMPAT_DATABASE_PROCESSED; 1062 } 1063 1064 1065 VOID PatchNewModules(PPEB Peb) 1066 { 1067 PLIST_ENTRY ListHead, ListEntry; 1068 PLDR_DATA_TABLE_ENTRY LdrEntry; 1069 1070 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; 1071 ListEntry = ListHead->Flink; 1072 1073 while (ListHead != ListEntry) 1074 { 1075 LdrEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); 1076 SeiHookImports(LdrEntry); 1077 1078 ListEntry = ListEntry->Flink; 1079 } 1080 } 1081 1082 1083 VOID SeiInitPaths(VOID) 1084 { 1085 #define SYSTEM32 L"\\system32" 1086 #define WINSXS L"\\winsxs" 1087 1088 PWSTR WindowsDirectory = SdbpStrDup(SharedUserData->NtSystemRoot); 1089 RtlInitUnicodeString(&g_WindowsDirectory, WindowsDirectory); 1090 1091 g_System32Directory.MaximumLength = g_WindowsDirectory.Length + SdbpStrsize(SYSTEM32); 1092 g_System32Directory.Buffer = SdbpAlloc(g_System32Directory.MaximumLength); 1093 RtlCopyUnicodeString(&g_System32Directory, &g_WindowsDirectory); 1094 RtlAppendUnicodeToString(&g_System32Directory, SYSTEM32); 1095 1096 g_SxsDirectory.MaximumLength = g_WindowsDirectory.Length + SdbpStrsize(WINSXS); 1097 g_SxsDirectory.Buffer = SdbpAlloc(g_SxsDirectory.MaximumLength); 1098 RtlCopyUnicodeString(&g_SxsDirectory, &g_WindowsDirectory); 1099 RtlAppendUnicodeToString(&g_SxsDirectory, WINSXS); 1100 1101 #undef SYSTEM32 1102 #undef WINSXS 1103 } 1104 1105 VOID SeiSetEntryProcessed(PPEB Peb) 1106 { 1107 PLIST_ENTRY ListHead, Entry; 1108 PLDR_DATA_TABLE_ENTRY LdrEntry; 1109 1110 ListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList; 1111 Entry = ListHead->Flink; 1112 while (Entry != ListHead) 1113 { 1114 LdrEntry = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks); 1115 Entry = Entry->Flink; 1116 1117 if (RtlEqualUnicodeString(&LdrEntry->BaseDllName, &Ntdll, TRUE) || 1118 RtlEqualUnicodeString(&LdrEntry->BaseDllName, &Kernel32, TRUE) || 1119 RtlEqualUnicodeString(&LdrEntry->BaseDllName, &Verifier, TRUE) || 1120 RtlEqualUnicodeString(&LdrEntry->BaseDllName, &g_LoadingShimDll, TRUE) || 1121 SE_IsShimDll(LdrEntry->DllBase) || 1122 (LdrEntry->Flags & LDRP_ENTRY_PROCESSED)) 1123 { 1124 SHIMENG_WARN("Don't mess with 0x%p '%wZ'\n", LdrEntry->DllBase, &LdrEntry->BaseDllName); 1125 } 1126 else 1127 { 1128 SHIMENG_WARN("Touching 0x%p '%wZ'\n", LdrEntry->DllBase, &LdrEntry->BaseDllName); 1129 LdrEntry->Flags |= (LDRP_ENTRY_PROCESSED | LDRP_SHIMENG_SUPPRESSED_ENTRY); 1130 } 1131 } 1132 1133 ListHead = &NtCurrentPeb()->Ldr->InMemoryOrderModuleList; 1134 Entry = ListHead->Flink; 1135 SHIMENG_INFO("In memory:\n"); 1136 while (Entry != ListHead) 1137 { 1138 LdrEntry = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); 1139 Entry = Entry->Flink; 1140 1141 SHIMENG_INFO(" 0x%p '%wZ'\n", LdrEntry->DllBase, &LdrEntry->BaseDllName); 1142 } 1143 1144 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; 1145 Entry = ListHead->Flink; 1146 SHIMENG_INFO("In load:\n"); 1147 while (Entry != ListHead) 1148 { 1149 LdrEntry = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); 1150 Entry = Entry->Flink; 1151 1152 SHIMENG_INFO(" 0x%p '%wZ'\n", LdrEntry->DllBase, &LdrEntry->BaseDllName); 1153 } 1154 } 1155 1156 VOID SeiResetEntryProcessed(PPEB Peb) 1157 { 1158 PLIST_ENTRY ListHead, Entry; 1159 PLDR_DATA_TABLE_ENTRY LdrEntry; 1160 1161 ListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList; 1162 Entry = ListHead->Flink; 1163 while (Entry != ListHead) 1164 { 1165 LdrEntry = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks); 1166 Entry = Entry->Flink; 1167 1168 if (SE_IsShimDll(LdrEntry->DllBase) || 1169 g_hInstance == LdrEntry->DllBase || 1170 RtlEqualUnicodeString(&LdrEntry->BaseDllName, &Ntdll, TRUE) || 1171 RtlEqualUnicodeString(&LdrEntry->BaseDllName, &Kernel32, TRUE) || 1172 RtlEqualUnicodeString(&LdrEntry->BaseDllName, &Verifier, TRUE) || 1173 !(LdrEntry->Flags & LDRP_SHIMENG_SUPPRESSED_ENTRY)) 1174 { 1175 SHIMENG_WARN("Don't mess with 0x%p '%wZ'\n", LdrEntry->DllBase, &LdrEntry->BaseDllName); 1176 } 1177 else 1178 { 1179 SHIMENG_WARN("Resetting 0x%p '%wZ'\n", LdrEntry->DllBase, &LdrEntry->BaseDllName); 1180 LdrEntry->Flags &= ~(LDRP_ENTRY_PROCESSED | LDRP_SHIMENG_SUPPRESSED_ENTRY); 1181 } 1182 } 1183 } 1184 1185 VOID SeiInit(LPCWSTR ProcessImage, HSDB hsdb, SDBQUERYRESULT* pQuery, BOOLEAN ProcessInit) 1186 { 1187 DWORD n; 1188 ARRAY ShimRefArray; 1189 DWORD dwTotalHooks = 0; 1190 FLAGINFO ShimFlags; 1191 1192 PPEB Peb = NtCurrentPeb(); 1193 1194 /* We should only be called once! */ 1195 ASSERT(g_pShimInfo.ItemSize__ == 0); 1196 1197 ARRAY_Init(&ShimRefArray, TAGREF); 1198 ARRAY_Init(&g_pShimInfo, PSHIMMODULE); 1199 ARRAY_Init(&g_pHookArray, HOOKMODULEINFO); 1200 ARRAY_Init(&g_InExclude, INEXCLUDE); 1201 RtlZeroMemory(&ShimFlags, sizeof(ShimFlags)); 1202 1203 SeiInitPaths(); 1204 1205 SeiCheckComPlusImage(Peb->ImageBaseAddress); 1206 1207 if (ProcessInit) 1208 { 1209 /* Mark all modules loaded until now as 'LDRP_ENTRY_PROCESSED' so that their entrypoint is not called while we are loading shims */ 1210 SeiSetEntryProcessed(Peb); 1211 } 1212 1213 /* TODO: 1214 if (pQuery->trApphelp) 1215 SeiDisplayAppHelp(?pQuery->trApphelp?); 1216 */ 1217 1218 SeiDbgPrint(SEI_MSG, NULL, "ShimInfo(ExePath(%S))\n", ProcessImage); 1219 SeiBuildShimRefArray(hsdb, pQuery, &ShimRefArray, &ShimFlags); 1220 if (ShimFlags.AppCompatFlags.QuadPart) 1221 { 1222 SeiDbgPrint(SEI_MSG, NULL, "Using KERNEL apphack flags 0x%I64x\n", ShimFlags.AppCompatFlags.QuadPart); 1223 Peb->AppCompatFlags.QuadPart |= ShimFlags.AppCompatFlags.QuadPart; 1224 } 1225 if (ShimFlags.AppCompatFlagsUser.QuadPart) 1226 { 1227 SeiDbgPrint(SEI_MSG, NULL, "Using USER apphack flags 0x%I64x\n", ShimFlags.AppCompatFlagsUser.QuadPart); 1228 Peb->AppCompatFlagsUser.QuadPart |= ShimFlags.AppCompatFlagsUser.QuadPart; 1229 } 1230 if (ShimFlags.ProcessParameters_Flags) 1231 { 1232 SeiDbgPrint(SEI_MSG, NULL, "Using ProcessParameters flags 0x%x\n", ShimFlags.ProcessParameters_Flags); 1233 Peb->ProcessParameters->Flags |= ShimFlags.ProcessParameters_Flags; 1234 } 1235 SeiDbgPrint(SEI_MSG, NULL, "ShimInfo(Complete)\n"); 1236 1237 SHIMENG_INFO("Got %d shims\n", ARRAY_Size(&ShimRefArray)); 1238 SeiBuildGlobalInclExclList(hsdb); 1239 1240 /* Walk all shims referenced (in layers + exes), and load their modules */ 1241 for (n = 0; n < ARRAY_Size(&ShimRefArray); ++n) 1242 { 1243 PDB pdb; 1244 TAGID ShimRef; 1245 1246 TAGREF tr = *ARRAY_At(&ShimRefArray, TAGREF, n); 1247 1248 if (SdbTagRefToTagID(hsdb, tr, &pdb, &ShimRef)) 1249 { 1250 LPCWSTR ShimName, DllName, CommandLine = NULL; 1251 TAGID ShimTag; 1252 WCHAR FullNameBuffer[MAX_PATH]; 1253 UNICODE_STRING UnicodeDllName; 1254 PVOID BaseAddress; 1255 PSHIMMODULE pShimModuleInfo = NULL; 1256 ANSI_STRING AnsiCommandLine = RTL_CONSTANT_STRING(""); 1257 PSHIMINFO pShimInfo = NULL; 1258 PHOOKAPIEX pHookApi; 1259 DWORD dwHookCount; 1260 1261 ShimName = SeiGetStringPtr(pdb, ShimRef, TAG_NAME); 1262 if (!ShimName) 1263 { 1264 SHIMENG_FAIL("Failed to retrieve the name for 0x%x\n", tr); 1265 continue; 1266 } 1267 1268 CommandLine = SeiGetStringPtr(pdb, ShimRef, TAG_COMMAND_LINE); 1269 if (CommandLine && *CommandLine) 1270 { 1271 RtlInitUnicodeString(&UnicodeDllName, CommandLine); 1272 if (NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiCommandLine, &UnicodeDllName, TRUE))) 1273 { 1274 SHIMENG_INFO("COMMAND LINE %s for %S", AnsiCommandLine.Buffer, ShimName); 1275 } 1276 else 1277 { 1278 AnsiCommandLine.Buffer = ""; 1279 CommandLine = NULL; 1280 } 1281 } 1282 1283 ShimTag = SeiGetDWORD(pdb, ShimRef, TAG_SHIM_TAGID); 1284 if (!ShimTag) 1285 { 1286 SHIMENG_FAIL("Failed to resolve %S to a shim\n", ShimName); 1287 continue; 1288 } 1289 1290 if (!SUCCEEDED(SdbGetAppPatchDir(NULL, FullNameBuffer, ARRAYSIZE(FullNameBuffer)))) 1291 { 1292 SHIMENG_WARN("Failed to get the AppPatch dir\n"); 1293 continue; 1294 } 1295 1296 DllName = SeiGetStringPtr(pdb, ShimTag, TAG_DLLFILE); 1297 if (DllName == NULL || 1298 !SUCCEEDED(StringCchCatW(FullNameBuffer, ARRAYSIZE(FullNameBuffer), L"\\")) || 1299 !SUCCEEDED(StringCchCatW(FullNameBuffer, ARRAYSIZE(FullNameBuffer), DllName))) 1300 { 1301 SHIMENG_WARN("Failed to build a full path for %S\n", ShimName); 1302 continue; 1303 } 1304 1305 RtlInitUnicodeString(&g_LoadingShimDll, DllName); 1306 RtlInitUnicodeString(&UnicodeDllName, FullNameBuffer); 1307 if (NT_SUCCESS(LdrGetDllHandle(NULL, NULL, &UnicodeDllName, &BaseAddress))) 1308 { 1309 /* This shim dll was already loaded, let's find it */ 1310 pShimModuleInfo = SeiGetShimModuleInfo(BaseAddress); 1311 } 1312 else if (!NT_SUCCESS(LdrLoadDll(NULL, NULL, &UnicodeDllName, &BaseAddress))) 1313 { 1314 SHIMENG_WARN("Failed to load %wZ for %S\n", &UnicodeDllName, ShimName); 1315 continue; 1316 } 1317 RtlInitUnicodeString(&g_LoadingShimDll, NULL); 1318 /* No shim module found (or we just loaded it) */ 1319 if (!pShimModuleInfo) 1320 { 1321 pShimModuleInfo = SeiCreateShimModuleInfo(DllName, BaseAddress); 1322 if (!pShimModuleInfo) 1323 { 1324 SHIMENG_FAIL("Failed to allocate ShimInfo for %S\n", DllName); 1325 continue; 1326 } 1327 } 1328 1329 SHIMENG_INFO("Shim DLL 0x%p \"%wZ\" loaded\n", BaseAddress, &UnicodeDllName); 1330 SHIMENG_INFO("Using SHIM \"%S!%S\"\n", DllName, ShimName); 1331 1332 /* Ask this shim what hooks it needs (and pass along the commandline) */ 1333 dwHookCount = 0; 1334 pHookApi = pShimModuleInfo->pGetHookAPIs(AnsiCommandLine.Buffer, ShimName, &dwHookCount); 1335 SHIMENG_INFO("GetHookAPIs returns %d hooks for DLL \"%wZ\" SHIM \"%S\"\n", dwHookCount, &UnicodeDllName, ShimName); 1336 if (dwHookCount && pHookApi) 1337 pShimInfo = SeiAppendHookInfo(pShimModuleInfo, pHookApi, dwHookCount, ShimName); 1338 else 1339 dwHookCount = 0; 1340 1341 /* If this shim has hooks, create the include / exclude lists */ 1342 if (pShimInfo) 1343 SeiBuildInclExclList(pdb, ShimTag, pShimInfo); 1344 1345 if (CommandLine && *CommandLine) 1346 RtlFreeAnsiString(&AnsiCommandLine); 1347 1348 dwTotalHooks += dwHookCount; 1349 } 1350 } 1351 1352 SeiAddInternalHooks(dwTotalHooks); 1353 SeiCombineHookInfo(); 1354 SeiResolveAPIs(); 1355 PatchNewModules(Peb); 1356 1357 if (ProcessInit) 1358 { 1359 /* Remove the 'LDRP_ENTRY_PROCESSED' flag from entries we modified, so that the loader can continue to process them */ 1360 SeiResetEntryProcessed(Peb); 1361 } 1362 g_bShimEngInitialized = TRUE; 1363 } 1364 1365 1366 /* Load the database + unpack the shim data (if this process is allowed) */ 1367 BOOL SeiGetShimData(PUNICODE_STRING ProcessImage, PVOID pShimData, HSDB* pHsdb, SDBQUERYRESULT* pQuery) 1368 { 1369 static const UNICODE_STRING ForbiddenShimmingApps[] = { 1370 RTL_CONSTANT_STRING(L"ntsd.exe"), 1371 RTL_CONSTANT_STRING(L"windbg.exe"), 1372 #if WINVER >= 0x600 1373 RTL_CONSTANT_STRING(L"slsvc.exe"), 1374 #endif 1375 }; 1376 static const UNICODE_STRING PathDividerFind = RTL_CONSTANT_STRING(L"\\/"); 1377 UNICODE_STRING ProcessName; 1378 USHORT PathDivider; 1379 HSDB hsdb; 1380 DWORD n; 1381 1382 if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, ProcessImage, &PathDividerFind, &PathDivider))) 1383 PathDivider = 0; 1384 1385 if (PathDivider) 1386 PathDivider += sizeof(WCHAR); 1387 1388 ProcessName.Buffer = ProcessImage->Buffer + PathDivider / sizeof(WCHAR); 1389 ProcessName.Length = ProcessImage->Length - PathDivider; 1390 ProcessName.MaximumLength = ProcessImage->MaximumLength - PathDivider; 1391 1392 for (n = 0; n < ARRAYSIZE(ForbiddenShimmingApps); ++n) 1393 { 1394 if (RtlEqualUnicodeString(&ProcessName, ForbiddenShimmingApps + n, TRUE)) 1395 { 1396 SHIMENG_MSG("Not shimming %wZ\n", ForbiddenShimmingApps + n); 1397 return FALSE; 1398 } 1399 } 1400 1401 /* We should probably load all db's here, but since we do not support that yet... */ 1402 hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL); 1403 if (hsdb) 1404 { 1405 if (SdbUnpackAppCompatData(hsdb, ProcessImage->Buffer, pShimData, pQuery)) 1406 { 1407 *pHsdb = hsdb; 1408 return TRUE; 1409 } 1410 SdbReleaseDatabase(hsdb); 1411 } 1412 return FALSE; 1413 } 1414 1415 1416 1417 VOID NTAPI SE_InstallBeforeInit(PUNICODE_STRING ProcessImage, PVOID pShimData) 1418 { 1419 HSDB hsdb = NULL; 1420 SDBQUERYRESULT QueryResult = { { 0 } }; 1421 SHIMENG_INFO("(%wZ, %p)\n", ProcessImage, pShimData); 1422 1423 if (!SeiGetShimData(ProcessImage, pShimData, &hsdb, &QueryResult)) 1424 { 1425 SHIMENG_FAIL("Failed to get shim data\n"); 1426 return; 1427 } 1428 1429 g_bShimDuringInit = TRUE; 1430 SeiInit(ProcessImage->Buffer, hsdb, &QueryResult, TRUE); 1431 g_bShimDuringInit = FALSE; 1432 1433 SdbReleaseDatabase(hsdb); 1434 } 1435 1436 VOID NTAPI SE_InstallAfterInit(PUNICODE_STRING ProcessImage, PVOID pShimData) 1437 { 1438 NotifyShims(SHIM_NOTIFY_ATTACH, NULL); 1439 } 1440 1441 VOID NTAPI SE_ProcessDying(VOID) 1442 { 1443 SHIMENG_MSG("()\n"); 1444 NotifyShims(SHIM_NOTIFY_DETACH, NULL); 1445 } 1446 1447 VOID WINAPI SE_DllLoaded(PLDR_DATA_TABLE_ENTRY LdrEntry) 1448 { 1449 PHOOKMODULEINFO HookModuleInfo; 1450 SHIMENG_INFO("%sINIT. loading DLL \"%wZ\"\n", g_bShimDuringInit ? "" : "AFTER ", &LdrEntry->BaseDllName); 1451 1452 HookModuleInfo = SeiFindHookModuleInfo(&LdrEntry->BaseDllName, NULL); 1453 if (HookModuleInfo) 1454 { 1455 ASSERT(HookModuleInfo->BaseAddress == NULL); 1456 HookModuleInfo->BaseAddress = LdrEntry->DllBase; 1457 SeiResolveAPI(HookModuleInfo); 1458 } 1459 1460 SeiHookImports(LdrEntry); 1461 1462 NotifyShims(SHIM_REASON_DLL_LOAD, LdrEntry); 1463 } 1464 1465 VOID WINAPI SE_DllUnloaded(PLDR_DATA_TABLE_ENTRY LdrEntry) 1466 { 1467 SHIMENG_INFO("(%p)\n", LdrEntry); 1468 1469 /* Should we unhook here? */ 1470 1471 NotifyShims(SHIM_REASON_DLL_UNLOAD, LdrEntry); 1472 } 1473 1474 BOOL WINAPI SE_IsShimDll(PVOID BaseAddress) 1475 { 1476 SHIMENG_INFO("(%p)\n", BaseAddress); 1477 1478 return SeiGetShimModuleInfo(BaseAddress) != NULL; 1479 } 1480 1481 /* 'Private' ntdll function */ 1482 BOOLEAN 1483 NTAPI 1484 LdrInitShimEngineDynamic(IN PVOID BaseAddress); 1485 1486 1487 BOOL WINAPI SE_DynamicShim(LPCWSTR ProcessImage, HSDB hsdb, PVOID pQueryResult, LPCSTR Module, LPDWORD lpdwDynamicToken) 1488 { 1489 if (g_bShimEngInitialized) 1490 { 1491 SHIMENG_MSG("ReactOS HACK(CORE-13283): ShimEng already initialized!\n"); 1492 return TRUE; 1493 } 1494 1495 g_bShimDuringInit = TRUE; 1496 SeiInit(ProcessImage, hsdb, pQueryResult, FALSE); 1497 g_bShimDuringInit = FALSE; 1498 1499 LdrInitShimEngineDynamic(g_hInstance); 1500 1501 return TRUE; 1502 } 1503 1504