Windows2003-3790/windows/appcompat/shims/layer/win2kpropagatelayer.cpp
2020-09-30 16:53:55 +02:00

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