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