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