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