[SHIMENG] Filter shims based on the include/exclude node specified, as well as their...
authorMark Jansen <mark.jansen@reactos.org>
Mon, 14 Aug 2017 16:34:56 +0000 (16:34 +0000)
committerMark Jansen <mark.jansen@reactos.org>
Mon, 14 Aug 2017 16:34:56 +0000 (16:34 +0000)
- Shims are not allowed on files from System32 / WinSxs.
- Individual shims can override this per module with includes/excludes.
- Shims can specify additional dlls to ignore or include.
- Specify a default list of dlls to be included from system32.
CORE-13618

svn path=/trunk/; revision=75541

reactos/dll/appcompat/apphelp/apphelp.h
reactos/dll/appcompat/apphelp/shimeng.c
reactos/dll/appcompat/apphelp/shimeng.h
reactos/media/sdb/sysmain.xml

index fe744be..e705a3b 100644 (file)
@@ -89,6 +89,7 @@ typedef struct tagSDBQUERYRESULT {
 
 /* sdbapi.c */
 PWSTR SdbpStrDup(LPCWSTR string);
+DWORD SdbpStrsize(PCWSTR string);
 HSDB WINAPI SdbInitDatabase(DWORD, LPCWSTR);
 void WINAPI SdbReleaseDatabase(HSDB);
 BOOL WINAPI SdbGUIDToString(CONST GUID *Guid, PWSTR GuidString, SIZE_T Length);
index cc67738..7432fa1 100644 (file)
 #include "shimeng.h"
 
 
-typedef FARPROC(WINAPI* GETPROCADDRESSPROC)(HINSTANCE, LPCSTR);
-
 
 FARPROC WINAPI StubGetProcAddress(HINSTANCE hModule, LPCSTR lpProcName);
 BOOL WINAPI SE_IsShimDll(PVOID BaseAddress);
 
 
 extern HMODULE g_hInstance;
+static UNICODE_STRING g_WindowsDirectory;
+static UNICODE_STRING g_System32Directory;
+static UNICODE_STRING g_SxsDirectory;
 ULONG g_ShimEngDebugLevel = 0xffffffff;
 BOOL g_bComPlusImage = FALSE;
 BOOL g_bShimDuringInit = FALSE;
 BOOL g_bInternalHooksUsed = FALSE;
-static ARRAY g_pShimInfo;  /* PSHIMMODULE */
-static ARRAY g_pHookArray; /* HOOKMODULEINFO */
+static ARRAY g_pShimInfo;   /* PSHIMMODULE */
+static ARRAY g_pHookArray;  /* HOOKMODULEINFO */
+static ARRAY g_InExclude;   /* INEXCLUDE */
 
+/* If we have setup a hook for a function, we should also redirect GetProcAddress for this function */
 HOOKAPIEX g_IntHookEx[] =
 {
     {
@@ -132,7 +135,7 @@ VOID SeiInitDebugSupport(VOID)
     static const UNICODE_STRING DebugKey = RTL_CONSTANT_STRING(L"SHIMENG_DEBUG_LEVEL");
     UNICODE_STRING DebugValue;
     NTSTATUS Status;
-    ULONG NewLevel = 0;
+    ULONG NewLevel = SEI_MSG;   /* Show some basic info in the logs, unless configured different */
     WCHAR Buffer[40];
 
     RtlInitEmptyUnicodeString(&DebugValue, Buffer, sizeof(Buffer));
@@ -294,20 +297,29 @@ PSHIMMODULE SeiCreateShimModuleInfo(PCWSTR DllName, PVOID BaseAddress)
     return Data;
 }
 
-VOID SeiAppendHookInfo(PSHIMMODULE pShimModuleInfo, PHOOKAPIEX pHookApi, DWORD dwHookCount)
+PSHIMINFO SeiAppendHookInfo(PSHIMMODULE pShimModuleInfo, PHOOKAPIEX pHookApi, DWORD dwHookCount, PCWSTR ShimName)
 {
     PSHIMINFO* pData, Data;
 
     pData = ARRAY_Append(&pShimModuleInfo->EnabledShims, PSHIMINFO);
     if (!pData)
-        return;
+        return NULL;
 
     *pData = SeiAlloc(sizeof(SHIMINFO));
     Data = *pData;
 
+    if (!Data)
+        return NULL;
+
+    Data->ShimName = SdbpStrDup(ShimName);
+    if (!Data->ShimName)
+        return NULL;
+
     Data->pHookApi = pHookApi;
     Data->dwHookCount = dwHookCount;
     Data->pShimModule = pShimModuleInfo;
+    ARRAY_Init(&Data->InExclude, INEXCLUDE);
+    return Data;
 }
 
 PHOOKMODULEINFO SeiFindHookModuleInfo(PUNICODE_STRING ModuleName, PVOID BaseAddress)
@@ -382,6 +394,7 @@ static VOID SeiAddShim(TAGREF trShimRef, PARRAY pShimRef)
     *Data = trShimRef;
 }
 
+/* Propagate layers to child processes */
 static VOID SeiSetLayerEnvVar(LPCWSTR wszLayer)
 {
     NTSTATUS Status;
@@ -399,6 +412,7 @@ static VOID SeiSetLayerEnvVar(LPCWSTR wszLayer)
 
 #define MAX_LAYER_LENGTH            256
 
+/* Translate all Exe and Layer entries to Shims, and propagate all layers */
 static VOID SeiBuildShimRefArray(HSDB hsdb, SDBQUERYRESULT* pQuery, PARRAY pShimRef)
 {
     WCHAR wszLayerEnvVar[MAX_LAYER_LENGTH] = { 0 };
@@ -468,7 +482,7 @@ static VOID SeiBuildShimRefArray(HSDB hsdb, SDBQUERYRESULT* pQuery, PARRAY pShim
         SeiSetLayerEnvVar(wszLayerEnvVar);
 }
 
-
+/* Given the hooks from one shim, find the relevant modules and store the combination of module + hook */
 VOID SeiAddHooks(PHOOKAPIEX hooks, DWORD dwHookCount, PSHIMINFO pShim)
 {
     DWORD n, j;
@@ -542,7 +556,9 @@ VOID SeiAddHooks(PHOOKAPIEX hooks, DWORD dwHookCount, PSHIMINFO pShim)
     }
 }
 
+typedef FARPROC(WINAPI* GETPROCADDRESSPROC)(HINSTANCE, LPCSTR);
 
+/* Check if we should fake the return from GetProcAddress (because we also redirected the iat for this module) */
 FARPROC WINAPI StubGetProcAddress(HINSTANCE hModule, LPCSTR lpProcName)
 {
     char szOrdProcName[10] = "";
@@ -588,6 +604,7 @@ FARPROC WINAPI StubGetProcAddress(HINSTANCE hModule, LPCSTR lpProcName)
     return proc;
 }
 
+/* Walk all shim modules / enabled shims, and add their hooks */
 VOID SeiResolveAPIs(VOID)
 {
     DWORD mod, n;
@@ -611,6 +628,7 @@ VOID SeiResolveAPIs(VOID)
     }
 }
 
+/* If we hooked something, we should also redirect GetProcAddress */
 VOID SeiAddInternalHooks(DWORD dwNumHooks)
 {
     if (dwNumHooks == 0)
@@ -623,6 +641,7 @@ VOID SeiAddInternalHooks(DWORD dwNumHooks)
     g_bInternalHooksUsed = TRUE;
 }
 
+/* Patch one function in the iat */
 VOID SeiPatchNewImport(PIMAGE_THUNK_DATA FirstThunk, PHOOKAPIEX HookApi, PLDR_DATA_TABLE_ENTRY LdrEntry)
 {
     ULONG OldProtection = 0;
@@ -658,8 +677,161 @@ VOID SeiPatchNewImport(PIMAGE_THUNK_DATA FirstThunk, PHOOKAPIEX HookApi, PLDR_DA
     }
 }
 
-/* Level(INFO) [SeiPrintExcludeInfo] Module "kernel32.dll" excluded for shim VistaRTMVersionLie, API "NTDLL.DLL!RtlGetVersion", because it is in System32/WinSXS. */
 
+PINEXCLUDE SeiFindInExclude(PARRAY InExclude, PCUNICODE_STRING DllName)
+{
+    DWORD n;
+
+    for (n = 0; n < ARRAY_Size(InExclude); ++n)
+    {
+        PINEXCLUDE InEx = ARRAY_At(InExclude, INEXCLUDE, n);
+
+        if (RtlEqualUnicodeString(&InEx->Module, DllName, TRUE))
+            return InEx;
+    }
+
+    return NULL;
+}
+
+BOOL SeiIsExcluded(PLDR_DATA_TABLE_ENTRY LdrEntry, PHOOKAPIEX HookApi)
+{
+    PSHIMINFO pShimInfo = HookApi->pShimInfo;
+    PINEXCLUDE InExclude;
+    BOOL IsExcluded = FALSE;
+
+    if (!pShimInfo)
+    {
+        /* Internal hook, do not exclude it */
+        return FALSE;
+    }
+
+    /* By default, everything from System32 or WinSxs is excluded */
+    if (RtlPrefixUnicodeString(&g_System32Directory, &LdrEntry->FullDllName, TRUE) ||
+        RtlPrefixUnicodeString(&g_SxsDirectory, &LdrEntry->FullDllName, TRUE))
+        IsExcluded = TRUE;
+
+    InExclude = SeiFindInExclude(&pShimInfo->InExclude, &LdrEntry->BaseDllName);
+    if (InExclude)
+    {
+        /* If it is on the 'exclude' list, bail out */
+        if (!InExclude->Include)
+        {
+            SHIMENG_INFO("Module '%wZ' excluded for shim %S, API '%s!%s', because it on in the exclude list.\n",
+                         &LdrEntry->BaseDllName, pShimInfo->ShimName, HookApi->LibraryName, HookApi->FunctionName);
+
+            return TRUE;
+        }
+        /* If it is on the 'include' list, override System32 / Winsxs check. */
+        if (IsExcluded)
+        {
+            SHIMENG_INFO("Module '%wZ' included for shim %S, API '%s!%s', because it is on the include list.\n",
+                         &LdrEntry->BaseDllName, pShimInfo->ShimName, HookApi->LibraryName, HookApi->FunctionName);
+
+        }
+        IsExcluded = FALSE;
+    }
+
+    if (IsExcluded)
+    {
+        SHIMENG_INFO("Module '%wZ' excluded for shim %S, API '%s!%s', because it is in System32/WinSXS.\n",
+                     &LdrEntry->BaseDllName, pShimInfo->ShimName, HookApi->LibraryName, HookApi->FunctionName);
+    }
+
+    return IsExcluded;
+}
+
+VOID SeiAppendInExclude(PARRAY dest, PCWSTR ModuleName, BOOL IsInclude)
+{
+    PINEXCLUDE InExclude;
+    UNICODE_STRING ModuleNameU;
+    RtlInitUnicodeString(&ModuleNameU, ModuleName);
+
+    InExclude = SeiFindInExclude(dest, &ModuleNameU);
+    if (InExclude)
+    {
+        InExclude->Include = IsInclude;
+        return;
+    }
+
+    InExclude = ARRAY_Append(dest, INEXCLUDE);
+    if (InExclude)
+    {
+        PCWSTR ModuleNameCopy = SdbpStrDup(ModuleName);
+        RtlInitUnicodeString(&InExclude->Module, ModuleNameCopy);
+        InExclude->Include = IsInclude;
+    }
+}
+
+/* Read the INEXCLUD tags from a given parent tag */
+VOID SeiReadInExclude(PDB pdb, TAGID parent, PARRAY dest)
+{
+    TAGID InExcludeTag;
+
+    InExcludeTag = SdbFindFirstTag(pdb, parent, TAG_INEXCLUD);
+
+    while (InExcludeTag != TAGID_NULL)
+    {
+        PCWSTR ModuleName;
+        TAGID ModuleTag = SdbFindFirstTag(pdb, InExcludeTag, TAG_MODULE);
+        TAGID IncludeTag = SdbFindFirstTag(pdb, InExcludeTag, TAG_INCLUDE);
+
+        ModuleName = SdbGetStringTagPtr(pdb, ModuleTag);
+        if (ModuleName)
+        {
+            SeiAppendInExclude(dest, ModuleName, IncludeTag != TAGID_NULL);
+        }
+        else
+        {
+            SHIMENG_WARN("INEXCLUDE without Module: 0x%x\n", InExcludeTag);
+        }
+
+        InExcludeTag = SdbFindNextTag(pdb, parent, InExcludeTag);
+    }
+}
+
+VOID SeiBuildGlobalInclExclList(HSDB hsdb)
+{
+    PDB pdb;
+    TAGREF tr = TAGREF_ROOT;
+    TAGID root, db, library;
+
+    if (!SdbTagRefToTagID(hsdb, tr, &pdb, &root))
+    {
+        SHIMENG_WARN("Unable to resolve database root\n");
+        return;
+    }
+    db = SdbFindFirstTag(pdb, root, TAG_DATABASE);
+    if (db == TAGID_NULL)
+    {
+        SHIMENG_WARN("Unable to resolve database\n");
+        return;
+    }
+    library = SdbFindFirstTag(pdb, db, TAG_LIBRARY);
+    if (library == TAGID_NULL)
+    {
+        SHIMENG_WARN("Unable to resolve library\n");
+        return;
+    }
+
+    SeiReadInExclude(pdb, library, &g_InExclude);
+}
+
+VOID SeiBuildInclExclList(PDB pdb, TAGID ShimTag, PSHIMINFO pShimInfo)
+{
+    DWORD n;
+
+    /* First duplicate the global in/excludes */
+    for (n = 0; n < ARRAY_Size(&g_InExclude); ++n)
+    {
+        PINEXCLUDE InEx = ARRAY_At(&g_InExclude, INEXCLUDE, n);
+        SeiAppendInExclude(&pShimInfo->InExclude, InEx->Module.Buffer, InEx->Include);
+    }
+
+    /* Now read this shim's in/excludes (possibly overriding the global ones) */
+    SeiReadInExclude(pdb, ShimTag, &pShimInfo->InExclude);
+}
+
+/* Given one loaded module, redirect (hook) all functions from the iat that are registered by shims */
 VOID SeiHookImports(PLDR_DATA_TABLE_ENTRY LdrEntry)
 {
     ULONG Size;
@@ -698,9 +870,16 @@ VOID SeiHookImports(PLDR_DATA_TABLE_ENTRY LdrEntry)
                 DWORD dwFound = 0;
                 PHOOKAPIEX HookApi = *ARRAY_At(&HookModuleInfo->HookApis, PHOOKAPIEX, n);
 
+                /* Check if this module should be excluded from being hooked (system32/winsxs, global or shim exclude) */
+                if (SeiIsExcluded(LdrEntry, HookApi))
+                {
+                    continue;
+                }
+
                 OriginalThunk = (PIMAGE_THUNK_DATA)(DllBase + ImportDescriptor->OriginalFirstThunk);
                 FirstThunk = (PIMAGE_THUNK_DATA)(DllBase + ImportDescriptor->FirstThunk);
 
+                /* Walk all imports */
                 for (;OriginalThunk->u1.AddressOfData && FirstThunk->u1.Function; OriginalThunk++, FirstThunk++)
                 {
                     if (!IMAGE_SNAP_BY_ORDINAL32(OriginalThunk->u1.AddressOfData))
@@ -755,6 +934,28 @@ VOID PatchNewModules(PPEB Peb)
 }
 
 
+VOID SeiInitPaths(VOID)
+{
+#define SYSTEM32  L"\\system32"
+#define WINSXS  L"\\winsxs"
+
+    PWSTR WindowsDirectory = SdbpStrDup(SharedUserData->NtSystemRoot);
+    RtlInitUnicodeString(&g_WindowsDirectory, WindowsDirectory);
+
+    g_System32Directory.MaximumLength = g_WindowsDirectory.Length + SdbpStrsize(SYSTEM32);
+    g_System32Directory.Buffer = SdbpAlloc(g_System32Directory.MaximumLength);
+    RtlCopyUnicodeString(&g_System32Directory, &g_WindowsDirectory);
+    RtlAppendUnicodeToString(&g_System32Directory, SYSTEM32);
+
+    g_SxsDirectory.MaximumLength = g_WindowsDirectory.Length + SdbpStrsize(WINSXS);
+    g_SxsDirectory.Buffer = SdbpAlloc(g_SxsDirectory.MaximumLength);
+    RtlCopyUnicodeString(&g_SxsDirectory, &g_WindowsDirectory);
+    RtlAppendUnicodeToString(&g_SxsDirectory, WINSXS);
+
+#undef SYSTEM32
+#undef WINSXS
+}
+
 /*
 Level(INFO) Using USER apphack flags 0x2080000
 */
@@ -773,6 +974,9 @@ VOID SeiInit(PUNICODE_STRING ProcessImage, HSDB hsdb, SDBQUERYRESULT* pQuery)
     ARRAY_Init(&ShimRefArray, TAGREF);
     ARRAY_Init(&g_pShimInfo, PSHIMMODULE);
     ARRAY_Init(&g_pHookArray, HOOKMODULEINFO);
+    ARRAY_Init(&g_InExclude, INEXCLUDE);
+
+    SeiInitPaths();
 
     SeiCheckComPlusImage(Peb->ImageBaseAddress);
 
@@ -786,10 +990,9 @@ VOID SeiInit(PUNICODE_STRING ProcessImage, HSDB hsdb, SDBQUERYRESULT* pQuery)
     SeiDbgPrint(SEI_MSG, NULL, "ShimInfo(Complete)\n");
 
     SHIMENG_INFO("Got %d shims\n", ARRAY_Size(&ShimRefArray));
-    /* TODO:
-    SeiBuildGlobalInclExclList()
-    */
+    SeiBuildGlobalInclExclList(hsdb);
 
+    /* Walk all shims referenced (in layers + exes), and load their modules */
     for (n = 0; n < ARRAY_Size(&ShimRefArray); ++n)
     {
         PDB pdb;
@@ -806,6 +1009,7 @@ VOID SeiInit(PUNICODE_STRING ProcessImage, HSDB hsdb, SDBQUERYRESULT* pQuery)
             PVOID BaseAddress;
             PSHIMMODULE pShimModuleInfo = NULL;
             ANSI_STRING AnsiCommandLine = RTL_CONSTANT_STRING("");
+            PSHIMINFO pShimInfo = NULL;
             PHOOKAPIEX pHookApi;
             DWORD dwHookCount;
 
@@ -856,6 +1060,7 @@ VOID SeiInit(PUNICODE_STRING ProcessImage, HSDB hsdb, SDBQUERYRESULT* pQuery)
             RtlInitUnicodeString(&UnicodeDllName, FullNameBuffer);
             if (NT_SUCCESS(LdrGetDllHandle(NULL, NULL, &UnicodeDllName, &BaseAddress)))
             {
+                /* This shim dll was already loaded, let's find it */
                 pShimModuleInfo = SeiGetShimModuleInfo(BaseAddress);
             }
             else if (!NT_SUCCESS(LdrLoadDll(NULL, NULL, &UnicodeDllName, &BaseAddress)))
@@ -863,6 +1068,7 @@ VOID SeiInit(PUNICODE_STRING ProcessImage, HSDB hsdb, SDBQUERYRESULT* pQuery)
                 SHIMENG_WARN("Failed to load %wZ for %S\n", &UnicodeDllName, ShimName);
                 continue;
             }
+            /* No shim module found (or we just loaded it) */
             if (!pShimModuleInfo)
             {
                 pShimModuleInfo = SeiCreateShimModuleInfo(DllName, BaseAddress);
@@ -876,16 +1082,20 @@ VOID SeiInit(PUNICODE_STRING ProcessImage, HSDB hsdb, SDBQUERYRESULT* pQuery)
             SHIMENG_INFO("Shim DLL 0x%p \"%wZ\" loaded\n", BaseAddress, &UnicodeDllName);
             SHIMENG_INFO("Using SHIM \"%S!%S\"\n", DllName, ShimName);
 
+            /* Ask this shim what hooks it needs (and pass along the commandline) */
             pHookApi = pShimModuleInfo->pGetHookAPIs(AnsiCommandLine.Buffer, ShimName, &dwHookCount);
             SHIMENG_INFO("GetHookAPIs returns %d hooks for DLL \"%wZ\" SHIM \"%S\"\n", dwHookCount, &UnicodeDllName, ShimName);
             if (dwHookCount)
-                SeiAppendHookInfo(pShimModuleInfo, pHookApi, dwHookCount);
+                pShimInfo = SeiAppendHookInfo(pShimModuleInfo, pHookApi, dwHookCount, ShimName);
+
+            /* If this shim has hooks, create the include / exclude lists */
+            if (pShimInfo)
+                SeiBuildInclExclList(pdb, ShimTag, pShimInfo);
 
             if (CommandLine && *CommandLine)
                 RtlFreeAnsiString(&AnsiCommandLine);
 
             dwTotalHooks += dwHookCount;
-            /*SeiBuildInclExclList*/
         }
     }
 
@@ -895,7 +1105,7 @@ VOID SeiInit(PUNICODE_STRING ProcessImage, HSDB hsdb, SDBQUERYRESULT* pQuery)
 }
 
 
