/*
* Copyright 2011 André Hentschel
* Copyright 2013 Mislav Blažević
- * Copyright 2015 Mark Jansen
+ * Copyright 2015-2017 Mark Jansen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "winver.h"
#include "strsafe.h"
#include "apphelp.h"
+#include "ndk/rtlfuncs.h"
+#include "ndk/kdtypes.h"
-#include "wine/winternl.h"
/* from dpfilter.h */
#define DPFLTR_APPCOMPAT_ID 123
#endif
ULONG g_ShimDebugLevel = 0xffffffff;
+HMODULE g_hInstance;
void ApphelppInitDebugLevel(void)
{
- UNICODE_STRING DebugKey, DebugValue;
+ static const UNICODE_STRING DebugKey = RTL_CONSTANT_STRING(L"SHIM_DEBUG_LEVEL");
+ UNICODE_STRING DebugValue;
NTSTATUS Status;
ULONG NewLevel = SHIM_ERR;
WCHAR Buffer[40];
- RtlInitUnicodeString(&DebugKey, L"SHIM_DEBUG_LEVEL");
- DebugValue.MaximumLength = sizeof(Buffer);
- DebugValue.Buffer = Buffer;
- DebugValue.Length = 0;
+ RtlInitEmptyUnicodeString(&DebugValue, Buffer, sizeof(Buffer));
- /* Hold the lock as short as possible. */
- RtlAcquirePebLock();
Status = RtlQueryEnvironmentVariable_U(NULL, &DebugKey, &DebugValue);
- RtlReleasePebLock();
if (NT_SUCCESS(Status))
{
g_ShimDebugLevel = NewLevel;
}
+
BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
{
switch (reason)
return FALSE; /* prefer native version */
#endif
case DLL_PROCESS_ATTACH:
+ g_hInstance = hinst;
DisableThreadLibraryCalls( hinst );
SdbpHeapInit();
break;
break;
}
StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s][%-20s] ", LevelStr, FunctionName);
+
va_start(ArgList, Format);
StringCchVPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, Format, ArgList);
va_end(ArgList);
+
#if defined(APPCOMPAT_USE_DBGPRINTEX) && APPCOMPAT_USE_DBGPRINTEX
return NT_SUCCESS(DbgPrintEx(DPFLTR_APPCOMPAT_ID, Level, "%s", Buffer));
#else
#endif
}
+
+#define APPHELP_DONTWRITE_REASON 2
+#define APPHELP_CLEARBITS 0x100 /* TODO: Investigate */
+#define APPHELP_IGNORE_ENVIRONMENT 0x400
+
+#define APPHELP_VALID_RESULT 0x10000
+#define APPHELP_RESULT_NOTFOUND 0x20000
+#define APPHELP_RESULT_FOUND 0x40000
+
+/**
+ * Lookup Shims / Fixes for the specified application
+ *
+ * @param [in] FileHandle Handle to the file to check.
+ * @param [in] Unk1
+ * @param [in] Unk2
+ * @param [in] ApplicationName Exe to check
+ * @param [in] Environment The environment variables to use, or NULL to use the current environment.
+ * @param [in] ExeType Exe type (MACHINE_TYPE_XXXX)
+ * @param [in,out] Reason Input/output flags
+ * @param [in] SdbQueryAppCompatData The resulting data.
+ * @param [in] SdbQueryAppCompatDataSize The resulting data size.
+ * @param [in] SxsData TODO
+ * @param [in] SxsDataSize TODO
+ * @param [in] FusionFlags TODO
+ * @param [in] SomeFlag1 TODO
+ * @param [in] SomeFlag2 TODO
+ *
+ * @return TRUE if the application is allowed to run.
+ */
+BOOL
+WINAPI
+ApphelpCheckRunAppEx(
+ _In_ HANDLE FileHandle,
+ _In_opt_ PVOID Unk1,
+ _In_opt_ PVOID Unk2,
+ _In_opt_z_ PWCHAR ApplicationName,
+ _In_opt_ PVOID Environment,
+ _In_opt_ USHORT ExeType,
+ _Inout_opt_ PULONG Reason,
+ _Out_opt_ PVOID* SdbQueryAppCompatData,
+ _Out_opt_ PULONG SdbQueryAppCompatDataSize,
+ _Out_opt_ PVOID* SxsData,
+ _Out_opt_ PULONG SxsDataSize,
+ _Out_opt_ PULONG FusionFlags,
+ _Out_opt_ PULONG64 SomeFlag1,
+ _Out_opt_ PULONG SomeFlag2)
+{
+ SDBQUERYRESULT* result = NULL;
+ HSDB hsdb = NULL;
+ DWORD dwFlags = 0;
+
+ if (SxsData)
+ *SxsData = NULL;
+ if (SxsDataSize)
+ *SxsDataSize = 0;
+ if (FusionFlags)
+ *FusionFlags = 0;
+ if (SomeFlag1)
+ *SomeFlag1 = 0;
+ if (SomeFlag2)
+ *SomeFlag2 = 0;
+ if (Reason)
+ dwFlags = *Reason;
+
+ dwFlags &= ~APPHELP_CLEARBITS;
+
+ *SdbQueryAppCompatData = result = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SDBQUERYRESULT));
+ if (SdbQueryAppCompatDataSize)
+ *SdbQueryAppCompatDataSize = sizeof(*result);
+
+
+ hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL);
+ if (hsdb)
+ {
+ BOOL FoundMatch;
+ DWORD MatchingExeFlags = 0;
+
+ if (dwFlags & APPHELP_IGNORE_ENVIRONMENT)
+ MatchingExeFlags |= SDBGMEF_IGNORE_ENVIRONMENT;
+
+ FoundMatch = SdbGetMatchingExe(hsdb, ApplicationName, NULL, Environment, MatchingExeFlags, result);
+ if (FileHandle != INVALID_HANDLE_VALUE)
+ {
+ dwFlags |= APPHELP_VALID_RESULT;
+ dwFlags |= (FoundMatch ? APPHELP_RESULT_FOUND : APPHELP_RESULT_NOTFOUND);
+ }
+
+ SdbReleaseDatabase(hsdb);
+ }
+
+ if (Reason && !(dwFlags & APPHELP_DONTWRITE_REASON))
+ *Reason = dwFlags;
+
+
+ /* We should _ALWAYS_ return TRUE here, unless we want to block an application from starting! */
+ return TRUE;
+}
+
+
/*
* Copyright 2011 André Hentschel
* Copyright 2013 Mislav Blažević
- * Copyright 2015,2016 Mark Jansen
+ * Copyright 2015-2017 Mark Jansen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "wine/unicode.h"
+#define MAX_LAYER_LENGTH 256
+#define GPLK_USER 1
+#define GPLK_MACHINE 2
static BOOL WINAPI SdbpFileExists(LPCWSTR path)
{
return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY));
}
+static BOOL WINAPI SdbpMatchExe(PDB db, TAGID exe, WCHAR* dir)
+{
+ static const WCHAR fmt[] = {'%','s','%','s',0};
+ WCHAR buffer[256]; /* FIXME: rewrite using a buffer that can grow if needed, f.e. RtlInitBuffer stuff! */
+ TAGID matching_file;
+
+ /* TODO: check size/checksum from the main exe as well as from the extra files */
+ for (matching_file = SdbFindFirstTag(db, exe, TAG_MATCHING_FILE);
+ matching_file != TAGID_NULL; matching_file = SdbFindNextTag(db, exe, matching_file))
+ {
+ TAGID tagName = SdbFindFirstTag(db, matching_file, TAG_NAME);
+ LPWSTR name = SdbGetStringTagPtr(db, tagName);
+
+ if (!wcscmp(name, L"*"))
+ {
+ // if attributes dont match main file, return FALSE!
+ continue;
+ }
+
+ snprintfW(buffer, _countof(buffer), fmt, dir, name);
+ if (!SdbpFileExists(buffer))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void SdbpAddDatabaseGuid(PDB db, PSDBQUERYRESULT result)
+{
+ size_t n;
+
+ for (n = 0; n < _countof(result->rgGuidDB); ++n)
+ {
+ if (!memcmp(&result->rgGuidDB[n], &db->database_id, sizeof(db->database_id)))
+ return;
+
+ if (result->dwCustomSDBMap & (1<<n))
+ continue;
+
+ memcpy(&result->rgGuidDB[n], &db->database_id, sizeof(result->rgGuidDB[n]));
+ result->dwCustomSDBMap |= (1<<n);
+ return;
+ }
+}
+
+static BOOL SdbpAddSingleLayerMatch(TAGREF layer, PSDBQUERYRESULT result)
+{
+ size_t n;
+
+ for (n = 0; n < result->dwLayerCount; ++n)
+ {
+ if (result->atrLayers[n] == layer)
+ return FALSE;
+ }
+
+ if (n >= _countof(result->atrLayers))
+ return FALSE;
+
+ result->atrLayers[n] = layer;
+ result->dwLayerCount++;
+
+ return TRUE;
+}
+
+
+static BOOL SdbpAddNamedLayerMatch(HSDB hsdb, PCWSTR layerName, PSDBQUERYRESULT result)
+{
+ TAGID database, layer;
+ TAGREF tr;
+ PDB db = hsdb->db;
+
+ database = SdbFindFirstTag(db, TAGID_ROOT, TAG_DATABASE);
+ if (database == TAGID_NULL)
+ return FALSE;
+
+ layer = SdbFindFirstNamedTag(db, database, TAG_LAYER, TAG_NAME, layerName);
+ if (layer == TAGID_NULL)
+ return FALSE;
+
+ if (!SdbTagIDToTagRef(hsdb, db, layer, &tr))
+ return FALSE;
+
+ if (!SdbpAddSingleLayerMatch(tr, result))
+ return FALSE;
+
+ SdbpAddDatabaseGuid(db, result);
+ return TRUE;
+}
+
+static void SdbpAddExeLayers(HSDB hsdb, PDB db, TAGID tagExe, PSDBQUERYRESULT result)
+{
+ TAGID layer = SdbFindFirstTag(db, tagExe, TAG_LAYER);
+
+ while (layer != TAGID_NULL)
+ {
+ TAGREF tr;
+ TAGID layerIdTag = SdbFindFirstTag(db, layer, TAG_LAYER_TAGID);
+ DWORD tagId = SdbReadDWORDTag(db, layerIdTag, TAGID_NULL);
+
+ if (layerIdTag != TAGID_NULL &&
+ tagId != TAGID_NULL &&
+ SdbTagIDToTagRef(hsdb, db, tagId, &tr))
+ {
+ SdbpAddSingleLayerMatch(tr, result);
+ }
+ else
+ {
+ /* Try a name lookup */
+ TAGID layerTag = SdbFindFirstTag(db, layer, TAG_NAME);
+ if (layerTag != TAGID_NULL)
+ {
+ LPCWSTR layerName = SdbGetStringTagPtr(db, layerTag);
+ if (layerName)
+ {
+ SdbpAddNamedLayerMatch(hsdb, layerName, result);
+ }
+ }
+ }
+
+ layer = SdbFindNextTag(db, tagExe, layer);
+ }
+}
+
+static void SdbpAddExeMatch(HSDB hsdb, PDB db, TAGID tagExe, PSDBQUERYRESULT result)
+{
+ size_t n;
+ TAGREF tr;
+
+ if (!SdbTagIDToTagRef(hsdb, db, tagExe, &tr))
+ return;
+
+ for (n = 0; n < result->dwExeCount; ++n)
+ {
+ if (result->atrExes[n] == tr)
+ return;
+ }
+
+ if (n >= _countof(result->atrExes))
+ return;
+
+ result->atrExes[n] = tr;
+ result->dwExeCount++;
+
+ SdbpAddExeLayers(hsdb, db, tagExe, result);
+
+ SdbpAddDatabaseGuid(db, result);
+}
+
+static ULONG SdbpAddLayerMatches(HSDB hsdb, PWSTR pwszLayers, DWORD pdwBytes, PSDBQUERYRESULT result)
+{
+ PWSTR start = pwszLayers, p;
+ ULONG Added = 0;
+
+ const PWSTR end = pwszLayers + (pdwBytes / sizeof(WCHAR));
+ while (start < end && (*start == L'!' || *start == L'#' || *start == L' ' || *start == L'\t'))
+ start++;
+
+ if (start == end)
+ return 0;
+
+ do
+ {
+ while (*start == L' ' || *start == L'\t')
+ ++start;
+
+ if (*start == UNICODE_NULL)
+ break;
+ p = wcspbrk(start, L" \t");
+
+ if (p)
+ *p = UNICODE_NULL;
+
+ if (SdbpAddNamedLayerMatch(hsdb, start, result))
+ Added++;
+
+ start = p + 1;
+ } while (start < end && p);
+
+ return Added;
+}
+
+static BOOL SdbpPropagateEnvLayers(HSDB hsdb, LPWSTR Environment, PSDBQUERYRESULT Result)
+{
+ static const UNICODE_STRING EnvKey = RTL_CONSTANT_STRING(L"__COMPAT_LAYER");
+ UNICODE_STRING EnvValue;
+ NTSTATUS Status;
+ WCHAR Buffer[MAX_LAYER_LENGTH];
+
+ RtlInitEmptyUnicodeString(&EnvValue, Buffer, sizeof(Buffer));
+
+ Status = RtlQueryEnvironmentVariable_U(Environment, &EnvKey, &EnvValue);
+
+ if (!NT_SUCCESS(Status))
+ return FALSE;
+
+ return SdbpAddLayerMatches(hsdb, Buffer, EnvValue.Length, Result) > 0;
+}
+
+
/**
* Opens specified shim database file Handle returned by this function may only be used by
static const WCHAR drivers[] = {'\\','d','r','v','m','a','i','n','.','s','d','b',0};
LPCWSTR name;
WCHAR buffer[128];
- HSDB sdb;
+ HSDB hsdb;
- sdb = SdbAlloc(sizeof(SDB));
- if (!sdb)
+ hsdb = SdbAlloc(sizeof(SDB));
+ if (!hsdb)
return NULL;
- sdb->auto_loaded = 0;
+ hsdb->auto_loaded = 0;
/* Check for predefined databases */
if ((flags & HID_DATABASE_TYPE_MASK) && path == NULL)
case SDB_DATABASE_MAIN_MSI: name = msi; break;
case SDB_DATABASE_MAIN_DRIVERS: name = drivers; break;
default:
- SdbReleaseDatabase(sdb);
+ SdbReleaseDatabase(hsdb);
return NULL;
}
SdbGetAppPatchDir(NULL, buffer, 128);
- memcpy(buffer + lstrlenW(buffer), name, SdbpStrlen(name));
+ memcpy(buffer + lstrlenW(buffer), name, SdbpStrsize(name));
+ flags = HID_DOS_PATHS;
}
- sdb->db = SdbOpenDatabase(path ? path : buffer, (flags & 0xF) - 1);
+ hsdb->db = SdbOpenDatabase(path ? path : buffer, (flags & 0xF) - 1);
/* If database could not be loaded, a handle doesn't make sense either */
- if (!sdb->db)
+ if (!hsdb->db)
{
- SdbReleaseDatabase(sdb);
+ SdbReleaseDatabase(hsdb);
return NULL;
}
- return sdb;
+ return hsdb;
}
/**
* @param [in] hsdb Handle to the shim database.
* @param [in] path Path to executable for which we query database.
* @param [in] module_name Unused.
- * @param [in] env Unused.
+ * @param [in] env The environment block to use
* @param [in] flags 0 or SDBGMEF_IGNORE_ENVIRONMENT.
* @param [out] result Pointer to structure in which query result shall be stored.
*
BOOL WINAPI SdbGetMatchingExe(HSDB hsdb, LPCWSTR path, LPCWSTR module_name,
LPCWSTR env, DWORD flags, PSDBQUERYRESULT result)
{
- static const WCHAR fmt[] = {'%','s','%','s',0};
- BOOL ok, ret;
- TAGID database, iter, attr;
+ BOOL ret = FALSE;
+ TAGID database, iter, name;
PATTRINFO attribs = NULL;
/*DWORD attr_count;*/
+ RTL_UNICODE_STRING_BUFFER DosApplicationName = { { 0 } };
+ WCHAR DosPathBuffer[MAX_PATH];
+ ULONG PathType = 0;
LPWSTR file_name;
- WCHAR dir_path[128];
- WCHAR buffer[256];
+ WCHAR wszLayers[MAX_LAYER_LENGTH];
+ DWORD dwSize;
PDB db;
/* Load default database if one is not specified */
/* To reproduce windows behaviour HID_DOS_PATHS needs
* to be specified when loading default database */
hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL);
- if(hsdb)
+ if (hsdb)
hsdb->auto_loaded = TRUE;
}
+ ZeroMemory(result, sizeof(*result));
+
/* No database could be loaded */
- if (!hsdb)
+ if (!hsdb || !path)
return FALSE;
+ /* We do not support multiple db's yet! */
db = hsdb->db;
+ RtlInitUnicodeString(&DosApplicationName.String, path);
+ RtlInitBuffer(&DosApplicationName.ByteBuffer, (PUCHAR)DosPathBuffer, sizeof(DosPathBuffer));
+ if (!NT_SUCCESS(RtlEnsureBufferSize(RTL_SKIP_BUFFER_COPY, &DosApplicationName.ByteBuffer, DosApplicationName.String.MaximumLength)))
+ {
+ SHIM_ERR("Failed to convert allocate buffer.");
+ goto Cleanup;
+ }
+ /* Update the internal buffer to contain the string */
+ memcpy(DosApplicationName.ByteBuffer.Buffer, path, DosApplicationName.String.MaximumLength);
+ /* Make sure the string uses our internal buffer (we want to modify the buffer,
+ and RtlNtPathNameToDosPathName does not always modify the String to point to the Buffer)! */
+ DosApplicationName.String.Buffer = (PWSTR)DosApplicationName.ByteBuffer.Buffer;
+
+ if (!NT_SUCCESS(RtlNtPathNameToDosPathName(0, &DosApplicationName, &PathType, NULL)))
+ {
+ SHIM_ERR("Failed to convert %S to DOS Path.", path);
+ goto Cleanup;
+ }
+
+
/* Extract file name */
- file_name = strrchrW(path, '\\') + 1;
+ file_name = strrchrW(DosApplicationName.String.Buffer, '\\');
+ if (!file_name)
+ {
+ SHIM_ERR("Failed to find Exe name in %wZ.", &DosApplicationName.String);
+ goto Cleanup;
+ }
- /* Extract directory path */
- memcpy(dir_path, path, (size_t)(file_name - path) * sizeof(WCHAR));
+ /* We will use the buffer for exe name and directory. */
+ *(file_name++) = UNICODE_NULL;
/* Get information about executable required to match it with database entry */
/*if (!SdbGetFileAttributes(path, &attribs, &attr_count))
/* DATABASE is list TAG which contains all executables */
database = SdbFindFirstTag(db, TAGID_ROOT, TAG_DATABASE);
if (database == TAGID_NULL)
- return FALSE;
+ {
+ goto Cleanup;
+ }
/* EXE is list TAG which contains data required to match executable */
iter = SdbFindFirstTag(db, database, TAG_EXE);
- /* Search for entry in database */
+ /* Search for entry in database, we should look into indexing tags! */
while (iter != TAGID_NULL)
{
+ LPWSTR foundName;
/* Check if exe name matches */
- attr = SdbFindFirstTag(db, iter, TAG_NAME);
- if (lstrcmpiW(SdbGetStringTagPtr(db, attr), file_name) == 0)
+ name = SdbFindFirstTag(db, iter, TAG_NAME);
+ /* If this is a malformed DB, (no TAG_NAME), we should not crash. */
+ foundName = SdbGetStringTagPtr(db, name);
+ if (foundName && !lstrcmpiW(foundName, file_name))
{
- /* Assume that entry is found (in case there are no "matching files") */
- ok = TRUE;
-
- /* Check if all "matching files" exist */
- /* TODO: check size/checksum as well */
- for (attr = SdbFindFirstTag(db, attr, TAG_MATCHING_FILE);
- attr != TAGID_NULL; attr = SdbFindNextTag(db, iter, attr))
+ /* We have a null terminator before the application name, so DosApplicationName only contains the path. */
+ if (SdbpMatchExe(db, iter, DosApplicationName.String.Buffer))
{
- snprintfW(buffer, 256, fmt, dir_path, SdbGetStringTagPtr(db, attr));
- if (!SdbpFileExists(buffer))
- ok = FALSE;
- }
-
- /* Found it! */
- if (ok)
- {
- /* TODO: fill result data */
- /* TODO: there may be multiple matches */
ret = TRUE;
- goto cleanup;
+ SdbpAddExeMatch(hsdb, db, iter, result);
}
}
iter = SdbFindNextTag(db, database, iter);
}
- /* Exe not found */
- ret = FALSE;
+ /* Restore the full path. */
+ *(--file_name) = L'\\';
+
+ dwSize = sizeof(wszLayers);
+ if (SdbGetPermLayerKeys(DosApplicationName.String.Buffer, wszLayers, &dwSize, GPLK_MACHINE | GPLK_USER))
+ {
+ SdbpAddLayerMatches(hsdb, wszLayers, dwSize, result);
+ ret = TRUE;
+ }
+
+ if (!(flags & SDBGMEF_IGNORE_ENVIRONMENT))
+ {
+ if (SdbpPropagateEnvLayers(hsdb, (LPWSTR)env, result))
+ {
+ ret = TRUE;
+ result->dwFlags |= SHIMREG_HAS_ENVIRONMENT;
+ }
+ }
-cleanup:
- SdbFreeFileAttributes(attribs);
- if (hsdb->auto_loaded) SdbReleaseDatabase(hsdb);
+Cleanup:
+ RtlFreeBuffer(&DosApplicationName.ByteBuffer);
+ if (attribs)
+ SdbFreeFileAttributes(attribs);
+ if (hsdb->auto_loaded)
+ SdbReleaseDatabase(hsdb);
return ret;
}