xref: /reactos/dll/appcompat/apphelp/shimeng.c (revision 40462c92)
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