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