[SHIMENG] Find shims case-insensitive
[reactos.git] / dll / appcompat / shims / shimlib / shimlib.c
1 /*
2 * PROJECT: ReactOS Shim helper library
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Shim helper functions
5 * COPYRIGHT: Copyright 2016-2018 Mark Jansen (mark.jansen@reactos.org)
6 */
7
8 #define WIN32_NO_STATUS
9 #include <windef.h>
10 #include <winbase.h>
11 #include <shimlib.h>
12 #include <strsafe.h>
13 #include <ndk/rtlfuncs.h>
14
15 typedef struct UsedShim
16 {
17 SLIST_ENTRY Entry;
18 PSHIMREG pShim;
19 #if (WINVER > _WIN32_WINNT_WS03)
20 BOOL bInitCalled;
21 #endif
22 } UsedShim, *pUsedShim;
23
24
25 ULONG g_ShimEngDebugLevel = 0xffffffff;
26 static HINSTANCE g_ShimLib_hInstance;
27 static HANDLE g_ShimLib_Heap;
28 static PSLIST_HEADER g_UsedShims;
29
30 void ShimLib_Init(HINSTANCE hInstance)
31 {
32 g_ShimLib_hInstance = hInstance;
33 g_ShimLib_Heap = HeapCreate(0, 0x10000, 0);
34
35 g_UsedShims = (PSLIST_HEADER)ShimLib_ShimMalloc(sizeof(SLIST_HEADER));
36 RtlInitializeSListHead(g_UsedShims);
37 }
38
39 void ShimLib_Deinit(VOID)
40 {
41 // Is this a good idea?
42 HeapDestroy(g_ShimLib_Heap);
43 }
44
45 PVOID ShimLib_ShimMalloc(SIZE_T dwSize)
46 {
47 return HeapAlloc(g_ShimLib_Heap, 0, dwSize);
48 }
49
50 void ShimLib_ShimFree(PVOID pData)
51 {
52 HeapFree(g_ShimLib_Heap, 0, pData);
53 }
54
55 HINSTANCE ShimLib_Instance(VOID)
56 {
57 return g_ShimLib_hInstance;
58 }
59
60 PCSTR ShimLib_StringNDuplicateA(PCSTR szString, SIZE_T stringLengthIncludingNullTerm)
61 {
62 PSTR NewString = ShimLib_ShimMalloc(stringLengthIncludingNullTerm);
63 StringCchCopyA(NewString, stringLengthIncludingNullTerm, szString);
64 return NewString;
65 }
66
67 PCSTR ShimLib_StringDuplicateA(PCSTR szString)
68 {
69 return ShimLib_StringNDuplicateA(szString, lstrlenA(szString) + 1);
70 }
71
72 BOOL ShimLib_StrAEqualsWNC(PCSTR szString, PCWSTR wszString)
73 {
74 while (toupper(*szString) == towupper(*wszString))
75 {
76 if (!*szString)
77 return TRUE;
78
79 szString++; wszString++;
80 }
81 return FALSE;
82 }
83
84 #if defined(_MSC_VER)
85
86 #if defined(_M_IA64) || defined(_M_AMD64)
87 #define _ATTRIBUTES read
88 #else
89 #define _ATTRIBUTES read
90 #endif
91
92
93 #pragma section(".shm",long,read)
94 #pragma section(".shm$AAA",long,read)
95 #pragma section(".shm$ZZZ",long,read)
96 #endif
97
98 #ifdef _MSC_VER
99 #pragma comment(linker, "/merge:.shm=.rdata")
100 #endif
101
102
103 _SHMALLOC(".shm") SHIMREG _shim_start = { 0 };
104 _SHMALLOC(".shm$ZZZ") SHIMREG _shim_end = { 0 };
105
106
107 /* Generic GetHookAPIs function.
108 The macro's from <setup_shim.inl> and <implement_shim.inl> will register a list of all apis that should be hooked
109 for a specific shim
110 This helper function will return the correct shim, and call the init function */
111 PHOOKAPI WINAPI ShimLib_GetHookAPIs(IN LPCSTR szCommandLine, IN LPCWSTR wszShimName, OUT PDWORD pdwHookCount)
112 {
113 PSHIMREG ps = &_shim_start;
114 ps++;
115 for (; ps != &_shim_end; ps++)
116 {
117 if (ps->GetHookAPIs != NULL && ps->ShimName != NULL)
118 {
119 if (ShimLib_StrAEqualsWNC(ps->ShimName, wszShimName))
120 {
121 pUsedShim shim = (pUsedShim)ShimLib_ShimMalloc(sizeof(UsedShim));
122 shim->pShim = ps;
123 #if (WINVER > _WIN32_WINNT_WS03)
124 shim->bInitCalled = FALSE;
125 #endif
126 RtlInterlockedPushEntrySList(g_UsedShims, &(shim->Entry));
127
128 return ps->GetHookAPIs(SHIM_NOTIFY_ATTACH, szCommandLine, pdwHookCount);
129 }
130 }
131 }
132 return NULL;
133 }
134
135
136 BOOL WINAPI ShimLib_NotifyShims(DWORD fdwReason, PVOID ptr)
137 {
138 PSLIST_ENTRY pEntry = RtlFirstEntrySList(g_UsedShims);
139
140 if (fdwReason < SHIM_REASON_INIT)
141 fdwReason += (SHIM_REASON_INIT - SHIM_NOTIFY_ATTACH);
142
143 while (pEntry)
144 {
145 pUsedShim pUsed = CONTAINING_RECORD(pEntry, UsedShim, Entry);
146 _PVNotify Notify = pUsed->pShim->Notify;
147 #if (WINVER > _WIN32_WINNT_WS03)
148 if (pUsed->bInitCalled && fdwReason == SHIM_REASON_INIT)
149 Notify = NULL;
150 #endif
151 if (Notify)
152 Notify(fdwReason, ptr);
153 #if (WINVER > _WIN32_WINNT_WS03)
154 if (fdwReason == SHIM_REASON_INIT)
155 pUsed->bInitCalled = TRUE;
156 #endif
157
158 pEntry = pEntry->Next;
159 }
160
161 return TRUE;
162 }
163
164
165 VOID SeiInitDebugSupport(VOID)
166 {
167 static const UNICODE_STRING DebugKey = RTL_CONSTANT_STRING(L"SHIM_DEBUG_LEVEL");
168 UNICODE_STRING DebugValue;
169 NTSTATUS Status;
170 ULONG NewLevel = SEI_MSG;
171 WCHAR Buffer[40];
172
173 RtlInitEmptyUnicodeString(&DebugValue, Buffer, sizeof(Buffer));
174
175 Status = RtlQueryEnvironmentVariable_U(NULL, &DebugKey, &DebugValue);
176
177 if (NT_SUCCESS(Status))
178 {
179 if (!NT_SUCCESS(RtlUnicodeStringToInteger(&DebugValue, 10, &NewLevel)))
180 NewLevel = 0;
181 }
182 g_ShimEngDebugLevel = NewLevel;
183 }
184
185
186 /**
187 * Outputs diagnostic info.
188 *
189 * @param [in] Level The level to log this message with, choose any of [SHIM_ERR,
190 * SHIM_WARN, SHIM_INFO].
191 * @param [in] FunctionName The function this log should be attributed to.
192 * @param [in] Format The format string.
193 * @param ... Variable arguments providing additional information.
194 *
195 * @return Success: TRUE Failure: FALSE.
196 */
197 BOOL WINAPIV SeiDbgPrint(SEI_LOG_LEVEL Level, PCSTR Function, PCSTR Format, ...)
198 {
199 char Buffer[512];
200 char* Current = Buffer;
201 const char* LevelStr;
202 size_t Length = sizeof(Buffer);
203 va_list ArgList;
204 HRESULT hr;
205
206 if (g_ShimEngDebugLevel == 0xffffffff)
207 SeiInitDebugSupport();
208
209 if (Level > g_ShimEngDebugLevel)
210 return FALSE;
211
212 switch (Level)
213 {
214 case SEI_MSG:
215 LevelStr = "MSG ";
216 break;
217 case SEI_FAIL:
218 LevelStr = "FAIL";
219 break;
220 case SEI_WARN:
221 LevelStr = "WARN";
222 break;
223 case SEI_INFO:
224 LevelStr = "INFO";
225 break;
226 default:
227 LevelStr = "USER";
228 break;
229 }
230
231 if (Function)
232 hr = StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s] [%s] ", LevelStr, Function);
233 else
234 hr = StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s] ", LevelStr);
235
236 if (!SUCCEEDED(hr))
237 return FALSE;
238
239 va_start(ArgList, Format);
240 hr = StringCchVPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, Format, ArgList);
241 va_end(ArgList);
242 if (!SUCCEEDED(hr))
243 return FALSE;
244
245 DbgPrint("%s", Buffer);
246 return TRUE;
247 }
248