xref: /reactos/dll/appcompat/apphelp/shimeng.c (revision 4572aad1)
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 FIXME:
883     Some observed tags:
884         '*' with include
885         '$' with include, followed by '*' without include
886     Include list logging, referring to: (MODE: EA)
887 */
888 VOID SeiReadInExclude(PDB pdb, TAGID parent, PARRAY dest)
889 {
890     TAGID InExcludeTag;
891 
892     InExcludeTag = SdbFindFirstTag(pdb, parent, TAG_INEXCLUD);
893 
894     while (InExcludeTag != TAGID_NULL)
895     {
896         PCWSTR ModuleName;
897         TAGID ModuleTag = SdbFindFirstTag(pdb, InExcludeTag, TAG_MODULE);
898         TAGID IncludeTag = SdbFindFirstTag(pdb, InExcludeTag, TAG_INCLUDE);
899 
900         ModuleName = SdbGetStringTagPtr(pdb, ModuleTag);
901         if (ModuleName)
902         {
903             SeiAppendInExclude(dest, ModuleName, IncludeTag != TAGID_NULL);
904         }
905         else
906         {
907             SHIMENG_WARN("INEXCLUDE without Module: 0x%x\n", InExcludeTag);
908         }
909 
910         InExcludeTag = SdbFindNextTag(pdb, parent, InExcludeTag);
911     }
912 }
913 
914 VOID SeiBuildGlobalInclExclList(HSDB hsdb)
915 {
916     PDB pdb;
917     TAGREF tr = TAGREF_ROOT;
918     TAGID root, db, library;
919 
920     if (!SdbTagRefToTagID(hsdb, tr, &pdb, &root))
921     {
922         SHIMENG_WARN("Unable to resolve database root\n");
923         return;
924     }
925     db = SdbFindFirstTag(pdb, root, TAG_DATABASE);
926     if (db == TAGID_NULL)
927     {
928         SHIMENG_WARN("Unable to resolve database\n");
929         return;
930     }
931     library = SdbFindFirstTag(pdb, db, TAG_LIBRARY);
932     if (library == TAGID_NULL)
933     {
934         SHIMENG_WARN("Unable to resolve library\n");
935         return;
936     }
937 
938     SeiReadInExclude(pdb, library, &g_InExclude);
939 }
940 
941 VOID SeiBuildInclExclList(PDB pdb, TAGID ShimTag, PSHIMINFO pShimInfo)
942 {
943     DWORD n;
944 
945     /* First duplicate the global in/excludes */
946     for (n = 0; n < ARRAY_Size(&g_InExclude); ++n)
947     {
948         PINEXCLUDE InEx = ARRAY_At(&g_InExclude, INEXCLUDE, n);
949         SeiAppendInExclude(&pShimInfo->InExclude, InEx->Module.Buffer, InEx->Include);
950     }
951 
952     /* Now read this shim's in/excludes (possibly overriding the global ones) */
953     SeiReadInExclude(pdb, ShimTag, &pShimInfo->InExclude);
954 }
955 
956 /* Given one loaded module, redirect (hook) all functions from the iat that are registered by shims */
957 VOID SeiHookImports(PLDR_DATA_TABLE_ENTRY LdrEntry)
958 {
959     ULONG Size;
960     PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
961     PBYTE DllBase = LdrEntry->DllBase;
962 
963     if (SE_IsShimDll(DllBase) ||
964         g_hInstance == LdrEntry->DllBase ||
965         RtlEqualUnicodeString(&g_LoadingShimDll, &LdrEntry->BaseDllName, TRUE))
966     {
967         SHIMENG_INFO("Skipping shim module 0x%p \"%wZ\"\n", LdrEntry->DllBase, &LdrEntry->BaseDllName);
968         return;
969     }
970 
971     if (LdrEntry->Flags & LDRP_COMPAT_DATABASE_PROCESSED)
972     {
973         SHIMENG_INFO("Skipping module 0x%p \"%wZ\" because it was already processed\n", LdrEntry->DllBase, &LdrEntry->BaseDllName);
974         return;
975     }
976 
977     ImportDescriptor = RtlImageDirectoryEntryToData(DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &Size);
978     if (!ImportDescriptor)
979     {
980         SHIMENG_INFO("Skipping module 0x%p \"%wZ\" due to no iat found\n", LdrEntry->DllBase, &LdrEntry->BaseDllName);
981         return;
982     }
983 
984     SHIMENG_INFO("Hooking module 0x%p \"%wZ\"\n", LdrEntry->DllBase, &LdrEntry->BaseDllName);
985 
986     for ( ;ImportDescriptor->Name && ImportDescriptor->OriginalFirstThunk; ImportDescriptor++)
987     {
988         PHOOKMODULEINFO HookModuleInfo;
989 
990         /* Do we have hooks for this module? */
991         HookModuleInfo = SeiFindHookModuleInfoForImportDescriptor(DllBase, ImportDescriptor);
992 
993         if (HookModuleInfo)
994         {
995             PIMAGE_THUNK_DATA OriginalThunk, FirstThunk;
996             DWORD n;
997 
998             for (n = 0; n < ARRAY_Size(&HookModuleInfo->HookApis); ++n)
999             {
1000                 DWORD dwFound = 0;
1001                 PHOOKAPIEX HookApi = *ARRAY_At(&HookModuleInfo->HookApis, PHOOKAPIEX, n);
1002 
1003                 /* Check if this module should be excluded from being hooked (system32/winsxs, global or shim exclude) */
1004                 if (SeiIsExcluded(LdrEntry, HookApi))
1005                 {
1006                     continue;
1007                 }
1008 
1009                 OriginalThunk = (PIMAGE_THUNK_DATA)(DllBase + ImportDescriptor->OriginalFirstThunk);
1010                 FirstThunk = (PIMAGE_THUNK_DATA)(DllBase + ImportDescriptor->FirstThunk);
1011 
1012                 /* Walk all imports */
1013                 for (;OriginalThunk->u1.AddressOfData && FirstThunk->u1.Function; OriginalThunk++, FirstThunk++)
1014                 {
1015                     if (!IMAGE_SNAP_BY_ORDINAL(OriginalThunk->u1.Function))
1016                     {
1017                         if (!SeiIsOrdinalName(HookApi->FunctionName))
1018                         {
1019                             PIMAGE_IMPORT_BY_NAME ImportName;
1020 
1021                             ImportName = (PIMAGE_IMPORT_BY_NAME)(DllBase + OriginalThunk->u1.Function);
1022                             if (!strcmp((PCSTR)ImportName->Name, HookApi->FunctionName))
1023                             {
1024                                 SeiPatchNewImport(FirstThunk, HookApi, LdrEntry);
1025 
1026                                 /* Sadly, iat does not have to be sorted, and can even contain duplicate entries. */
1027                                 dwFound++;
1028                             }
1029                         }
1030                     }
1031                     else
1032                     {
1033                         if (SeiIsOrdinalName(HookApi->FunctionName))
1034                         {
1035                             if ((PCSTR)IMAGE_ORDINAL(OriginalThunk->u1.Function) == HookApi->FunctionName)
1036                             {
1037                                 SeiPatchNewImport(FirstThunk, HookApi, LdrEntry);
1038                                 dwFound++;
1039                             }
1040                         }
1041                     }
1042                 }
1043 
1044                 if (dwFound != 1)
1045                 {
1046                     char szOrdProcFmt[10];
1047                     LPCSTR FuncName = SeiPrintFunctionName(HookApi->FunctionName, szOrdProcFmt);
1048 
1049                     /* One entry not found. */
1050                     if (!dwFound)
1051                         SHIMENG_INFO("Entry \"%s!%s\" not found for \"%wZ\"\n", HookApi->LibraryName, FuncName, &LdrEntry->BaseDllName);
1052                     else
1053                         SHIMENG_INFO("Entry \"%s!%s\" found %d times for \"%wZ\"\n", HookApi->LibraryName, FuncName, dwFound, &LdrEntry->BaseDllName);
1054                 }
1055             }
1056         }
1057     }
1058 
1059     /* Mark this module as processed. */
1060     LdrEntry->Flags |= LDRP_COMPAT_DATABASE_PROCESSED;
1061 }
1062 
1063 
1064 VOID PatchNewModules(PPEB Peb)
1065 {
1066     PLIST_ENTRY ListHead, ListEntry;
1067     PLDR_DATA_TABLE_ENTRY LdrEntry;
1068 
1069     ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1070     ListEntry = ListHead->Flink;
1071 
1072     while (ListHead != ListEntry)
1073     {
1074         LdrEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
1075         SeiHookImports(LdrEntry);
1076 
1077         ListEntry = ListEntry->Flink;
1078     }
1079 }
1080 
1081 
1082 VOID SeiInitPaths(VOID)
1083 {
1084 #define SYSTEM32  L"\\system32"
1085 #define WINSXS  L"\\winsxs"
1086 
1087     PWSTR WindowsDirectory = SdbpStrDup(SharedUserData->NtSystemRoot);
1088     RtlInitUnicodeString(&g_WindowsDirectory, WindowsDirectory);
1089 
1090     g_System32Directory.MaximumLength = g_WindowsDirectory.Length + SdbpStrsize(SYSTEM32);
1091     g_System32Directory.Buffer = SdbpAlloc(g_System32Directory.MaximumLength);
1092     RtlCopyUnicodeString(&g_System32Directory, &g_WindowsDirectory);
1093     RtlAppendUnicodeToString(&g_System32Directory, SYSTEM32);
1094 
1095     g_SxsDirectory.MaximumLength = g_WindowsDirectory.Length + SdbpStrsize(WINSXS);
1096     g_SxsDirectory.Buffer = SdbpAlloc(g_SxsDirectory.MaximumLength);
1097     RtlCopyUnicodeString(&g_SxsDirectory, &g_WindowsDirectory);
1098     RtlAppendUnicodeToString(&g_SxsDirectory, WINSXS);
1099 
1100 #undef SYSTEM32
1101 #undef WINSXS
1102 }
1103 
1104 VOID SeiSetEntryProcessed(PPEB Peb)
1105 {
1106     PLIST_ENTRY ListHead, Entry;
1107     PLDR_DATA_TABLE_ENTRY LdrEntry;
1108 
1109     ListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
1110     Entry = ListHead->Flink;
1111     while (Entry != ListHead)
1112     {
1113         LdrEntry = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);
1114         Entry = Entry->Flink;
1115 
1116         if (RtlEqualUnicodeString(&LdrEntry->BaseDllName, &Ntdll, TRUE) ||
1117             RtlEqualUnicodeString(&LdrEntry->BaseDllName, &Kernel32, TRUE) ||
1118             RtlEqualUnicodeString(&LdrEntry->BaseDllName, &Verifier, TRUE) ||
1119             RtlEqualUnicodeString(&LdrEntry->BaseDllName, &g_LoadingShimDll, TRUE) ||
1120             SE_IsShimDll(LdrEntry->DllBase) ||
1121             (LdrEntry->Flags & LDRP_ENTRY_PROCESSED))
1122         {
1123             SHIMENG_WARN("Don't mess with 0x%p '%wZ'\n", LdrEntry->DllBase, &LdrEntry->BaseDllName);
1124         }
1125         else
1126         {
1127             SHIMENG_WARN("Touching        0x%p '%wZ'\n", LdrEntry->DllBase, &LdrEntry->BaseDllName);
1128             LdrEntry->Flags |= (LDRP_ENTRY_PROCESSED | LDRP_SHIMENG_SUPPRESSED_ENTRY);
1129         }
1130     }
1131 
1132     ListHead = &NtCurrentPeb()->Ldr->InMemoryOrderModuleList;
1133     Entry = ListHead->Flink;
1134     SHIMENG_INFO("In memory:\n");
1135     while (Entry != ListHead)
1136     {
1137         LdrEntry = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
1138         Entry = Entry->Flink;
1139 
1140         SHIMENG_INFO("    0x%p '%wZ'\n", LdrEntry->DllBase, &LdrEntry->BaseDllName);
1141     }
1142 
1143     ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1144     Entry = ListHead->Flink;
1145     SHIMENG_INFO("In load:\n");
1146     while (Entry != ListHead)
1147     {
1148         LdrEntry = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
1149         Entry = Entry->Flink;
1150 
1151         SHIMENG_INFO("    0x%p '%wZ'\n", LdrEntry->DllBase, &LdrEntry->BaseDllName);
1152     }
1153 }
1154 
1155 VOID SeiResetEntryProcessed(PPEB Peb)
1156 {
1157     PLIST_ENTRY ListHead, Entry;
1158     PLDR_DATA_TABLE_ENTRY LdrEntry;
1159 
1160     ListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
1161     Entry = ListHead->Flink;
1162     while (Entry != ListHead)
1163     {
1164         LdrEntry = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);
1165         Entry = Entry->Flink;
1166 
1167         if (SE_IsShimDll(LdrEntry->DllBase) ||
1168             g_hInstance == LdrEntry->DllBase ||
1169             RtlEqualUnicodeString(&LdrEntry->BaseDllName, &Ntdll, TRUE) ||
1170             RtlEqualUnicodeString(&LdrEntry->BaseDllName, &Kernel32, TRUE) ||
1171             RtlEqualUnicodeString(&LdrEntry->BaseDllName, &Verifier, TRUE) ||
1172             !(LdrEntry->Flags & LDRP_SHIMENG_SUPPRESSED_ENTRY))
1173         {
1174             SHIMENG_WARN("Don't mess with 0x%p '%wZ'\n", LdrEntry->DllBase, &LdrEntry->BaseDllName);
1175         }
1176         else
1177         {
1178             SHIMENG_WARN("Resetting       0x%p '%wZ'\n", LdrEntry->DllBase, &LdrEntry->BaseDllName);
1179             LdrEntry->Flags &= ~(LDRP_ENTRY_PROCESSED | LDRP_SHIMENG_SUPPRESSED_ENTRY);
1180         }
1181     }
1182 }
1183 
1184 VOID SeiInit(LPCWSTR ProcessImage, HSDB hsdb, SDBQUERYRESULT* pQuery, BOOLEAN ProcessInit)
1185 {
1186     DWORD n;
1187     ARRAY ShimRefArray;
1188     DWORD dwTotalHooks = 0;
1189     FLAGINFO ShimFlags;
1190 
1191     PPEB Peb = NtCurrentPeb();
1192 
1193     /* We should only be called once! */
1194     ASSERT(g_pShimInfo.ItemSize__ == 0);
1195 
1196     ARRAY_Init(&ShimRefArray, TAGREF);
1197     ARRAY_Init(&g_pShimInfo, PSHIMMODULE);
1198     ARRAY_Init(&g_pHookArray, HOOKMODULEINFO);
1199     ARRAY_Init(&g_InExclude, INEXCLUDE);
1200     RtlZeroMemory(&ShimFlags, sizeof(ShimFlags));
1201 
1202     SeiInitPaths();
1203 
1204     SeiCheckComPlusImage(Peb->ImageBaseAddress);
1205 
1206     if (ProcessInit)
1207     {
1208         /* Mark all modules loaded until now as 'LDRP_ENTRY_PROCESSED' so that their entrypoint is not called while we are loading shims */
1209         SeiSetEntryProcessed(Peb);
1210     }
1211 
1212     /* TODO:
1213     if (pQuery->trApphelp)
1214         SeiDisplayAppHelp(?pQuery->trApphelp?);
1215     */
1216 
1217     SeiDbgPrint(SEI_MSG, NULL, "ShimInfo(ExePath(%S))\n", ProcessImage);
1218     SeiBuildShimRefArray(hsdb, pQuery, &ShimRefArray, &ShimFlags);
1219     if (ShimFlags.AppCompatFlags.QuadPart)
1220     {
1221         SeiDbgPrint(SEI_MSG, NULL, "Using KERNEL apphack flags 0x%I64x\n", ShimFlags.AppCompatFlags.QuadPart);
1222         Peb->AppCompatFlags.QuadPart |= ShimFlags.AppCompatFlags.QuadPart;
1223     }
1224     if (ShimFlags.AppCompatFlagsUser.QuadPart)
1225     {
1226         SeiDbgPrint(SEI_MSG, NULL, "Using USER apphack flags 0x%I64x\n", ShimFlags.AppCompatFlagsUser.QuadPart);
1227         Peb->AppCompatFlagsUser.QuadPart |= ShimFlags.AppCompatFlagsUser.QuadPart;
1228     }
1229     if (ShimFlags.ProcessParameters_Flags)
1230     {
1231         SeiDbgPrint(SEI_MSG, NULL, "Using ProcessParameters flags 0x%x\n", ShimFlags.ProcessParameters_Flags);
1232         Peb->ProcessParameters->Flags |= ShimFlags.ProcessParameters_Flags;
1233     }
1234     SeiDbgPrint(SEI_MSG, NULL, "ShimInfo(Complete)\n");
1235 
1236     SHIMENG_INFO("Got %d shims\n", ARRAY_Size(&ShimRefArray));
1237     SeiBuildGlobalInclExclList(hsdb);
1238 
1239     /* Walk all shims referenced (in layers + exes), and load their modules */
1240     for (n = 0; n < ARRAY_Size(&ShimRefArray); ++n)
1241     {
1242         PDB pdb;
1243         TAGID ShimRef;
1244 
1245         TAGREF tr = *ARRAY_At(&ShimRefArray, TAGREF, n);
1246 
1247         if (SdbTagRefToTagID(hsdb, tr, &pdb, &ShimRef))
1248         {
1249             LPCWSTR ShimName, DllName, CommandLine = NULL;
1250             TAGID ShimTag;
1251             WCHAR FullNameBuffer[MAX_PATH];
1252             UNICODE_STRING UnicodeDllName;
1253             PVOID BaseAddress;
1254             PSHIMMODULE pShimModuleInfo = NULL;
1255             ANSI_STRING AnsiCommandLine = RTL_CONSTANT_STRING("");
1256             PSHIMINFO pShimInfo = NULL;
1257             PHOOKAPIEX pHookApi;
1258             DWORD dwHookCount;
1259 
1260             ShimName = SeiGetStringPtr(pdb, ShimRef, TAG_NAME);
1261             if (!ShimName)
1262             {
1263                 SHIMENG_FAIL("Failed to retrieve the name for 0x%x\n", tr);
1264                 continue;
1265             }
1266 
1267             CommandLine = SeiGetStringPtr(pdb, ShimRef, TAG_COMMAND_LINE);
1268             if (CommandLine && *CommandLine)
1269             {
1270                 RtlInitUnicodeString(&UnicodeDllName, CommandLine);
1271                 if (NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiCommandLine, &UnicodeDllName, TRUE)))
1272                 {
1273                     SHIMENG_INFO("COMMAND LINE %s for %S", AnsiCommandLine.Buffer, ShimName);
1274                 }
1275                 else
1276                 {
1277                     AnsiCommandLine.Buffer = "";
1278                     CommandLine = NULL;
1279                 }
1280             }
1281 
1282             ShimTag = SeiGetDWORD(pdb, ShimRef, TAG_SHIM_TAGID);
1283             if (!ShimTag)
1284             {
1285                 SHIMENG_FAIL("Failed to resolve %S to a shim\n", ShimName);
1286                 continue;
1287             }
1288 
1289             if (!SUCCEEDED(SdbGetAppPatchDir(NULL, FullNameBuffer, ARRAYSIZE(FullNameBuffer))))
1290             {
1291                 SHIMENG_WARN("Failed to get the AppPatch dir\n");
1292                 continue;
1293             }
1294 
1295             DllName = SeiGetStringPtr(pdb, ShimTag, TAG_DLLFILE);
1296             if (DllName == NULL ||
1297                 !SUCCEEDED(StringCchCatW(FullNameBuffer, ARRAYSIZE(FullNameBuffer), L"\\")) ||
1298                 !SUCCEEDED(StringCchCatW(FullNameBuffer, ARRAYSIZE(FullNameBuffer), DllName)))
1299             {
1300                 SHIMENG_WARN("Failed to build a full path for %S\n", ShimName);
1301                 continue;
1302             }
1303 
1304             RtlInitUnicodeString(&g_LoadingShimDll, DllName);
1305             RtlInitUnicodeString(&UnicodeDllName, FullNameBuffer);
1306             if (NT_SUCCESS(LdrGetDllHandle(NULL, NULL, &UnicodeDllName, &BaseAddress)))
1307             {
1308                 /* This shim dll was already loaded, let's find it */
1309                 pShimModuleInfo = SeiGetShimModuleInfo(BaseAddress);
1310             }
1311             else if (!NT_SUCCESS(LdrLoadDll(NULL, NULL, &UnicodeDllName, &BaseAddress)))
1312             {
1313                 SHIMENG_WARN("Failed to load %wZ for %S\n", &UnicodeDllName, ShimName);
1314                 continue;
1315             }
1316             RtlInitUnicodeString(&g_LoadingShimDll, NULL);
1317             /* No shim module found (or we just loaded it) */
1318             if (!pShimModuleInfo)
1319             {
1320                 pShimModuleInfo = SeiCreateShimModuleInfo(DllName, BaseAddress);
1321                 if (!pShimModuleInfo)
1322                 {
1323                     SHIMENG_FAIL("Failed to allocate ShimInfo for %S\n", DllName);
1324                     continue;
1325                 }
1326             }
1327 
1328             SHIMENG_INFO("Shim DLL 0x%p \"%wZ\" loaded\n", BaseAddress, &UnicodeDllName);
1329             SHIMENG_INFO("Using SHIM \"%S!%S\"\n", DllName, ShimName);
1330 
1331             /* Ask this shim what hooks it needs (and pass along the commandline) */
1332             dwHookCount = 0;
1333             pHookApi = pShimModuleInfo->pGetHookAPIs(AnsiCommandLine.Buffer, ShimName, &dwHookCount);
1334             SHIMENG_INFO("GetHookAPIs returns %d hooks for DLL \"%wZ\" SHIM \"%S\"\n", dwHookCount, &UnicodeDllName, ShimName);
1335             if (dwHookCount && pHookApi)
1336                 pShimInfo = SeiAppendHookInfo(pShimModuleInfo, pHookApi, dwHookCount, ShimName);
1337             else
1338                 dwHookCount = 0;
1339 
1340             /* If this shim has hooks, create the include / exclude lists */
1341             if (pShimInfo)
1342                 SeiBuildInclExclList(pdb, ShimTag, pShimInfo);
1343 
1344             if (CommandLine && *CommandLine)
1345                 RtlFreeAnsiString(&AnsiCommandLine);
1346 
1347             dwTotalHooks += dwHookCount;
1348         }
1349     }
1350 
1351     SeiAddInternalHooks(dwTotalHooks);
1352     SeiCombineHookInfo();
1353     SeiResolveAPIs();
1354     PatchNewModules(Peb);
1355 
1356     if (ProcessInit)
1357     {
1358         /* Remove the 'LDRP_ENTRY_PROCESSED' flag from entries we modified, so that the loader can continue to process them */
1359         SeiResetEntryProcessed(Peb);
1360     }
1361 }
1362 
1363 
1364 /* Load the database + unpack the shim data (if this process is allowed) */
1365 BOOL SeiGetShimData(PUNICODE_STRING ProcessImage, PVOID pShimData, HSDB* pHsdb, SDBQUERYRESULT* pQuery)
1366 {
1367     static const UNICODE_STRING ForbiddenShimmingApps[] = {
1368         RTL_CONSTANT_STRING(L"ntsd.exe"),
1369         RTL_CONSTANT_STRING(L"windbg.exe"),
1370 #if WINVER >= 0x600
1371         RTL_CONSTANT_STRING(L"slsvc.exe"),
1372 #endif
1373     };
1374     static const UNICODE_STRING PathDividerFind = RTL_CONSTANT_STRING(L"\\/");
1375     UNICODE_STRING ProcessName;
1376     USHORT PathDivider;
1377     HSDB hsdb;
1378     DWORD n;
1379 
1380     if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, ProcessImage, &PathDividerFind, &PathDivider)))
1381         PathDivider = 0;
1382 
1383     if (PathDivider)
1384         PathDivider += sizeof(WCHAR);
1385 
1386     ProcessName.Buffer = ProcessImage->Buffer + PathDivider / sizeof(WCHAR);
1387     ProcessName.Length = ProcessImage->Length - PathDivider;
1388     ProcessName.MaximumLength = ProcessImage->MaximumLength - PathDivider;
1389 
1390     for (n = 0; n < ARRAYSIZE(ForbiddenShimmingApps); ++n)
1391     {
1392         if (RtlEqualUnicodeString(&ProcessName, ForbiddenShimmingApps + n, TRUE))
1393         {
1394             SHIMENG_MSG("Not shimming %wZ\n", ForbiddenShimmingApps + n);
1395             return FALSE;
1396         }
1397     }
1398 
1399     /* We should probably load all db's here, but since we do not support that yet... */
1400     hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL);
1401     if (hsdb)
1402     {
1403         if (SdbUnpackAppCompatData(hsdb, ProcessImage->Buffer, pShimData, pQuery))
1404         {
1405             *pHsdb = hsdb;
1406             return TRUE;
1407         }
1408         SdbReleaseDatabase(hsdb);
1409     }
1410     return FALSE;
1411 }
1412 
1413 
1414 
1415 VOID NTAPI SE_InstallBeforeInit(PUNICODE_STRING ProcessImage, PVOID pShimData)
1416 {
1417     HSDB hsdb = NULL;
1418     SDBQUERYRESULT QueryResult = { { 0 } };
1419     SHIMENG_INFO("(%wZ, %p)\n", ProcessImage, pShimData);
1420 
1421     if (!SeiGetShimData(ProcessImage, pShimData, &hsdb, &QueryResult))
1422     {
1423         SHIMENG_FAIL("Failed to get shim data\n");
1424         return;
1425     }
1426 
1427     g_bShimDuringInit = TRUE;
1428     SeiInit(ProcessImage->Buffer, hsdb, &QueryResult, TRUE);
1429     g_bShimDuringInit = FALSE;
1430 
1431     SdbReleaseDatabase(hsdb);
1432 }
1433 
1434 VOID NTAPI SE_InstallAfterInit(PUNICODE_STRING ProcessImage, PVOID pShimData)
1435 {
1436     NotifyShims(SHIM_NOTIFY_ATTACH, NULL);
1437 }
1438 
1439 VOID NTAPI SE_ProcessDying(VOID)
1440 {
1441     SHIMENG_MSG("()\n");
1442     NotifyShims(SHIM_NOTIFY_DETACH, NULL);
1443 }
1444 
1445 VOID WINAPI SE_DllLoaded(PLDR_DATA_TABLE_ENTRY LdrEntry)
1446 {
1447     PHOOKMODULEINFO HookModuleInfo;
1448     SHIMENG_INFO("%sINIT. loading DLL \"%wZ\"\n", g_bShimDuringInit ? "" : "AFTER ", &LdrEntry->BaseDllName);
1449 
1450     HookModuleInfo = SeiFindHookModuleInfo(&LdrEntry->BaseDllName, NULL);
1451     if (HookModuleInfo)
1452     {
1453         ASSERT(HookModuleInfo->BaseAddress == NULL);
1454         HookModuleInfo->BaseAddress = LdrEntry->DllBase;
1455         SeiResolveAPI(HookModuleInfo);
1456     }
1457 
1458     SeiHookImports(LdrEntry);
1459 
1460     NotifyShims(SHIM_REASON_DLL_LOAD, LdrEntry);
1461 }
1462 
1463 VOID WINAPI SE_DllUnloaded(PLDR_DATA_TABLE_ENTRY LdrEntry)
1464 {
1465     SHIMENG_INFO("(%p)\n", LdrEntry);
1466 
1467     /* Should we unhook here? */
1468 
1469     NotifyShims(SHIM_REASON_DLL_UNLOAD, LdrEntry);
1470 }
1471 
1472 BOOL WINAPI SE_IsShimDll(PVOID BaseAddress)
1473 {
1474     SHIMENG_INFO("(%p)\n", BaseAddress);
1475 
1476     return SeiGetShimModuleInfo(BaseAddress) != NULL;
1477 }
1478 
1479 /* 'Private' ntdll function */
1480 BOOLEAN
1481 NTAPI
1482 LdrInitShimEngineDynamic(IN PVOID BaseAddress);
1483 
1484 
1485 BOOL WINAPI SE_DynamicShim(LPCWSTR ProcessImage, HSDB hsdb, PVOID pQueryResult, LPCSTR Module, LPDWORD lpdwDynamicToken)
1486 {
1487     g_bShimDuringInit = TRUE;
1488     SeiInit(ProcessImage, hsdb, pQueryResult, FALSE);
1489     g_bShimDuringInit = FALSE;
1490 
1491     LdrInitShimEngineDynamic(g_hInstance);
1492 
1493     return TRUE;
1494 }
1495 
1496