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