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