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