-
+/* Load the database + unpack the shim data (if this process is allowed) */
 BOOL SeiGetShimData(PUNICODE_STRING ProcessImage, PVOID pShimData, HSDB* pHsdb, SDBQUERYRESULT* pQuery)
 {
     static const UNICODE_STRING ForbiddenShimmingApps[] = {
index ddcb09c..c07b183 100644 (file)
@@ -19,7 +19,7 @@
 #ifndef SHIMENG_H
 #define SHIMENG_H
 
-/* ReactOS specific */
+/* This header is ReactOS specific */
 
 /* Structure that allows dynamic growing.
    Be aware, the data may move! */
@@ -48,12 +48,19 @@ typedef struct tagHOOKAPIEX
 C_ASSERT(sizeof(HOOKAPIEX) == sizeof(HOOKAPI));
 C_ASSERT(offsetof(HOOKAPIEX, pShimInfo) == offsetof(HOOKAPI, Reserved));
 
+typedef struct _INEXCLUDE
+{
+    UNICODE_STRING Module;
+    BOOL Include;
+} INEXCLUDE, *PINEXCLUDE;
 
 typedef struct _SHIMINFO
 {
+    PCWSTR ShimName;
     PHOOKAPIEX pHookApi;
     DWORD dwHookCount;
     PSHIMMODULE pShimModule;
+    ARRAY InExclude;        /* INEXCLUDE */
 } SHIMINFO, *PSHIMINFO;
 
 typedef struct _SHIMMODULE
index 9cc1332..a8caa7c 100644 (file)
@@ -4,6 +4,10 @@
         <OS_PLATFORM >1</OS_PLATFORM>
         <DATABASE_ID>{11111111-1111-1111-1111-111111111111}</DATABASE_ID>
         <LIBRARY>
+            <INCLUDE MODULE="kernel32.dll" />
+            <INCLUDE MODULE="msvcrt.dll" />
+            <INCLUDE MODULE="ole32.dll" />
+            <INCLUDE MODULE="oleaut32.dll" />
 
             <!-- Version lie shims -->