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