xref: /reactos/dll/appcompat/shims/shimlib/shimlib.c (revision 6db3d889)
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 
ShimLib_Init(HINSTANCE hInstance)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 
ShimLib_Deinit(VOID)39 void ShimLib_Deinit(VOID)
40 {
41     // Is this a good idea?
42     HeapDestroy(g_ShimLib_Heap);
43 }
44 
ShimLib_ShimMalloc(SIZE_T dwSize)45 PVOID ShimLib_ShimMalloc(SIZE_T dwSize)
46 {
47     return HeapAlloc(g_ShimLib_Heap, 0, dwSize);
48 }
49 
ShimLib_ShimFree(PVOID pData)50 void ShimLib_ShimFree(PVOID pData)
51 {
52     HeapFree(g_ShimLib_Heap, 0, pData);
53 }
54 
ShimLib_Instance(VOID)55 HINSTANCE ShimLib_Instance(VOID)
56 {
57     return g_ShimLib_hInstance;
58 }
59 
ShimLib_StringNDuplicateA(PCSTR szString,SIZE_T stringLengthIncludingNullTerm)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 
ShimLib_StringDuplicateA(PCSTR szString)67 PCSTR ShimLib_StringDuplicateA(PCSTR szString)
68 {
69     return ShimLib_StringNDuplicateA(szString, lstrlenA(szString) + 1);
70 }
71 
ShimLib_StrAEqualsWNC(PCSTR szString,PCWSTR wszString)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$AAA") 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 */
ShimLib_GetHookAPIs(IN LPCSTR szCommandLine,IN LPCWSTR wszShimName,OUT PDWORD pdwHookCount)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 
ShimLib_NotifyShims(DWORD fdwReason,PVOID ptr)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 
SeiInitDebugSupport(VOID)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 */
SeiDbgPrint(SEI_LOG_LEVEL Level,PCSTR Function,PCSTR Format,...)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 
249