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$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 */ 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 249