|
|
/*++
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
|