1447 lines
40 KiB
C++
1447 lines
40 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (c) 2000 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
Win2kPropagateLayer.cpp
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This shim propagates a layer from a process to its child processes on Win2k.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
This is a layer shim.
|
||
|
|
||
|
History:
|
||
|
|
||
|
03/13/2000 clupu Created
|
||
|
10/26/2000 Vadimb Merged WowProcessHistory functionality, new environment-handling cases
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "precomp.h"
|
||
|
|
||
|
IMPLEMENT_SHIM_BEGIN(Win2kPropagateLayer)
|
||
|
#include "ShimHookMacro.h"
|
||
|
|
||
|
#include "Win2kPropagateLayer.h"
|
||
|
#include "stdio.h"
|
||
|
|
||
|
APIHOOK_ENUM_BEGIN
|
||
|
APIHOOK_ENUM_ENTRY(CreateProcessA)
|
||
|
APIHOOK_ENUM_ENTRY(CreateProcessW)
|
||
|
APIHOOK_ENUM_ENTRY(UserRegisterWowHandlers)
|
||
|
APIHOOK_ENUM_END
|
||
|
|
||
|
|
||
|
#define LI_WIN95 0x00000001
|
||
|
#define LI_NT4 0x00000002
|
||
|
#define LI_WIN98 0x00000004
|
||
|
|
||
|
#define LS_MAGIC 0x07036745
|
||
|
|
||
|
typedef struct tagLayerStorageHeader {
|
||
|
DWORD dwItemCount; // number of items in the file
|
||
|
DWORD dwMagic; // magic to identify the file
|
||
|
SYSTEMTIME timeLast; // time of last access
|
||
|
} LayerStorageHeader, *PLayerStorageHeader;
|
||
|
|
||
|
|
||
|
typedef struct tagLayeredItem {
|
||
|
WCHAR szItemName[MAX_PATH];
|
||
|
DWORD dwFlags;
|
||
|
|
||
|
} LayeredItem, *PLayeredItem;
|
||
|
|
||
|
|
||
|
|
||
|
#define APPCOMPAT_KEY L"System\\CurrentControlSet\\Control\\Session Manager\\AppCompatibility"
|
||
|
|
||
|
WCHAR g_szLayerStorage[MAX_PATH] = L"";
|
||
|
|
||
|
|
||
|
CHAR g_szCompatLayerVar[] = "__COMPAT_LAYER";
|
||
|
CHAR g_szProcessHistoryVar[] = "__PROCESS_HISTORY";
|
||
|
CHAR g_szShimFileLogVar[] = "SHIM_FILE_LOG";
|
||
|
|
||
|
|
||
|
WCHAR g_wszCompatLayerVar[] = L"__COMPAT_LAYER";
|
||
|
WCHAR g_wszProcessHistroyVar[] = L"__PROCESS_HISTORY";
|
||
|
//
|
||
|
// This variable receives current process' compat layer
|
||
|
//
|
||
|
|
||
|
WCHAR* g_pwszCompatLayer = NULL;
|
||
|
WCHAR* g_pwszProcessHistory = NULL;
|
||
|
|
||
|
//
|
||
|
// Unicode equivalent of the above
|
||
|
//
|
||
|
UNICODE_STRING g_ustrProcessHistoryVar = RTL_CONSTANT_STRING(L"__PROCESS_HISTORY");
|
||
|
UNICODE_STRING g_ustrCompatLayerVar = RTL_CONSTANT_STRING(L"__COMPAT_LAYER");
|
||
|
|
||
|
//
|
||
|
// Global flags
|
||
|
//
|
||
|
BOOL g_bIsNTVDM = FALSE;
|
||
|
BOOL g_bIsExplorer = FALSE;
|
||
|
|
||
|
INT g_argc = 0;
|
||
|
CHAR** g_argv = NULL;
|
||
|
|
||
|
//
|
||
|
// is this a separate wow ?
|
||
|
//
|
||
|
|
||
|
BOOL* g_pSeparateWow = NULL;
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
InitLayerStorage(
|
||
|
BOOL bDelete
|
||
|
)
|
||
|
{
|
||
|
if (GetSystemWindowsDirectoryW(g_szLayerStorage, MAX_PATH) >= MAX_PATH) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (lstrlenW(g_szLayerStorage) >= 1 && g_szLayerStorage[lstrlenW(g_szLayerStorage) - 1] == L'\\') {
|
||
|
g_szLayerStorage[lstrlenW(g_szLayerStorage) - 1] = 0;
|
||
|
}
|
||
|
|
||
|
if (FAILED(StringCchCatW(g_szLayerStorage, MAX_PATH, L"\\AppPatch\\LayerStorage.dat"))) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (bDelete) {
|
||
|
DeleteFileW(g_szLayerStorage);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ReadLayeredStorage(
|
||
|
LPWSTR pszItem,
|
||
|
LPDWORD lpdwFlags
|
||
|
)
|
||
|
{
|
||
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||
|
HANDLE hFileMapping = NULL;
|
||
|
DWORD dwFileSize;
|
||
|
PBYTE pData = NULL;
|
||
|
PLayerStorageHeader pHeader = NULL;
|
||
|
PLayeredItem pItems;
|
||
|
PLayeredItem pCrtItem = NULL;
|
||
|
int nLeft, nRight, nMid, nItem;
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[ReadLayeredStorage] for \"%S\"",
|
||
|
pszItem);
|
||
|
|
||
|
//
|
||
|
// Make sure we don't corrupt the layer storage.
|
||
|
//
|
||
|
if (lstrlenW(pszItem) + 1 > MAX_PATH) {
|
||
|
pszItem[MAX_PATH - 1] = 0;
|
||
|
}
|
||
|
|
||
|
hFile = CreateFileW(g_szLayerStorage,
|
||
|
GENERIC_READ | GENERIC_WRITE,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
0,
|
||
|
NULL);
|
||
|
|
||
|
if (hFile == INVALID_HANDLE_VALUE) {
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[ReadLayeredStorage] the layer storage doesn't exist.");
|
||
|
*lpdwFlags = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The file already exists. Create a file mapping that will allow
|
||
|
// for querying the item.
|
||
|
//
|
||
|
dwFileSize = GetFileSize(hFile, NULL);
|
||
|
|
||
|
hFileMapping = CreateFileMapping(hFile,
|
||
|
NULL,
|
||
|
PAGE_READWRITE,
|
||
|
0,
|
||
|
dwFileSize,
|
||
|
NULL);
|
||
|
|
||
|
if (hFileMapping == NULL) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ReadLayeredStorage] CreateFileMapping failed 0x%X",
|
||
|
GetLastError());
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
pData = (PBYTE)MapViewOfFile(hFileMapping,
|
||
|
FILE_MAP_READ | FILE_MAP_WRITE,
|
||
|
0,
|
||
|
0,
|
||
|
0);
|
||
|
|
||
|
if (pData == NULL) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ReadLayeredStorage] MapViewOfFile failed 0x%X",
|
||
|
GetLastError());
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
pHeader = (PLayerStorageHeader)pData;
|
||
|
|
||
|
pItems = (PLayeredItem)(pData + sizeof(LayerStorageHeader));
|
||
|
|
||
|
//
|
||
|
// Make sure it's our file.
|
||
|
//
|
||
|
if (dwFileSize < sizeof(LayerStorageHeader) || pHeader->dwMagic != LS_MAGIC) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ReadLayeredStorage] invalid file magic 0x%X",
|
||
|
pHeader->dwMagic);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// First search for the item. The array is sorted so we do binary search.
|
||
|
//
|
||
|
nItem = -1, nLeft = 0, nRight = (int)pHeader->dwItemCount - 1;
|
||
|
|
||
|
while (nLeft <= nRight) {
|
||
|
|
||
|
int nVal;
|
||
|
|
||
|
nMid = (nLeft + nRight) / 2;
|
||
|
|
||
|
pCrtItem = pItems + nMid;
|
||
|
|
||
|
nVal = _wcsnicmp(pszItem, pCrtItem->szItemName, lstrlenW(pCrtItem->szItemName));
|
||
|
|
||
|
if (nVal == 0) {
|
||
|
nItem = nMid;
|
||
|
break;
|
||
|
} else if (nVal < 0) {
|
||
|
nRight = nMid - 1;
|
||
|
} else {
|
||
|
nLeft = nMid + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (nItem == -1) {
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[ReadLayeredStorage] the item was not found in the file.");
|
||
|
|
||
|
*lpdwFlags = 0;
|
||
|
} else {
|
||
|
//
|
||
|
// The item is in the file.
|
||
|
//
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[ReadLayeredStorage] the item is in the file.");
|
||
|
|
||
|
*lpdwFlags = pCrtItem->dwFlags;
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
|
||
|
if (pData != NULL) {
|
||
|
UnmapViewOfFile(pData);
|
||
|
}
|
||
|
|
||
|
if (hFileMapping != NULL) {
|
||
|
CloseHandle(hFileMapping);
|
||
|
}
|
||
|
|
||
|
if (hFile != INVALID_HANDLE_VALUE) {
|
||
|
CloseHandle(hFile);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
GetFileNameFromCmdLine(
|
||
|
LPWSTR lpFileName,
|
||
|
DWORD dwFileNameSize,
|
||
|
LPCWSTR lpCmdLine
|
||
|
)
|
||
|
{
|
||
|
LPCWSTR pSrc = lpCmdLine;
|
||
|
LPCWSTR pStart;
|
||
|
BOOL bQuote = FALSE;
|
||
|
BOOL bInitialQuote = FALSE;
|
||
|
BOOL bDone = FALSE;
|
||
|
DWORD dwLength; // length of the result, in chars
|
||
|
|
||
|
pSrc += wcsspn(pSrc, L" \t");
|
||
|
if (*pSrc == L'\"') {
|
||
|
++pSrc;
|
||
|
bQuote = TRUE;
|
||
|
bInitialQuote = TRUE;
|
||
|
}
|
||
|
|
||
|
pStart = pSrc; // note -- we're past the quote
|
||
|
|
||
|
// we end when: 1) we start we the quote -- we end with the quote or
|
||
|
// we did not start with the quote -- we encounter space then
|
||
|
|
||
|
while (*pSrc && !bDone) {
|
||
|
switch(*pSrc) {
|
||
|
case L'\"':
|
||
|
bQuote = !bQuote;
|
||
|
break;
|
||
|
|
||
|
case L' ':
|
||
|
bDone = !bQuote; // out of quotes? this is the end
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!bDone) {
|
||
|
++pSrc;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pSrc > pStart && bInitialQuote && *(pSrc-1) == L'\"') {
|
||
|
--pSrc;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// now that we ended the run, copy
|
||
|
//
|
||
|
dwLength = (DWORD)(pSrc - pStart);
|
||
|
|
||
|
if (dwFileNameSize < (dwLength + 1)) {
|
||
|
// too big
|
||
|
LOGN( eDbgLevelError,
|
||
|
"[GetFileNameFromCmdLine] filename is too long\"%S\".\n", lpCmdLine);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(lpFileName, pStart, dwLength * sizeof(WCHAR));
|
||
|
lpFileName[dwLength] = L'\0';
|
||
|
return TRUE;
|
||
|
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
AddSupport(
|
||
|
LPCWSTR lpCommandLine,
|
||
|
LPVOID* ppEnvironment,
|
||
|
LPDWORD lpdwCreationFlags
|
||
|
)
|
||
|
{
|
||
|
WCHAR szKey[MAX_PATH];
|
||
|
WCHAR szFullPath[MAX_PATH + 3] = L"\"";
|
||
|
WCHAR szExeName[MAX_PATH + 1];
|
||
|
HKEY hkey;
|
||
|
DWORD type;
|
||
|
DWORD cbData = 0;
|
||
|
BOOL bBraket = FALSE;
|
||
|
LPVOID pEnvironmentNew = NULL;
|
||
|
DWORD dwCreationFlags = *lpdwCreationFlags;
|
||
|
BOOL bUserEnvironment = (*ppEnvironment != NULL);
|
||
|
NTSTATUS Status;
|
||
|
LPCWSTR pszEnd;
|
||
|
LPCWSTR pszStart = lpCommandLine;
|
||
|
|
||
|
//
|
||
|
// Need to look in lpCommandLine for the first token
|
||
|
//
|
||
|
LPCWSTR psz = lpCommandLine;
|
||
|
|
||
|
while (*psz == L' ' || *psz == L'\t') {
|
||
|
psz++;
|
||
|
}
|
||
|
|
||
|
if (*psz == L'\"') {
|
||
|
pszStart = psz + 1;
|
||
|
bBraket = TRUE;
|
||
|
} else {
|
||
|
pszStart = psz;
|
||
|
}
|
||
|
|
||
|
while (*psz != 0) {
|
||
|
if (*psz == L'\"') {
|
||
|
bBraket = !bBraket;
|
||
|
} else if (*psz == L' ' && !bBraket) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
psz++;
|
||
|
}
|
||
|
|
||
|
pszEnd = psz;
|
||
|
|
||
|
//
|
||
|
// Now walk back to get the caracters.
|
||
|
//
|
||
|
psz--;
|
||
|
|
||
|
// Be careful not to walk back past the beginning of the command line.
|
||
|
if (psz > lpCommandLine && *psz == L'\"') {
|
||
|
psz--;
|
||
|
pszEnd--;
|
||
|
}
|
||
|
|
||
|
// Don't under- or over-flow.
|
||
|
// szFullPath is of size MAX_PATH + 3. We can copy in:
|
||
|
// MAX_PATH + 3 - 2 (quotes) - 1 (NULL) = MAX_PATH characters.
|
||
|
if( pszEnd <= pszStart || pszEnd - pszStart > MAX_PATH ) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
memcpy(szFullPath + 1, pszStart, (pszEnd - pszStart) * sizeof(WCHAR));
|
||
|
szFullPath[pszEnd - pszStart + 1] = L'\"';
|
||
|
szFullPath[pszEnd - pszStart + 2] = 0;
|
||
|
|
||
|
pszStart = lpCommandLine;
|
||
|
|
||
|
pszEnd = psz + 1;
|
||
|
|
||
|
while (psz >= lpCommandLine) {
|
||
|
if (*psz == L'\\') {
|
||
|
pszStart = psz + 1;
|
||
|
break;
|
||
|
}
|
||
|
psz--;
|
||
|
}
|
||
|
|
||
|
// We already know that pszEnd - pszStart is <= MAX_PATH because, since the
|
||
|
// test above, we have only possibly reduced the size. Since szExeName is of
|
||
|
// size MAX_PATH + 1, we are okay to do the memcopy.
|
||
|
memcpy(szExeName, pszStart, (pszEnd - pszStart) * sizeof(WCHAR));
|
||
|
szExeName[pszEnd - pszStart] = 0;
|
||
|
|
||
|
if (g_bIsExplorer) {
|
||
|
DWORD dwFlags = 0;
|
||
|
|
||
|
ReadLayeredStorage(szFullPath, &dwFlags);
|
||
|
|
||
|
if (dwFlags != LI_WIN95 && dwFlags != LI_NT4 && dwFlags != LI_WIN98) {
|
||
|
//
|
||
|
// no layer support
|
||
|
//
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[AddSupport] No Layer specified for \"%S\".",
|
||
|
lpCommandLine);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// we are using layer -- clone the environment
|
||
|
Status = ShimCloneEnvironment(&pEnvironmentNew, *ppEnvironment, !!(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT));
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[AddSupport] Failed to Clone the environment. Status = 0x%x",
|
||
|
Status);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (LI_WIN95 == dwFlags) {
|
||
|
Status = ShimSetEnvironmentVar(&pEnvironmentNew, g_wszCompatLayerVar, L"Win95");
|
||
|
|
||
|
LOGN( eDbgLevelInfo, "[AddSupport] Env var \"Win95\" added.");
|
||
|
|
||
|
} else if (LI_WIN98 == dwFlags) {
|
||
|
Status = ShimSetEnvironmentVar(&pEnvironmentNew, g_wszCompatLayerVar, L"Win98");
|
||
|
|
||
|
LOGN( eDbgLevelInfo, "[AddSupport] Env var \"Win98\" added.");
|
||
|
|
||
|
} else if (LI_NT4 == dwFlags) {
|
||
|
Status = ShimSetEnvironmentVar(&pEnvironmentNew, g_wszCompatLayerVar, L"NT4SP5");
|
||
|
|
||
|
LOGN( eDbgLevelInfo, "[AddSupport] Env var \"NT4SP5\" added.");
|
||
|
|
||
|
}
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[AddSupport] Failed to set the environment variable. Status = 0x%x",
|
||
|
Status);
|
||
|
ShimFreeEnvironment(pEnvironmentNew);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We have succeeded, set the output values.
|
||
|
//
|
||
|
*ppEnvironment = pEnvironmentNew;
|
||
|
*lpdwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// not explorer - set the environment variable up
|
||
|
// compat_layer will be inherited by the child process if bUserEnvironment is FALSE
|
||
|
//
|
||
|
if (bUserEnvironment) {
|
||
|
|
||
|
//
|
||
|
// Clone the environment and add the layer variable to the new env.
|
||
|
//
|
||
|
Status = ShimCloneEnvironment(&pEnvironmentNew,
|
||
|
*ppEnvironment,
|
||
|
!!(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT));
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[AddSupport] Failed to clone the environment. Status = 0x%x",
|
||
|
Status);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Status = ShimSetEnvironmentVar(&pEnvironmentNew,
|
||
|
g_wszCompatLayerVar,
|
||
|
g_pwszCompatLayer);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ShimFreeEnvironment(pEnvironmentNew);
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[AddSupport] Failed to set compat layer variable. Status = 0x%x",
|
||
|
Status);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[AddSupport] Env var \"%S\" added.",
|
||
|
g_pwszCompatLayer);
|
||
|
|
||
|
*ppEnvironment = pEnvironmentNew;
|
||
|
*lpdwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Build the registry key.
|
||
|
//
|
||
|
if( FAILED(StringCchPrintfW(szKey, MAX_PATH, L"%s\\%s", APPCOMPAT_KEY, szExeName)) )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (RegCreateKeyW(HKEY_LOCAL_MACHINE, szKey, &hkey) != ERROR_SUCCESS) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"Failed to open/create the appcompat key \"%s\"",
|
||
|
szKey);
|
||
|
} else {
|
||
|
if (RegQueryValueExA(hkey, "DllPatch-x", NULL, &type, NULL, &cbData) != ERROR_SUCCESS) {
|
||
|
|
||
|
BYTE data[16] = {0x0c, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0x06, 0, 0, 0, 0, 0, 0, 0};
|
||
|
|
||
|
//
|
||
|
// The value doesn't exist. Create it.
|
||
|
//
|
||
|
RegSetValueExA(hkey,
|
||
|
"y",
|
||
|
NULL,
|
||
|
REG_BINARY,
|
||
|
data,
|
||
|
sizeof(data));
|
||
|
|
||
|
data[0] = 0;
|
||
|
|
||
|
RegSetValueExA(hkey,
|
||
|
"DllPatch-y",
|
||
|
NULL,
|
||
|
REG_SZ,
|
||
|
data,
|
||
|
2);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RegCloseKey(hkey);
|
||
|
|
||
|
//
|
||
|
// Finally, set a separate vdm flag
|
||
|
// if we are here, it means that we are running under the layer
|
||
|
// and the next exe is going to be shimmed.
|
||
|
//
|
||
|
*lpdwCreationFlags &= ~CREATE_SHARED_WOW_VDM;
|
||
|
*lpdwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
LPVOID
|
||
|
ShimCreateWowEnvironment_U(
|
||
|
LPVOID lpEnvironment, // pointer to the existing environment
|
||
|
DWORD* lpdwFlags, // process creation flags
|
||
|
BOOL bNewEnvironment // when set, forces us to clone environment ptr
|
||
|
)
|
||
|
{
|
||
|
WOWENVDATA WowEnvData = { 0 };
|
||
|
LPVOID lpEnvRet = lpEnvironment;
|
||
|
LPVOID lpEnvCurrent = NULL;
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
DWORD dwFlags = *lpdwFlags;
|
||
|
UNICODE_STRING ustrProcessHistory = { 0 };
|
||
|
ANSI_STRING strProcessHistory = { 0 };
|
||
|
DWORD dwProcessHistoryLength = 0;
|
||
|
UNICODE_STRING ustrCompatLayer = { 0 };
|
||
|
ANSI_STRING strCompatLayer = { 0 };
|
||
|
|
||
|
if (!ShimRetrieveVariablesEx(&WowEnvData)) {
|
||
|
//
|
||
|
// If no data, we have failed. Return the current data.
|
||
|
//
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
if (bNewEnvironment) {
|
||
|
Status = ShimCloneEnvironment(&lpEnvCurrent,
|
||
|
lpEnvironment,
|
||
|
!!(dwFlags & CREATE_UNICODE_ENVIRONMENT));
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ShimCreateWowEnvironment_U] Failed to clone the environment. Status = 0x%x",
|
||
|
Status);
|
||
|
goto Fail;
|
||
|
}
|
||
|
} else {
|
||
|
lpEnvCurrent = lpEnvironment;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now we are ready to set the environment in place.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// Nuke the existing process history first. We don't care for the return result.
|
||
|
//
|
||
|
RtlSetEnvironmentVariable(&lpEnvCurrent, &g_ustrProcessHistoryVar, NULL);
|
||
|
|
||
|
if (WowEnvData.pszProcessHistory != NULL ||
|
||
|
WowEnvData.pszCurrentProcessHistory != NULL) {
|
||
|
|
||
|
//
|
||
|
// Convert the process history which consists of 2 strings.
|
||
|
//
|
||
|
// The length is the existing process history length + 1 (for ';') +
|
||
|
// new process history length + 1 (for '\0')
|
||
|
//
|
||
|
dwProcessHistoryLength = ((WowEnvData.pszProcessHistory == NULL) ? 0 : (strlen(WowEnvData.pszProcessHistory) + 1)) +
|
||
|
((WowEnvData.pszCurrentProcessHistory == NULL) ? 0 : strlen(WowEnvData.pszCurrentProcessHistory)) + 1;
|
||
|
|
||
|
//
|
||
|
// Allocate process history buffer and convert it, allocating resulting unicode string.
|
||
|
//
|
||
|
strProcessHistory.Buffer = (PCHAR)ShimMalloc(dwProcessHistoryLength);
|
||
|
|
||
|
if (strProcessHistory.Buffer == NULL) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ShimCreateWowEnvironment_U] failed to allocate %d bytes for process history.",
|
||
|
dwProcessHistoryLength);
|
||
|
Status = STATUS_NO_MEMORY;
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
strProcessHistory.MaximumLength = (USHORT)dwProcessHistoryLength;
|
||
|
|
||
|
if (WowEnvData.pszProcessHistory != NULL) {
|
||
|
// This StringCpy won't fail because strProcessHistory.Buffer has been allocated to the proper length.
|
||
|
StringCchCopyA(strProcessHistory.Buffer, dwProcessHistoryLength, WowEnvData.pszProcessHistory);
|
||
|
strProcessHistory.Length = strlen(WowEnvData.pszProcessHistory);
|
||
|
} else {
|
||
|
strProcessHistory.Length = 0;
|
||
|
}
|
||
|
|
||
|
if (WowEnvData.pszCurrentProcessHistory != NULL) {
|
||
|
|
||
|
//
|
||
|
// Append ';' if the string was not empty.
|
||
|
//
|
||
|
if (strProcessHistory.Length) {
|
||
|
Status = RtlAppendAsciizToString(&strProcessHistory, ";");
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ShimCreateWowEnvironment_U] failed to append ';' to the process history. Status = 0x%x",
|
||
|
Status);
|
||
|
goto Fail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Status = RtlAppendAsciizToString(&strProcessHistory,
|
||
|
WowEnvData.pszCurrentProcessHistory);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ShimCreateWowEnvironment_U] failed to build the process history. Status = 0x%x",
|
||
|
Status);
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Convert the process history.
|
||
|
//
|
||
|
Status = RtlAnsiStringToUnicodeString(&ustrProcessHistory, &strProcessHistory, TRUE);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ShimCreateWowEnvironment_U] failed to convert process history to UNICODE. Status = 0x%x",
|
||
|
Status);
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now we can set the process history.
|
||
|
//
|
||
|
Status = RtlSetEnvironmentVariable(&lpEnvCurrent,
|
||
|
&g_ustrProcessHistoryVar,
|
||
|
&ustrProcessHistory);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ShimCreateWowEnvironment_U] failed to set the process history. Status = 0x%x",
|
||
|
Status);
|
||
|
goto Fail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now we pass along any compat layer that we might have.
|
||
|
//
|
||
|
if (g_pwszCompatLayer != NULL) {
|
||
|
|
||
|
//
|
||
|
// Pass along this thing, we have been started under layer.
|
||
|
//
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[ShimCreateWowEnvironment_U] Propagating CompatLayer from the ntvdm environment __COMPAT_LAYER=\"%S\"",
|
||
|
g_pwszCompatLayer);
|
||
|
|
||
|
RtlInitUnicodeString(&ustrCompatLayer, g_pwszCompatLayer);
|
||
|
|
||
|
Status = RtlSetEnvironmentVariable(&lpEnvCurrent, &g_ustrCompatLayerVar, &ustrCompatLayer);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ShimCreateWowEnvironment_U] Failed to set compatlayer environment variable. Status = 0x%x",
|
||
|
Status);
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
} else if (WowEnvData.pszCompatLayerVal != NULL) {
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[ShimCreateWowEnvironment_U] Propagating CompatLayer from the parent WOW app \"%s\"",
|
||
|
WowEnvData.pszCompatLayer);
|
||
|
|
||
|
RtlInitString(&strCompatLayer, WowEnvData.pszCompatLayerVal);
|
||
|
|
||
|
Status = RtlAnsiStringToUnicodeString(&ustrCompatLayer, &strCompatLayer, TRUE);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ShimCreateWowEnvironment_U] Failed to convert compatlayer to UNICODE. Status = 0x%x",
|
||
|
Status);
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
Status = RtlSetEnvironmentVariable(&lpEnvCurrent, &g_ustrCompatLayerVar, &ustrCompatLayer);
|
||
|
|
||
|
RtlFreeUnicodeString(&ustrCompatLayer);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ShimCreateWowEnvironment_U] Failed to set compatlayer environment variable. Status = 0x%x",
|
||
|
Status);
|
||
|
goto Fail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We have been successful. The return environment is UNICODE now.
|
||
|
//
|
||
|
lpEnvRet = (LPVOID)lpEnvCurrent;
|
||
|
*lpdwFlags = dwFlags | CREATE_UNICODE_ENVIRONMENT;
|
||
|
Status = STATUS_SUCCESS;
|
||
|
|
||
|
Fail:
|
||
|
|
||
|
if (!NT_SUCCESS(Status) && lpEnvCurrent != NULL && bNewEnvironment) {
|
||
|
//
|
||
|
// This points to the cloned environment ALWAYS.
|
||
|
//
|
||
|
RtlDestroyEnvironment(lpEnvCurrent);
|
||
|
}
|
||
|
|
||
|
RtlFreeUnicodeString(&ustrProcessHistory);
|
||
|
|
||
|
if (strProcessHistory.Buffer != NULL) {
|
||
|
ShimFree(strProcessHistory.Buffer);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This call is only necessary when using ShimRetrieveVariables.
|
||
|
// It is not needed when using ShimRetrieveVariablesEx.
|
||
|
//
|
||
|
// ShimFreeWOWEnvData(&WowEnvData);
|
||
|
//
|
||
|
|
||
|
return lpEnvRet;
|
||
|
}
|
||
|
|
||
|
ULONG
|
||
|
Win2kPropagateLayerExceptionHandler(
|
||
|
PEXCEPTION_POINTERS pexi,
|
||
|
char* szFile,
|
||
|
DWORD dwLine
|
||
|
)
|
||
|
{
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[Win2kPropagateLayerExceptionHandler] %#x in module \"%s\", line %d,"
|
||
|
" at address %#p. flags:%#x. !exr %#p !cxr %#p",
|
||
|
pexi->ExceptionRecord->ExceptionCode,
|
||
|
szFile,
|
||
|
dwLine,
|
||
|
CONTEXT_TO_PROGRAM_COUNTER(pexi->ContextRecord),
|
||
|
pexi->ExceptionRecord->ExceptionFlags,
|
||
|
pexi->ExceptionRecord,
|
||
|
pexi->ContextRecord);
|
||
|
|
||
|
#if DBG
|
||
|
DbgBreakPoint();
|
||
|
#endif // DBG
|
||
|
|
||
|
return EXCEPTION_EXECUTE_HANDLER;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Stub functions that are intercepted from WOW initialization code
|
||
|
(through APIHook_UserRegisterWowHandlers)
|
||
|
|
||
|
--*/
|
||
|
|
||
|
|
||
|
|
||
|
NSWOWUSERP::PFNINITTASK g_pfnInitTask;
|
||
|
NSWOWUSERP::PFNWOWCLEANUP g_pfnWowCleanup;
|
||
|
|
||
|
BOOL WINAPI
|
||
|
StubInitTask(
|
||
|
UINT dwExpWinVer,
|
||
|
DWORD dwAppCompatFlags,
|
||
|
LPCSTR lpszModName,
|
||
|
LPCSTR lpszBaseFileName,
|
||
|
DWORD hTaskWow,
|
||
|
DWORD dwHotkey,
|
||
|
DWORD idTask,
|
||
|
DWORD dwX,
|
||
|
DWORD dwY,
|
||
|
DWORD dwXSize,
|
||
|
DWORD dwYSize
|
||
|
)
|
||
|
{
|
||
|
BOOL bReturn;
|
||
|
|
||
|
bReturn = g_pfnInitTask(dwExpWinVer,
|
||
|
dwAppCompatFlags,
|
||
|
lpszModName,
|
||
|
lpszBaseFileName,
|
||
|
hTaskWow,
|
||
|
dwHotkey,
|
||
|
idTask,
|
||
|
dwX,
|
||
|
dwY,
|
||
|
dwXSize,
|
||
|
dwYSize);
|
||
|
if (bReturn) {
|
||
|
CheckAndShimNTVDM((WORD)hTaskWow);
|
||
|
UpdateWowTaskList((WORD)hTaskWow);
|
||
|
}
|
||
|
|
||
|
|
||
|
return bReturn;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL WINAPI
|
||
|
StubWowCleanup(
|
||
|
HANDLE hInstance,
|
||
|
DWORD hTaskWow
|
||
|
)
|
||
|
{
|
||
|
BOOL bReturn;
|
||
|
|
||
|
bReturn = g_pfnWowCleanup(hInstance, hTaskWow);
|
||
|
|
||
|
if (bReturn) {
|
||
|
CleanupWowTaskList((WORD)hTaskWow);
|
||
|
}
|
||
|
|
||
|
return bReturn;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*++
|
||
|
APIHook_UserRegisterWowHandlers
|
||
|
|
||
|
Trap InitTask and WowCleanup functions and
|
||
|
replace them with stubs
|
||
|
|
||
|
--*/
|
||
|
|
||
|
ULONG_PTR
|
||
|
APIHOOK(UserRegisterWowHandlers)(
|
||
|
NSWOWUSERP::APFNWOWHANDLERSIN apfnWowIn,
|
||
|
NSWOWUSERP::APFNWOWHANDLERSOUT apfnWowOut
|
||
|
)
|
||
|
{
|
||
|
ULONG_PTR ulRet;
|
||
|
|
||
|
ulRet = ORIGINAL_API(UserRegisterWowHandlers)(apfnWowIn, apfnWowOut);
|
||
|
|
||
|
g_pfnInitTask = apfnWowOut->pfnInitTask;
|
||
|
apfnWowOut->pfnInitTask = StubInitTask;
|
||
|
|
||
|
g_pfnWowCleanup = apfnWowOut->pfnWOWCleanup;
|
||
|
apfnWowOut->pfnWOWCleanup = StubWowCleanup;
|
||
|
|
||
|
return ulRet;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
CheckWOWExe(
|
||
|
LPCWSTR lpApplicationName,
|
||
|
LPVOID lpEnvironment,
|
||
|
LPDWORD lpdwCreationFlags
|
||
|
)
|
||
|
{
|
||
|
BOOL bSuccess;
|
||
|
BOOL bReturn = FALSE;
|
||
|
NTSTATUS Status;
|
||
|
LPVOID pEnvironmentNew = lpEnvironment;
|
||
|
SDBQUERYRESULT QueryResult;
|
||
|
DWORD dwBinaryType = 0;
|
||
|
HSDB hSDB = NULL;
|
||
|
DWORD dwExes;
|
||
|
WCHAR wszAppName[MAX_PATH];
|
||
|
|
||
|
bSuccess = GetFileNameFromCmdLine(wszAppName, CHARCOUNT(wszAppName), lpApplicationName);
|
||
|
if (!bSuccess) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
bSuccess = GetBinaryTypeW(wszAppName, &dwBinaryType);
|
||
|
if (!bSuccess || dwBinaryType != SCS_WOW_BINARY) {
|
||
|
LOGN( eDbgLevelInfo, "[CheckWowExe] can't get binary type\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// for these binaries we shall perform the good deed of running the detection
|
||
|
//
|
||
|
hSDB = SdbInitDatabase(0, NULL);
|
||
|
if (hSDB == NULL) {
|
||
|
LOGN( eDbgLevelError, "[CheckWowExe] Failed to init the database.");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (lpEnvironment != NULL && !(*lpdwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) { // non-null unicode env?
|
||
|
Status = ShimCloneEnvironment(&pEnvironmentNew,
|
||
|
lpEnvironment,
|
||
|
FALSE);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
LOGN( eDbgLevelError, "[ShimCloneEnvironment] failed with status 0x%lx\n", Status);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// all parameters below have to be unicode
|
||
|
//
|
||
|
|
||
|
dwExes = SdbGetMatchingExe(hSDB,
|
||
|
wszAppName,
|
||
|
NULL,
|
||
|
(LPCWSTR)pEnvironmentNew,
|
||
|
0,
|
||
|
&QueryResult);
|
||
|
bSuccess = (QueryResult.atrExes [0] != TAGREF_NULL ||
|
||
|
QueryResult.atrLayers[0] != TAGREF_NULL);
|
||
|
|
||
|
//
|
||
|
// if we have been successful -- layers apply to this thing
|
||
|
//
|
||
|
|
||
|
if (!bSuccess) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// set the separate ntvdm flag and be on our way out
|
||
|
//
|
||
|
*lpdwCreationFlags &= ~CREATE_SHARED_WOW_VDM;
|
||
|
*lpdwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
|
||
|
|
||
|
bReturn = TRUE;
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
if (pEnvironmentNew != lpEnvironment) {
|
||
|
ShimFreeEnvironment(pEnvironmentNew);
|
||
|
}
|
||
|
|
||
|
if (hSDB) {
|
||
|
SdbReleaseDatabase(hSDB);
|
||
|
}
|
||
|
|
||
|
return bReturn;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
APIHOOK(CreateProcessA)(
|
||
|
LPCSTR lpApplicationName,
|
||
|
LPSTR lpCommandLine,
|
||
|
LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||
|
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
||
|
BOOL bInheritHandles,
|
||
|
DWORD dwCreationFlags,
|
||
|
LPVOID lpEnvironment,
|
||
|
LPCSTR lpCurrentDirectory,
|
||
|
LPSTARTUPINFOA lpStartupInfo,
|
||
|
LPPROCESS_INFORMATION lpProcessInformation
|
||
|
)
|
||
|
{
|
||
|
BOOL bRet;
|
||
|
LPVOID lpEnvironmentNew = lpEnvironment;
|
||
|
LPSTR pszApp = NULL;
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[CreateProcessA] called for:");
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[CreateProcessA] lpApplicationName : \"%s\"",
|
||
|
(lpApplicationName == NULL ? "null": lpApplicationName));
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[CreateProcessA] lpCommandLine : \"%s\"",
|
||
|
(lpCommandLine == NULL ? "null": lpCommandLine));
|
||
|
|
||
|
if (lpApplicationName != NULL) {
|
||
|
pszApp = (LPSTR)lpApplicationName;
|
||
|
} else if (lpCommandLine != NULL) {
|
||
|
pszApp = lpCommandLine;
|
||
|
} else {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[CreateProcessA] called with NULL params.");
|
||
|
}
|
||
|
|
||
|
__try {
|
||
|
|
||
|
WCHAR wszApp[MAX_PATH];
|
||
|
|
||
|
if (pszApp != NULL) {
|
||
|
|
||
|
if( MultiByteToWideChar(CP_ACP,
|
||
|
0,
|
||
|
pszApp,
|
||
|
-1,
|
||
|
wszApp,
|
||
|
MAX_PATH) )
|
||
|
{
|
||
|
AddSupport(wszApp, &lpEnvironmentNew, &dwCreationFlags);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (g_bIsNTVDM) {
|
||
|
|
||
|
//
|
||
|
// if the environment stayed the same as it was passed in -- clone it to propagate process history
|
||
|
// if it was modified in AddSupport -- use it
|
||
|
//
|
||
|
|
||
|
lpEnvironmentNew = ShimCreateWowEnvironment_U(lpEnvironmentNew,
|
||
|
&dwCreationFlags,
|
||
|
lpEnvironmentNew == lpEnvironment);
|
||
|
}
|
||
|
|
||
|
|
||
|
if (pszApp != NULL && !(dwCreationFlags & CREATE_SEPARATE_WOW_VDM)) {
|
||
|
// since the separate vdm flag is not set -- we need to determine whether we have
|
||
|
// any kind of fixes to care about.
|
||
|
CheckWOWExe(wszApp, lpEnvironmentNew, &dwCreationFlags);
|
||
|
}
|
||
|
|
||
|
|
||
|
} __except(WOWPROCESSHISTORYEXCEPTIONFILTER) {
|
||
|
|
||
|
//
|
||
|
// cleanup the mess, if we have allocated the environment, free it now
|
||
|
//
|
||
|
if (lpEnvironmentNew != lpEnvironment) {
|
||
|
|
||
|
ShimFreeEnvironment(lpEnvironmentNew);
|
||
|
|
||
|
lpEnvironmentNew = lpEnvironment;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
bRet = ORIGINAL_API(CreateProcessA)(lpApplicationName,
|
||
|
lpCommandLine,
|
||
|
lpProcessAttributes,
|
||
|
lpThreadAttributes,
|
||
|
bInheritHandles,
|
||
|
dwCreationFlags,
|
||
|
lpEnvironmentNew,
|
||
|
lpCurrentDirectory,
|
||
|
lpStartupInfo,
|
||
|
lpProcessInformation);
|
||
|
|
||
|
if (lpEnvironmentNew != lpEnvironment) {
|
||
|
//
|
||
|
// The function below does not need a __try/__except wrapper, it has it internally
|
||
|
//
|
||
|
ShimFreeEnvironment(lpEnvironmentNew);
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
APIHOOK(CreateProcessW)(
|
||
|
LPCWSTR lpApplicationName,
|
||
|
LPWSTR lpCommandLine,
|
||
|
LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||
|
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
||
|
BOOL bInheritHandles,
|
||
|
DWORD dwCreationFlags,
|
||
|
LPVOID lpEnvironment,
|
||
|
LPCWSTR lpCurrentDirectory,
|
||
|
LPSTARTUPINFOW lpStartupInfo,
|
||
|
LPPROCESS_INFORMATION lpProcessInformation
|
||
|
)
|
||
|
{
|
||
|
LPWSTR pszApp = NULL;
|
||
|
BOOL bRet;
|
||
|
LPVOID lpEnvironmentNew = lpEnvironment;
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[CreateProcessW] called for:");
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[CreateProcessW] lpApplicationName : \"%S\"",
|
||
|
(lpApplicationName == NULL ? L"null": lpApplicationName));
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[CreateProcessW] lpCommandLine : \"%S\"",
|
||
|
(lpCommandLine == NULL ? L"null": lpCommandLine));
|
||
|
|
||
|
if (lpApplicationName != NULL) {
|
||
|
pszApp = (LPWSTR)lpApplicationName;
|
||
|
} else if (lpCommandLine != NULL) {
|
||
|
pszApp = lpCommandLine;
|
||
|
} else {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[CreateProcessW] called with NULL params.");
|
||
|
}
|
||
|
|
||
|
__try {
|
||
|
|
||
|
if (pszApp != NULL) {
|
||
|
|
||
|
AddSupport(pszApp, &lpEnvironmentNew, &dwCreationFlags);
|
||
|
}
|
||
|
|
||
|
if (g_bIsNTVDM) {
|
||
|
|
||
|
lpEnvironmentNew = ShimCreateWowEnvironment_U(lpEnvironmentNew,
|
||
|
&dwCreationFlags,
|
||
|
lpEnvironment == lpEnvironmentNew);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// typically we need to find out whether the current app is ntvdm
|
||
|
//
|
||
|
|
||
|
if (!(dwCreationFlags & CREATE_SEPARATE_WOW_VDM)) {
|
||
|
// since the separate vdm flag is not set -- we need to determine whether we have
|
||
|
// any kind of fixes to care about.
|
||
|
CheckWOWExe(pszApp, lpEnvironmentNew, &dwCreationFlags);
|
||
|
}
|
||
|
|
||
|
} __except(WOWPROCESSHISTORYEXCEPTIONFILTER) {
|
||
|
|
||
|
if (lpEnvironmentNew != lpEnvironment) {
|
||
|
|
||
|
ShimFreeEnvironment(lpEnvironmentNew);
|
||
|
|
||
|
lpEnvironmentNew = lpEnvironment; // reset the pointer
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bRet = ORIGINAL_API(CreateProcessW)(lpApplicationName,
|
||
|
lpCommandLine,
|
||
|
lpProcessAttributes,
|
||
|
lpThreadAttributes,
|
||
|
bInheritHandles,
|
||
|
dwCreationFlags,
|
||
|
lpEnvironmentNew,
|
||
|
lpCurrentDirectory,
|
||
|
lpStartupInfo,
|
||
|
lpProcessInformation);
|
||
|
|
||
|
if (lpEnvironmentNew != lpEnvironment) {
|
||
|
|
||
|
ShimFreeEnvironment(lpEnvironmentNew);
|
||
|
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
GetVariableFromEnvironment(
|
||
|
LPCWSTR pwszVariableName,
|
||
|
LPWSTR* ppwszVariableValue
|
||
|
)
|
||
|
{
|
||
|
DWORD dwLength;
|
||
|
DWORD dwLen;
|
||
|
BOOL bSuccess = FALSE;
|
||
|
LPWSTR pwszVariableValue = *ppwszVariableValue;
|
||
|
|
||
|
dwLength = GetEnvironmentVariableW(pwszVariableName, NULL, 0);
|
||
|
|
||
|
if (dwLength == 0) {
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[GetCompatLayerFromEnvironment] Not under the compatibility layer.");
|
||
|
*ppwszVariableValue = NULL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (pwszVariableValue != NULL) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[GetCompatLayerFromEnvironment] called twice!");
|
||
|
ShimFree(pwszVariableValue);
|
||
|
pwszVariableValue = NULL;
|
||
|
}
|
||
|
|
||
|
pwszVariableValue = (WCHAR*)ShimMalloc(dwLength * sizeof(WCHAR));
|
||
|
|
||
|
if (pwszVariableValue == NULL) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[GetCompatLayerFromEnvironment] Failed to allocate %d bytes for Compat Layer.",
|
||
|
dwLength * sizeof(WCHAR));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
*pwszVariableValue = L'\0';
|
||
|
|
||
|
dwLen = GetEnvironmentVariableW(pwszVariableName,
|
||
|
pwszVariableValue,
|
||
|
dwLength);
|
||
|
|
||
|
bSuccess = (dwLen != 0 && dwLen < dwLength);
|
||
|
|
||
|
if (!bSuccess) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[GetCompatLayerFromEnvironment] Failed to get compat layer variable.");
|
||
|
ShimFree(pwszVariableValue);
|
||
|
pwszVariableValue = NULL;
|
||
|
}
|
||
|
|
||
|
out:
|
||
|
|
||
|
*ppwszVariableValue = pwszVariableValue;
|
||
|
|
||
|
return bSuccess;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
GetCompatLayerFromEnvironment(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
return GetVariableFromEnvironment(g_wszCompatLayerVar, &g_pwszCompatLayer);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
GetSeparateWowPtr(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
|
||
|
HMODULE hMod = GetModuleHandle(NULL);
|
||
|
|
||
|
g_pSeparateWow = (BOOL*)GetProcAddress(hMod, "fSeparateWow");
|
||
|
if (g_pSeparateWow == NULL) {
|
||
|
LOGN( eDbgLevelError, "[GetSeparateWowPtr] Failed 0x%lx\n", GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
ParseCommandLine(
|
||
|
LPCSTR commandLine
|
||
|
)
|
||
|
{
|
||
|
int i;
|
||
|
char* pArg;
|
||
|
|
||
|
g_argc = 0;
|
||
|
g_argv = NULL;
|
||
|
|
||
|
g_bIsNTVDM = FALSE;
|
||
|
g_bIsExplorer = FALSE;
|
||
|
|
||
|
g_argv = _CommandLineToArgvA(commandLine, &g_argc);
|
||
|
|
||
|
if (0 == g_argc || NULL == g_argv) {
|
||
|
return; // nothing to do
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < g_argc; ++i) {
|
||
|
pArg = g_argv[i];
|
||
|
|
||
|
if (!_strcmpi(pArg, "ntvdm")) {
|
||
|
LOGN( eDbgLevelInfo, "[ParseCommandLine] Running NTVDM.");
|
||
|
g_bIsNTVDM = TRUE;
|
||
|
|
||
|
} else if (!_strcmpi(pArg, "explorer")) {
|
||
|
LOGN( eDbgLevelInfo, "[ParseCommandLine] Running Explorer.");
|
||
|
g_bIsExplorer = TRUE;
|
||
|
|
||
|
} else {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ParseCommandLine] Unrecognized argument: \"%s\"",
|
||
|
pArg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (g_bIsNTVDM && g_bIsExplorer) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ParseCommandLine] Conflicting arguments! Neither will be applied.");
|
||
|
g_bIsNTVDM = FALSE;
|
||
|
g_bIsExplorer = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
NOTIFY_FUNCTION(
|
||
|
DWORD fdwReason
|
||
|
)
|
||
|
{
|
||
|
OSVERSIONINFO osvi;
|
||
|
BOOL bHook = FALSE;
|
||
|
|
||
|
if (fdwReason != DLL_PROCESS_ATTACH) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
||
|
|
||
|
GetVersionEx(&osvi);
|
||
|
|
||
|
if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
|
||
|
|
||
|
ParseCommandLine(COMMAND_LINE);
|
||
|
|
||
|
if( InitLayerStorage(FALSE) ) {
|
||
|
CleanupRegistryForCurrentExe();
|
||
|
|
||
|
if (g_bIsNTVDM) {
|
||
|
|
||
|
bHook = TRUE;
|
||
|
|
||
|
//
|
||
|
// Retrieve the compat layer variable that we have been started with (just in case)
|
||
|
//
|
||
|
GetCompatLayerFromEnvironment();
|
||
|
|
||
|
GetSeparateWowPtr(); // retrieve ptr to a sep flag
|
||
|
|
||
|
} else if (g_bIsExplorer) {
|
||
|
|
||
|
//
|
||
|
// Cleanup compat layer variable
|
||
|
//
|
||
|
SetEnvironmentVariableW(g_wszCompatLayerVar, NULL);
|
||
|
bHook = TRUE;
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// Neither explorer nor ntvdm. Get the compat layer.
|
||
|
//
|
||
|
bHook = GetCompatLayerFromEnvironment();
|
||
|
if (!bHook) {
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[NOTIFY_FUNCTION] Not under the compatibility layer.");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bHook) {
|
||
|
APIHOOK_ENTRY(KERNEL32.DLL, CreateProcessA)
|
||
|
APIHOOK_ENTRY(KERNEL32.DLL, CreateProcessW)
|
||
|
APIHOOK_ENTRY(USER32.DLL, UserRegisterWowHandlers)
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
HOOK_BEGIN
|
||
|
|
||
|
CALL_NOTIFY_FUNCTION
|
||
|
|
||
|
HOOK_END
|
||
|
|
||
|
|
||
|
IMPLEMENT_SHIM_END
|
||
|
|