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