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