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