[SHIMENG] Pass shim commandline to shims. CORE-11329
[reactos.git] / reactos / dll / appcompat / apphelp / shimeng.c
1 /*
2 * Copyright 2015-2017 Mark Jansen (mark.jansen@reactos.org)
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #define WIN32_NO_STATUS
20 #include "windows.h"
21 #include "ntndk.h"
22 #define IN_APPHELP
23 #include "shimlib.h"
24 #include <strsafe.h>
25 /* Make sure we don't include apphelp logging */
26 #define APPHELP_NOSDBPAPI
27 #include "apphelp.h"
28 #include "shimeng.h"
29
30
31 typedef FARPROC(WINAPI* GETPROCADDRESSPROC)(HINSTANCE, LPCSTR);
32
33
34 FARPROC WINAPI StubGetProcAddress(HINSTANCE hModule, LPCSTR lpProcName);
35 BOOL WINAPI SE_IsShimDll(PVOID BaseAddress);
36
37
38 extern HMODULE g_hInstance;
39 ULONG g_ShimEngDebugLevel = 0xffffffff;
40 BOOL g_bComPlusImage = FALSE;
41 BOOL g_bShimDuringInit = FALSE;
42 BOOL g_bInternalHooksUsed = FALSE;
43 static ARRAY g_pShimInfo; /* SHIMMODULE */
44 static ARRAY g_pHookArray; /* HOOKMODULEINFO */
45
46 HOOKAPIEX g_IntHookEx[] =
47 {
48 {
49 "kernel32.dll", /* LibraryName */
50 "GetProcAddress", /* FunctionName */
51 StubGetProcAddress, /* ReplacementFunction*/
52 NULL, /* OriginalFunction */
53 { NULL }, /* ModuleLink */
54 { NULL } /* ApiLink */
55 },
56 };
57
58
59
60
61 static BOOL ARRAY_EnsureSize(PARRAY Array, DWORD ItemSize, DWORD GrowWith)
62 {
63 PVOID pNewData;
64 DWORD Count;
65
66 if (Array->MaxSize > Array->Size)
67 return TRUE;
68
69 Count = Array->Size + GrowWith;
70 pNewData = SeiAlloc(Count * ItemSize);
71
72 if (!pNewData)
73 {
74 SHIMENG_FAIL("Failed to allocate %d bytes\n", Count * ItemSize);
75 return FALSE;
76 }
77 Array->MaxSize = Count;
78
79 if (Array->Data)
80 {
81 memcpy(pNewData, Array->Data, Array->Size * ItemSize);
82 SeiFree(Array->Data);
83 }
84 Array->Data = pNewData;
85
86 return TRUE;
87 }
88
89
90
91 VOID SeiInitDebugSupport(VOID)
92 {
93 static const UNICODE_STRING DebugKey = RTL_CONSTANT_STRING(L"SHIMENG_DEBUG_LEVEL");
94 UNICODE_STRING DebugValue;
95 NTSTATUS Status;
96 ULONG NewLevel = 0;
97 WCHAR Buffer[40];
98
99 RtlInitEmptyUnicodeString(&DebugValue, Buffer, sizeof(Buffer));
100
101 Status = RtlQueryEnvironmentVariable_U(NULL, &DebugKey, &DebugValue);
102
103 if (NT_SUCCESS(Status))
104 {
105 if (!NT_SUCCESS(RtlUnicodeStringToInteger(&DebugValue, 10, &NewLevel)))
106 NewLevel = 0;
107 }
108 g_ShimEngDebugLevel = NewLevel;
109 }
110
111
112 /**
113 * Outputs diagnostic info.
114 *
115 * @param [in] Level The level to log this message with, choose any of [SHIM_ERR,
116 * SHIM_WARN, SHIM_INFO].
117 * @param [in] FunctionName The function this log should be attributed to.
118 * @param [in] Format The format string.
119 * @param ... Variable arguments providing additional information.
120 *
121 * @return Success: TRUE Failure: FALSE.
122 */
123 BOOL WINAPIV SeiDbgPrint(SEI_LOG_LEVEL Level, PCSTR Function, PCSTR Format, ...)
124 {
125 char Buffer[512];
126 char* Current = Buffer;
127 const char* LevelStr;
128 size_t Length = sizeof(Buffer);
129 va_list ArgList;
130 HRESULT hr;
131
132 if (g_ShimEngDebugLevel == 0xffffffff)
133 SeiInitDebugSupport();
134
135 if (Level > g_ShimEngDebugLevel)
136 return FALSE;
137
138 switch (Level)
139 {
140 case SEI_MSG:
141 LevelStr = "MSG ";
142 break;
143 case SEI_FAIL:
144 LevelStr = "FAIL";
145 break;
146 case SEI_WARN:
147 LevelStr = "WARN";
148 break;
149 case SEI_INFO:
150 LevelStr = "INFO";
151 break;
152 default:
153 LevelStr = "USER";
154 break;
155 }
156
157 if (Function)
158 hr = StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s] [%s] ", LevelStr, Function);
159 else
160 hr = StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s] ", LevelStr);
161
162 if (!SUCCEEDED(hr))
163 return FALSE;
164
165 va_start(ArgList, Format);
166 hr = StringCchVPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, Format, ArgList);
167 va_end(ArgList);
168 if (!SUCCEEDED(hr))
169 return FALSE;
170
171 DbgPrint("%s", Buffer);
172 return TRUE;
173 }
174
175
176 PVOID SeiGetModuleFromAddress(PVOID addr)
177 {
178 PVOID hModule = NULL;
179 RtlPcToFileHeader(addr, &hModule);
180 return hModule;
181 }
182
183
184
185 /* TODO: Guard against recursive calling / calling init multiple times! */
186 VOID NotifyShims(DWORD dwReason, PVOID Info)
187 {
188 PSHIMMODULE Data;
189 DWORD n;
190
191 Data = g_pShimInfo.Data;
192 for (n = 0; n < g_pShimInfo.Size; ++n)
193 {
194 if (!Data[n].pNotifyShims)
195 continue;
196
197 Data[n].pNotifyShims(dwReason, Info);
198 }
199 }
200
201
202
203 VOID SeiCheckComPlusImage(PVOID BaseAddress)
204 {
205 ULONG ComSectionSize;
206 g_bComPlusImage = RtlImageDirectoryEntryToData(BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, &ComSectionSize) != NULL;
207
208 SHIMENG_INFO("COM+ executable %s\n", g_bComPlusImage ? "TRUE" : "FALSE");
209 }
210
211
212 PSHIMMODULE SeiGetShimInfo(PVOID BaseAddress)
213 {
214 PSHIMMODULE Data;
215 DWORD n;
216
217 Data = g_pShimInfo.Data;
218 for (n = 0; n < g_pShimInfo.Size; ++n)
219 {
220 if (Data[n].BaseAddress == BaseAddress)
221 return &Data[n];
222 }
223 return NULL;
224 }
225
226 PSHIMMODULE SeiCreateShimInfo(PCWSTR DllName, PVOID BaseAddress)
227 {
228 static const ANSI_STRING GetHookAPIs = RTL_CONSTANT_STRING("GetHookAPIs");
229 static const ANSI_STRING NotifyShims = RTL_CONSTANT_STRING("NotifyShims");
230 PSHIMMODULE Data;
231 PVOID pGetHookAPIs, pNotifyShims;
232
233 if (!NT_SUCCESS(LdrGetProcedureAddress(BaseAddress, (PANSI_STRING)&GetHookAPIs, 0, &pGetHookAPIs)) ||
234 !NT_SUCCESS(LdrGetProcedureAddress(BaseAddress, (PANSI_STRING)&NotifyShims, 0, &pNotifyShims)))
235 {
236 SHIMENG_WARN("Failed to resolve entry points for %S\n", DllName);
237 return NULL;
238 }
239
240 if (!ARRAY_EnsureSize(&g_pShimInfo, sizeof(SHIMMODULE), 5))
241 return NULL;
242
243 Data = g_pShimInfo.Data;
244 Data += g_pShimInfo.Size;
245 g_pShimInfo.Size++;
246
247 RtlCreateUnicodeString(&Data->Name, DllName);
248 Data->BaseAddress = BaseAddress;
249
250 Data->pGetHookAPIs = pGetHookAPIs;
251 Data->pNotifyShims = pNotifyShims;
252
253 Data->HookApis.Data = NULL;
254 Data->HookApis.Size = 0;
255 Data->HookApis.MaxSize = 0;
256
257 return Data;
258 }
259
260 VOID SeiAppendHookInfo(PSHIMMODULE pShimInfo, PHOOKAPIEX pHookApi, DWORD dwHookCount)
261 {
262 PHOOKAPIPOINTERS Data;
263 if (!ARRAY_EnsureSize(&pShimInfo->HookApis, sizeof(HOOKAPIPOINTERS), 5))
264 return;
265
266 Data = pShimInfo->HookApis.Data;
267 Data += pShimInfo->HookApis.Size;
268
269 Data->pHookApi = pHookApi;
270 Data->dwHookCount = dwHookCount;
271 pShimInfo->HookApis.Size++;
272 }
273
274 PHOOKMODULEINFO SeiFindHookModuleInfo(PUNICODE_STRING ModuleName, PVOID BaseAddress)
275 {
276 DWORD n;
277 PHOOKMODULEINFO Data = g_pHookArray.Data;
278
279 for (n = 0; n < g_pHookArray.Size; ++n)
280 {
281 if (BaseAddress && BaseAddress == Data[n].BaseAddress)
282 return &Data[n];
283
284 if (!BaseAddress && RtlEqualUnicodeString(ModuleName, &Data[n].Name, TRUE))
285 return &Data[n];
286 }
287
288 return NULL;
289 }
290
291 PHOOKMODULEINFO SeiFindHookModuleInfoForImportDescriptor(PBYTE DllBase, PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor)
292 {
293 UNICODE_STRING DllName;
294 PVOID DllHandle;
295 NTSTATUS Success;
296
297 if (!RtlCreateUnicodeStringFromAsciiz(&DllName, (PCSZ)(DllBase + ImportDescriptor->Name)))
298 {
299 SHIMENG_FAIL("Unable to convert dll name to unicode\n");
300 return NULL;
301 }
302
303 Success = LdrGetDllHandle(NULL, NULL, &DllName, &DllHandle);
304 RtlFreeUnicodeString(&DllName);
305
306 if (!NT_SUCCESS(Success))
307 {
308 SHIMENG_FAIL("Unable to get module handle for %wZ\n", &DllName);
309 return NULL;
310 }
311
312 return SeiFindHookModuleInfo(NULL, DllHandle);
313 }
314
315 static LPCWSTR SeiGetStringPtr(PDB pdb, TAGID tag, TAG type)
316 {
317 TAGID tagEntry = SdbFindFirstTag(pdb, tag, type);
318 if (tagEntry == TAGID_NULL)
319 return NULL;
320
321 return SdbGetStringTagPtr(pdb, tagEntry);
322 }
323
324 static DWORD SeiGetDWORD(PDB pdb, TAGID tag, TAG type)
325 {
326 TAGID tagEntry = SdbFindFirstTag(pdb, tag, type);
327 if (tagEntry == TAGID_NULL)
328 return 0;
329
330 return SdbReadDWORDTag(pdb, tagEntry, TAGID_NULL);
331 }
332
333
334 static VOID SeiAddShim(TAGREF trShimRef, PARRAY pShimRef)
335 {
336 TAGREF* Data;
337 if (!ARRAY_EnsureSize(pShimRef, sizeof(TAGREF), 10))
338 return;
339
340 Data = pShimRef->Data;
341 Data[pShimRef->Size] = trShimRef;
342 pShimRef->Size++;
343 }
344
345 static VOID SeiSetLayerEnvVar(LPCWSTR wszLayer)
346 {
347 NTSTATUS Status;
348 UNICODE_STRING VarName = RTL_CONSTANT_STRING(L"__COMPAT_LAYER");
349 UNICODE_STRING Value;
350
351 RtlInitUnicodeString(&Value, wszLayer);
352
353 Status = RtlSetEnvironmentVariable(NULL, &VarName, &Value);
354 if (NT_SUCCESS(Status))
355 SHIMENG_INFO("Set env var %wZ=%wZ\n", &VarName, &Value);
356 else
357 SHIMENG_FAIL("Failed to set %wZ: 0x%x\n", &VarName, Status);
358 }
359
360 #define MAX_LAYER_LENGTH 256
361
362 static VOID SeiBuildShimRefArray(HSDB hsdb, SDBQUERYRESULT* pQuery, PARRAY pShimRef)
363 {
364 WCHAR wszLayerEnvVar[MAX_LAYER_LENGTH] = { 0 };
365 DWORD n;
366
367 for (n = 0; n < pQuery->dwExeCount; ++n)
368 {
369 PDB pdb;
370 TAGID tag;
371 if (SdbTagRefToTagID(hsdb, pQuery->atrExes[n], &pdb, &tag))
372 {
373 LPCWSTR ExeName = SeiGetStringPtr(pdb, tag, TAG_NAME);
374 TAGID ShimRef = SdbFindFirstTag(pdb, tag, TAG_SHIM_REF);
375
376 if (ExeName)
377 SeiDbgPrint(SEI_MSG, NULL, "ShimInfo(Exe(%S))\n", ExeName);
378
379 while (ShimRef != TAGID_NULL)
380 {
381 TAGREF trShimRef;
382 if (SdbTagIDToTagRef(hsdb, pdb, ShimRef, &trShimRef))
383 SeiAddShim(trShimRef, pShimRef);
384
385
386 ShimRef = SdbFindNextTag(pdb, tag, ShimRef);
387 }
388
389 /* Handle FLAG_REF */
390 }
391 }
392
393
394 for (n = 0; n < pQuery->dwLayerCount; ++n)
395 {
396 PDB pdb;
397 TAGID tag;
398 if (SdbTagRefToTagID(hsdb, pQuery->atrLayers[n], &pdb, &tag))
399 {
400 LPCWSTR LayerName = SeiGetStringPtr(pdb, tag, TAG_NAME);
401 TAGID ShimRef = SdbFindFirstTag(pdb, tag, TAG_SHIM_REF);
402 if (LayerName)
403 {
404 HRESULT hr;
405 SeiDbgPrint(SEI_MSG, NULL, "ShimInfo(Layer(%S))\n", LayerName);
406 if (wszLayerEnvVar[0])
407 StringCchCatW(wszLayerEnvVar, _countof(wszLayerEnvVar), L" ");
408 hr = StringCchCatW(wszLayerEnvVar, _countof(wszLayerEnvVar), LayerName);
409 if (!SUCCEEDED(hr))
410 {
411 SHIMENG_FAIL("Unable to append %S\n", LayerName);
412 }
413 }
414
415 while (ShimRef != TAGID_NULL)
416 {
417 TAGREF trShimRef;
418 if (SdbTagIDToTagRef(hsdb, pdb, ShimRef, &trShimRef))
419 SeiAddShim(trShimRef, pShimRef);
420
421 ShimRef = SdbFindNextTag(pdb, tag, ShimRef);
422 }
423
424 /* Handle FLAG_REF */
425 }
426 }
427 if (wszLayerEnvVar[0])
428 SeiSetLayerEnvVar(wszLayerEnvVar);
429 }
430
431
432 VOID SeiAddHooks(PHOOKAPIEX hooks, DWORD dwHookCount)
433 {
434 DWORD n;
435 UNICODE_STRING UnicodeModName;
436 WCHAR Buf[512];
437
438 RtlInitEmptyUnicodeString(&UnicodeModName, Buf, sizeof(Buf));
439
440 for (n = 0; n < dwHookCount; ++n)
441 {
442 ANSI_STRING AnsiString;
443 PVOID DllHandle;
444 PHOOKAPIEX hook = hooks + n;
445 PHOOKMODULEINFO HookModuleInfo;
446 PSINGLE_LIST_ENTRY Entry;
447
448 RtlInitAnsiString(&AnsiString, hook->LibraryName);
449 if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeModName, &AnsiString, FALSE)))
450 {
451 SHIMENG_FAIL("Unable to convert %s to Unicode\n", hook->LibraryName);
452 continue;
453 }
454
455 RtlInitAnsiString(&AnsiString, hook->FunctionName);
456 if (NT_SUCCESS(LdrGetDllHandle(NULL, 0, &UnicodeModName, &DllHandle)))
457 {
458 PVOID ProcAddress;
459
460
461 if (!NT_SUCCESS(LdrGetProcedureAddress(DllHandle, &AnsiString, 0, &ProcAddress)))
462 {
463 SHIMENG_FAIL("Unable to retrieve %s!%s\n", hook->LibraryName, hook->FunctionName);
464 continue;
465 }
466
467 HookModuleInfo = SeiFindHookModuleInfo(NULL, DllHandle);
468 hook->OriginalFunction = ProcAddress;
469 }
470 else
471 {
472 HookModuleInfo = SeiFindHookModuleInfo(&UnicodeModName, NULL);
473 DllHandle = NULL;
474 }
475
476 if (!HookModuleInfo)
477 {
478 if (!ARRAY_EnsureSize(&g_pHookArray, sizeof(HOOKMODULEINFO), 5))
479 continue;
480
481 HookModuleInfo = g_pHookArray.Data;
482 HookModuleInfo += g_pHookArray.Size;
483 g_pHookArray.Size++;
484
485 HookModuleInfo->BaseAddress = DllHandle;
486 RtlCreateUnicodeString(&HookModuleInfo->Name, UnicodeModName.Buffer);
487 }
488
489 Entry = &HookModuleInfo->ModuleLink;
490
491 while (Entry && Entry->Next)
492 {
493 PHOOKAPIEX HookApi = CONTAINING_RECORD(Entry->Next, HOOKAPIEX, ModuleLink);
494
495 int CmpResult = strcmp(hook->FunctionName, HookApi->FunctionName);
496 if (CmpResult == 0)
497 {
498 /* Multiple hooks on one function? --> use ApiLink */
499 SHIMENG_FAIL("Multiple hooks on one API is not yet supported!\n");
500 ASSERT(0);
501 }
502 else if (CmpResult < 0)
503 {
504 /* Break out of the loop to have the entry inserted 'in place' */
505 break;
506 }
507
508 Entry = Entry->Next;
509 }
510 /* If Entry is not NULL, the item is not inserted yet, so link it at the end. */
511 if (Entry)
512 {
513 hook->ModuleLink.Next = Entry->Next;
514 Entry->Next = &hook->ModuleLink;
515 }
516 }
517 }
518
519
520 FARPROC WINAPI StubGetProcAddress(HINSTANCE hModule, LPCSTR lpProcName)
521 {
522 char szOrdProcName[10] = "";
523 LPCSTR lpPrintName = lpProcName;
524 PVOID Addr = _ReturnAddress();
525 PHOOKMODULEINFO HookModuleInfo;
526 FARPROC proc = ((GETPROCADDRESSPROC)g_IntHookEx[0].OriginalFunction)(hModule, lpProcName);
527
528 if (!HIWORD(lpProcName))
529 {
530 sprintf(szOrdProcName, "#%lu", (DWORD)lpProcName);
531 lpPrintName = szOrdProcName;
532 }
533
534 Addr = SeiGetModuleFromAddress(Addr);
535 if (SE_IsShimDll(Addr))
536 {
537 SHIMENG_MSG("Not touching GetProcAddress for shim dll (%p!%s)", hModule, lpPrintName);
538 return proc;
539 }
540
541 SHIMENG_MSG("(GetProcAddress(%p!%s) => %p\n", hModule, lpProcName, lpPrintName);
542
543 HookModuleInfo = SeiFindHookModuleInfo(NULL, hModule);
544
545 /* FIXME: Ordinal not yet supported */
546 if (HookModuleInfo && HIWORD(lpProcName))
547 {
548 PSINGLE_LIST_ENTRY Entry;
549
550 Entry = HookModuleInfo->ModuleLink.Next;
551
552 while (Entry)
553 {
554 PHOOKAPIEX HookApi = CONTAINING_RECORD(Entry, HOOKAPIEX, ModuleLink);
555
556 int CmpResult = strcmp(lpProcName, HookApi->FunctionName);
557 if (CmpResult == 0)
558 {
559 SHIMENG_MSG("Redirecting %p to %p\n", proc, HookApi->ReplacementFunction);
560 proc = HookApi->ReplacementFunction;
561 break;
562 }
563 else if (CmpResult < 0)
564 {
565 SHIMENG_MSG("Not found %s\n", lpProcName);
566 /* We are not going to find it anymore.. */
567 break;
568 }
569
570 Entry = Entry->Next;
571 }
572 }
573
574 return proc;
575 }
576
577 VOID SeiResolveAPIs(VOID)
578 {
579 PSHIMMODULE Data;
580 DWORD mod, n;
581
582 Data = g_pShimInfo.Data;
583
584 /* Enumerate all Shim modules */
585 for (mod = 0; mod < g_pShimInfo.Size; ++mod)
586 {
587 PHOOKAPIPOINTERS pShims = Data[mod].HookApis.Data;
588 DWORD dwShimCount = Data[mod].HookApis.Size;
589
590 /* Enumerate all Shims */
591 for (n = 0; n < dwShimCount; ++n)
592 {
593 PHOOKAPIEX hooks = pShims[n].pHookApi;
594 DWORD dwHookCount = pShims[n].dwHookCount;
595
596 SeiAddHooks(hooks, dwHookCount);
597 }
598 }
599 }
600
601 VOID SeiAddInternalHooks(DWORD dwNumHooks)
602 {
603 if (dwNumHooks == 0)
604 {
605 g_bInternalHooksUsed = FALSE;
606 return;
607 }
608
609 SeiAddHooks(g_IntHookEx, _countof(g_IntHookEx));
610 g_bInternalHooksUsed = TRUE;
611 }
612
613 VOID SeiPatchNewImport(PIMAGE_THUNK_DATA FirstThunk, PHOOKAPIEX HookApi, PLDR_DATA_TABLE_ENTRY LdrEntry)
614 {
615 ULONG OldProtection = 0;
616 PVOID Ptr;
617 ULONG Size;
618 NTSTATUS Status;
619
620 SHIMENG_INFO("Hooking API \"%s!%s\" for DLL \"%wZ\"\n", HookApi->LibraryName, HookApi->FunctionName, &LdrEntry->BaseDllName);
621
622 Ptr = &FirstThunk->u1.Function;
623 Size = sizeof(FirstThunk->u1.Function);
624 Status = NtProtectVirtualMemory(NtCurrentProcess(), &Ptr, &Size, PAGE_EXECUTE_READWRITE, &OldProtection);
625
626 if (!NT_SUCCESS(Status))
627 {
628 SHIMENG_FAIL("Unable to unprotect 0x%p\n", &FirstThunk->u1.Function);
629 return;
630 }
631
632 SHIMENG_INFO("changing 0x%p to 0x%p\n", FirstThunk->u1.Function, HookApi->ReplacementFunction);
633 #ifdef _WIN64
634 FirstThunk->u1.Function = (ULONGLONG)HookApi->ReplacementFunction;
635 #else
636 FirstThunk->u1.Function = (DWORD)HookApi->ReplacementFunction;
637 #endif
638
639 Size = sizeof(FirstThunk->u1.Function);
640 Status = NtProtectVirtualMemory(NtCurrentProcess(), &Ptr, &Size, OldProtection, &OldProtection);
641
642 if (!NT_SUCCESS(Status))
643 {
644 SHIMENG_WARN("Unable to reprotect 0x%p\n", &FirstThunk->u1.Function);
645 }
646 }
647
648 /* Level(INFO) [SeiPrintExcludeInfo] Module "kernel32.dll" excluded for shim VistaRTMVersionLie, API "NTDLL.DLL!RtlGetVersion", because it is in System32/WinSXS. */
649
650 VOID SeiHookImports(PLDR_DATA_TABLE_ENTRY LdrEntry)
651 {
652 ULONG Size;
653 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
654 PBYTE DllBase = LdrEntry->DllBase;
655
656 if (SE_IsShimDll(DllBase) || g_hInstance == LdrEntry->DllBase)
657 {
658 SHIMENG_INFO("Skipping shim module 0x%p \"%wZ\"\n", LdrEntry->DllBase, &LdrEntry->BaseDllName);
659 return;
660 }
661
662 ImportDescriptor = RtlImageDirectoryEntryToData(DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &Size);
663 if (!ImportDescriptor)
664 {
665 SHIMENG_INFO("Skipping module 0x%p \"%wZ\" due to no iat found\n", LdrEntry->DllBase, &LdrEntry->BaseDllName);
666 return;
667 }
668
669 SHIMENG_INFO("Hooking module 0x%p \"%wZ\"\n", LdrEntry->DllBase, &LdrEntry->BaseDllName);
670
671 for ( ;ImportDescriptor->Name && ImportDescriptor->OriginalFirstThunk; ImportDescriptor++)
672 {
673 PHOOKMODULEINFO HookModuleInfo;
674
675 /* Do we have hooks for this module? */
676 HookModuleInfo = SeiFindHookModuleInfoForImportDescriptor(DllBase, ImportDescriptor);
677
678 if (HookModuleInfo)
679 {
680 PIMAGE_THUNK_DATA OriginalThunk, FirstThunk;
681 PSINGLE_LIST_ENTRY Entry;
682
683 Entry = HookModuleInfo->ModuleLink.Next;
684
685 while (Entry)
686 {
687 DWORD dwFound = 0;
688 OriginalThunk = (PIMAGE_THUNK_DATA)(DllBase + ImportDescriptor->OriginalFirstThunk);
689 FirstThunk = (PIMAGE_THUNK_DATA)(DllBase + ImportDescriptor->FirstThunk);
690
691 for (;OriginalThunk->u1.AddressOfData && FirstThunk->u1.Function && Entry; OriginalThunk++, FirstThunk++)
692 {
693 PHOOKAPIEX HookApi = CONTAINING_RECORD(Entry, HOOKAPIEX, ModuleLink);
694
695 if (!IMAGE_SNAP_BY_ORDINAL32(OriginalThunk->u1.AddressOfData))
696 {
697 PIMAGE_IMPORT_BY_NAME ImportName;
698
699 ImportName = (PIMAGE_IMPORT_BY_NAME)(DllBase + OriginalThunk->u1.AddressOfData);
700 if (!strcmp((PCSTR)ImportName->Name, HookApi->FunctionName))
701 {
702 SeiPatchNewImport(FirstThunk, HookApi, LdrEntry);
703
704 /* Sadly, iat does not have to be sorted, and can even contain duplicate entries. */
705 dwFound++;
706 }
707 }
708 else
709 {
710 SHIMENG_FAIL("Ordinals not yet supported\n");
711 ASSERT(0);
712 }
713 }
714
715 if (Entry)
716 {
717 if (dwFound != 1)
718 {
719 /* One entry not found. */
720 PHOOKAPIEX HookApi = CONTAINING_RECORD(Entry, HOOKAPIEX, ModuleLink);
721 if (!dwFound)
722 SHIMENG_INFO("Entry \"%s!%s\" not found for \"%wZ\"\n", HookApi->LibraryName, HookApi->FunctionName, &LdrEntry->BaseDllName);
723 else
724 SHIMENG_INFO("Entry \"%s!%s\" found %d times for \"%wZ\"\n", HookApi->LibraryName, HookApi->FunctionName, dwFound, &LdrEntry->BaseDllName);
725 }
726
727 Entry = Entry->Next;
728 }
729 }
730 }
731 }
732 }
733
734
735 VOID PatchNewModules(PPEB Peb)
736 {
737 PLIST_ENTRY ListHead, ListEntry;
738 PLDR_DATA_TABLE_ENTRY LdrEntry;
739
740 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
741 ListEntry = ListHead->Flink;
742
743 while (ListHead != ListEntry)
744 {
745 LdrEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
746 SeiHookImports(LdrEntry);
747
748 ListEntry = ListEntry->Flink;
749 }
750 }
751
752
753 /*
754 Level(INFO) Using USER apphack flags 0x2080000
755 */
756
757 VOID SeiInit(PUNICODE_STRING ProcessImage, HSDB hsdb, SDBQUERYRESULT* pQuery)
758 {
759 DWORD n;
760 ARRAY ShimRef;
761 TAGREF* Data;
762 DWORD dwTotalHooks = 0;
763
764 PPEB Peb = NtCurrentPeb();
765
766 ShimRef.Data = NULL;
767 ShimRef.Size = ShimRef.MaxSize = 0;
768
769 SeiCheckComPlusImage(Peb->ImageBaseAddress);
770
771 /* TODO:
772 if (pQuery->trApphelp)
773 SeiDisplayAppHelp(?pQuery->trApphelp?);
774 */
775
776 SeiDbgPrint(SEI_MSG, NULL, "ShimInfo(ExePath(%wZ))\n", ProcessImage);
777 SeiBuildShimRefArray(hsdb, pQuery, &ShimRef);
778 SeiDbgPrint(SEI_MSG, NULL, "ShimInfo(Complete)\n");
779
780 SHIMENG_INFO("Got %d shims\n", ShimRef.Size);
781 /* TODO:
782 SeiBuildGlobalInclExclList()
783 */
784
785 Data = ShimRef.Data;
786
787 for (n = 0; n < ShimRef.Size; ++n)
788 {
789 PDB pdb;
790 TAGID ShimRef;
791 if (SdbTagRefToTagID(hsdb, Data[n], &pdb, &ShimRef))
792 {
793 LPCWSTR ShimName, DllName, CommandLine = NULL;
794 TAGID ShimTag;
795 WCHAR FullNameBuffer[MAX_PATH];
796 UNICODE_STRING UnicodeDllName;
797 PVOID BaseAddress;
798 PSHIMMODULE pShimInfo = NULL;
799 ANSI_STRING AnsiCommandLine = RTL_CONSTANT_STRING("");
800 PHOOKAPIEX pHookApi;
801 DWORD dwHookCount;
802
803 ShimName = SeiGetStringPtr(pdb, ShimRef, TAG_NAME);
804 if (!ShimName)
805 {
806 SHIMENG_FAIL("Failed to retrieve the name for 0x%x\n", Data[n]);
807 continue;
808 }
809
810 CommandLine = SeiGetStringPtr(pdb, ShimRef, TAG_COMMAND_LINE);
811 if (CommandLine && *CommandLine)
812 {
813 RtlInitUnicodeString(&UnicodeDllName, CommandLine);
814 if (NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiCommandLine, &UnicodeDllName, TRUE)))
815 {
816 SHIMENG_INFO("COMMAND LINE %s for %S", AnsiCommandLine.Buffer, ShimName);
817 }
818 else
819 {
820 AnsiCommandLine.Buffer = "";
821 CommandLine = NULL;
822 }
823 }
824
825 ShimTag = SeiGetDWORD(pdb, ShimRef, TAG_SHIM_TAGID);
826 if (!ShimTag)
827 {
828 SHIMENG_FAIL("Failed to resolve %S to a shim\n", ShimName);
829 continue;
830 }
831
832 if (!SdbGetAppPatchDir(NULL, FullNameBuffer, _countof(FullNameBuffer)))
833 {
834 SHIMENG_WARN("Failed to get the AppPatch dir\n");
835 continue;
836 }
837
838 DllName = SeiGetStringPtr(pdb, ShimTag, TAG_DLLFILE);
839 if (DllName == NULL ||
840 !SUCCEEDED(StringCchCatW(FullNameBuffer, _countof(FullNameBuffer), L"\\")) ||
841 !SUCCEEDED(StringCchCatW(FullNameBuffer, _countof(FullNameBuffer), DllName)))
842 {
843 SHIMENG_WARN("Failed to build a full path for %S\n", ShimName);
844 continue;
845 }
846
847 RtlInitUnicodeString(&UnicodeDllName, FullNameBuffer);
848 if (NT_SUCCESS(LdrGetDllHandle(NULL, NULL, &UnicodeDllName, &BaseAddress)))
849 {
850 pShimInfo = SeiGetShimInfo(BaseAddress);
851 }
852 else if (!NT_SUCCESS(LdrLoadDll(NULL, NULL, &UnicodeDllName, &BaseAddress)))
853 {
854 SHIMENG_WARN("Failed to load %wZ for %S\n", &UnicodeDllName, ShimName);
855 continue;
856 }
857 if (!pShimInfo)
858 {
859 pShimInfo = SeiCreateShimInfo(DllName, BaseAddress);
860 if (!pShimInfo)
861 {
862 SHIMENG_FAIL("Failed to allocate ShimInfo for %S\n", DllName);
863 continue;
864 }
865 }
866
867 SHIMENG_INFO("Shim DLL 0x%p \"%wZ\" loaded\n", BaseAddress, &UnicodeDllName);
868 SHIMENG_INFO("Using SHIM \"%S!%S\"\n", DllName, ShimName);
869
870 pHookApi = pShimInfo->pGetHookAPIs(AnsiCommandLine.Buffer, ShimName, &dwHookCount);
871 SHIMENG_INFO("GetHookAPIs returns %d hooks for DLL \"%wZ\" SHIM \"%S\"\n", dwHookCount, &UnicodeDllName, ShimName);
872 if (dwHookCount)
873 SeiAppendHookInfo(pShimInfo, pHookApi, dwHookCount);
874
875 if (CommandLine && *CommandLine)
876 RtlFreeAnsiString(&AnsiCommandLine);
877
878 dwTotalHooks += dwHookCount;
879 /*SeiBuildInclExclList*/
880 }
881 }
882
883 SeiAddInternalHooks(dwTotalHooks);
884 SeiResolveAPIs();
885 PatchNewModules(Peb);
886 }
887
888
889
890 BOOL SeiGetShimData(PUNICODE_STRING ProcessImage, PVOID pShimData, HSDB* pHsdb, SDBQUERYRESULT* pQuery)
891 {
892 static const UNICODE_STRING ForbiddenShimmingApps[] = {
893 RTL_CONSTANT_STRING(L"ntsd.exe"),
894 RTL_CONSTANT_STRING(L"windbg.exe"),
895 #if WINVER >= 0x600
896 RTL_CONSTANT_STRING(L"slsvc.exe"),
897 #endif
898 };
899 static const UNICODE_STRING BackSlash = RTL_CONSTANT_STRING(L"\\");
900 static const UNICODE_STRING ForwdSlash = RTL_CONSTANT_STRING(L"/");
901 UNICODE_STRING ProcessName;
902 USHORT Back, Forward;
903 HSDB hsdb;
904 DWORD n;
905
906 if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, ProcessImage, &BackSlash, &Back)))
907 Back = 0;
908
909 if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, ProcessImage, &ForwdSlash, &Forward)))
910 Forward = 0;
911
912 if (Back < Forward)
913 Back = Forward;
914
915 if (Back)
916 Back += sizeof(WCHAR);
917
918 ProcessName.Buffer = ProcessImage->Buffer + Back / sizeof(WCHAR);
919 ProcessName.Length = ProcessImage->Length - Back;
920 ProcessName.MaximumLength = ProcessImage->MaximumLength - Back;
921
922 for (n = 0; n < _countof(ForbiddenShimmingApps); ++n)
923 {
924 if (RtlEqualUnicodeString(&ProcessName, ForbiddenShimmingApps + n, TRUE))
925 {
926 SHIMENG_MSG("Not shimming %wZ\n", ForbiddenShimmingApps + n);
927 return FALSE;
928 }
929 }
930
931 /* We should probably load all db's here, but since we do not support that yet... */
932 hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL);
933 if (hsdb)
934 {
935 if (SdbUnpackAppCompatData(hsdb, ProcessImage->Buffer, pShimData, pQuery))
936 {
937 *pHsdb = hsdb;
938 return TRUE;
939 }
940 SdbReleaseDatabase(hsdb);
941 }
942 return FALSE;
943 }
944
945
946
947 VOID NTAPI SE_InstallBeforeInit(PUNICODE_STRING ProcessImage, PVOID pShimData)
948 {
949 HSDB hsdb = NULL;
950 SDBQUERYRESULT QueryResult = { { 0 } };
951 SHIMENG_MSG("(%wZ, %p)\n", ProcessImage, pShimData);
952
953 if (!SeiGetShimData(ProcessImage, pShimData, &hsdb, &QueryResult))
954 {
955 SHIMENG_FAIL("Failed to get shim data\n");
956 return;
957 }
958
959 g_bShimDuringInit = TRUE;
960 SeiInit(ProcessImage, hsdb, &QueryResult);
961 g_bShimDuringInit = FALSE;
962
963 SdbReleaseDatabase(hsdb);
964 }
965
966 VOID NTAPI SE_InstallAfterInit(PUNICODE_STRING ProcessImage, PVOID pShimData)
967 {
968 NotifyShims(SHIM_NOTIFY_ATTACH, NULL);
969 }
970
971 VOID NTAPI SE_ProcessDying(VOID)
972 {
973 SHIMENG_MSG("()\n");
974 NotifyShims(SHIM_NOTIFY_DETACH, NULL);
975 }
976
977 VOID WINAPI SE_DllLoaded(PLDR_DATA_TABLE_ENTRY LdrEntry)
978 {
979 SHIMENG_INFO("%sINIT. loading DLL \"%wZ\"\n", g_bShimDuringInit ? "" : "AFTER ", &LdrEntry->BaseDllName);
980 NotifyShims(SHIM_REASON_DLL_LOAD, LdrEntry);
981 }
982
983 VOID WINAPI SE_DllUnloaded(PLDR_DATA_TABLE_ENTRY LdrEntry)
984 {
985 SHIMENG_MSG("(%p)\n", LdrEntry);
986 NotifyShims(SHIM_REASON_DLL_UNLOAD, LdrEntry);
987 }
988
989 BOOL WINAPI SE_IsShimDll(PVOID BaseAddress)
990 {
991 SHIMENG_MSG("(%p)\n", BaseAddress);
992
993 return SeiGetShimInfo(BaseAddress) != NULL;
994 }
995