You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1087 lines
31 KiB
1087 lines
31 KiB
/*++
|
|
*
|
|
* WOW v1.0
|
|
*
|
|
* Copyright (c) 1991, Microsoft Corporation
|
|
*
|
|
* WKMAN.C
|
|
* WOW32 16-bit Kernel API support (manually-coded thunks)
|
|
*
|
|
* History:
|
|
* Created 16-Apr-2001 jarbats
|
|
*
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
/*
|
|
* shimdb has ..,.. which conflicts with the typedef TAG used in winuserp.h
|
|
* so we redfine it here and put all the code which uses shimdb interfaces
|
|
* in this file.
|
|
*
|
|
*/
|
|
|
|
#ifdef TAG
|
|
#undef TAG
|
|
#endif
|
|
#define TAG _SHIMDB_WORDTAG
|
|
|
|
#include "shimdb.h"
|
|
|
|
#include "nt.h"
|
|
#include "ntrtl.h"
|
|
#include "nturtl.h"
|
|
#include "zwapi.h"
|
|
|
|
#define _wshimdb
|
|
|
|
|
|
|
|
MODNAME(wshimdb.c);
|
|
|
|
|
|
|
|
|
|
CHAR g_szCompatLayerVar[] = "__COMPAT_LAYER";
|
|
CHAR g_szProcessHistoryVar[] = "__PROCESS_HISTORY";
|
|
CHAR g_szShimFileLogVar[] = "SHIM_FILE_LOG";
|
|
|
|
extern PFAMILY_TABLE *pgDpmWowFamTbls;
|
|
|
|
UNICODE_STRING g_ustrProcessHistoryVar = RTL_CONSTANT_STRING(L"__PROCESS_HISTORY");
|
|
UNICODE_STRING g_ustrCompatLayerVar = RTL_CONSTANT_STRING(L"__COMPAT_LAYER" );
|
|
UNICODE_STRING g_ustrShimFileLogVar = RTL_CONSTANT_STRING(L"SHIM_FILE_LOG" );
|
|
|
|
BOOL CheckAppHelpInfo(PTD pTD,PSZ szFileName,PSZ szModName) {
|
|
|
|
BOOL fReturn = TRUE;
|
|
NTVDM_FLAGS NtVdmFlags = { 0 };
|
|
|
|
WCHAR wszFileName[256];
|
|
WCHAR wszModName[16];
|
|
|
|
WCHAR *pwszTempEnv = NULL;
|
|
PSZ pszEnvTemplate = NULL;
|
|
PWOWENVDATA pWowEnvData = NULL;
|
|
PTD pTDParent = NULL;
|
|
|
|
WCHAR wszCompatLayer[COMPATLAYERMAXLEN];
|
|
|
|
APPHELP_INFO AHInfo;
|
|
|
|
HSDB hSdb = NULL;
|
|
SDBQUERYRESULT SdbQuery;
|
|
|
|
HANDLE hProcess;
|
|
|
|
ghTaskAppHelp = NULL;
|
|
|
|
RtlOemToUnicodeN(
|
|
wszFileName,
|
|
sizeof(wszFileName),
|
|
NULL,
|
|
szFileName,
|
|
strlen(szFileName) + 1
|
|
);
|
|
|
|
RtlOemToUnicodeN(
|
|
wszModName,
|
|
sizeof(wszModName),
|
|
NULL,
|
|
szModName,
|
|
strlen(szModName) + 1
|
|
);
|
|
|
|
//
|
|
// find parent TD -- it contains the environment we need to
|
|
// pass into the detection routine plus wowdata
|
|
//
|
|
|
|
pTDParent = GetParentTD(pTD->htask16);
|
|
|
|
if (NULL != pTDParent) {
|
|
pWowEnvData = pTDParent->pWowEnvDataChild;
|
|
}
|
|
|
|
pszEnvTemplate = GetTaskEnvptr(pTD->htask16);
|
|
pwszTempEnv = WOWForgeUnicodeEnvironment(pszEnvTemplate, pWowEnvData) ;
|
|
|
|
|
|
wszCompatLayer[0] = UNICODE_NULL;
|
|
AHInfo.tiExe = 0;
|
|
|
|
fReturn = ApphelpGetNTVDMInfo(wszFileName,
|
|
wszModName,
|
|
pwszTempEnv,
|
|
wszCompatLayer,
|
|
&NtVdmFlags,
|
|
&AHInfo,
|
|
&hSdb,
|
|
&SdbQuery);
|
|
|
|
if(fReturn) {
|
|
|
|
if (AHInfo.tiExe &&
|
|
ApphelpShowDialog(&AHInfo,&hProcess) &&
|
|
hProcess) {
|
|
ghTaskAppHelp = hProcess;
|
|
}
|
|
|
|
if(AHInfo.dwSeverity == APPHELP_HARDBLOCK) {
|
|
fReturn = FALSE;
|
|
goto ExitCAHI;
|
|
}
|
|
}
|
|
|
|
WOWInheritEnvironment(pTD, pTDParent, wszCompatLayer, szFileName);
|
|
|
|
pTD->dwWOWCompatFlags = NtVdmFlags.dwWOWCompatFlags;
|
|
pTD->dwWOWCompatFlagsEx = NtVdmFlags.dwWOWCompatFlagsEx;
|
|
pTD->dwUserWOWCompatFlags = NtVdmFlags.dwUserWOWCompatFlags;
|
|
pTD->dwWOWCompatFlags2 = NtVdmFlags.dwWOWCompatFlags2;
|
|
#ifdef FE_SB
|
|
pTD->dwWOWCompatFlagsFE = NtVdmFlags.dwWOWCompatFlagsFE;
|
|
#endif // FE_SB
|
|
|
|
pTD->pWOWCompatFlagsEx_Info = InitFlagInfo(NtVdmFlags.pFlagsInfo, WOWCOMPATFLAGSEX,NtVdmFlags.dwWOWCompatFlagsEx);
|
|
pTD->pWOWCompatFlags2_Info = InitFlagInfo(NtVdmFlags.pFlagsInfo, WOWCOMPATFLAGS2,NtVdmFlags.dwWOWCompatFlags2);
|
|
|
|
// if the app requires Dynamic Patch Module(s) and/or shims to be linked
|
|
if(NtVdmFlags.dwWOWCompatFlags2 & WOWCF2_DPM_PATCHES) {
|
|
CMDLNPARMS CmdLnParms;
|
|
PFLAGINFOBITS pFlagInfoBits;
|
|
|
|
pFlagInfoBits = CheckFlagInfo(WOWCOMPATFLAGS2, WOWCF2_DPM_PATCHES);
|
|
|
|
if(pFlagInfoBits) {
|
|
CmdLnParms.argc = (int)pFlagInfoBits->dwFlagArgc;
|
|
CmdLnParms.argv = (char **)pFlagInfoBits->pFlagArgv;
|
|
CmdLnParms.dwFlag = WOWCF2_DPM_PATCHES;
|
|
|
|
InitTaskDpmSupport(NUM_WOW_FAMILIES_HOOKED,
|
|
pgDpmWowFamTbls,
|
|
&CmdLnParms,
|
|
(PVOID)hSdb,
|
|
(PVOID)&SdbQuery,
|
|
wszFileName,
|
|
wszModName,
|
|
pwszTempEnv);
|
|
}
|
|
}
|
|
|
|
ExitCAHI:
|
|
|
|
if (pwszTempEnv != NULL) {
|
|
WOWFreeUnicodeEnvironment(pwszTempEnv);
|
|
}
|
|
|
|
if (hSdb != NULL) {
|
|
SdbReleaseDatabase(hSdb);
|
|
}
|
|
|
|
SdbFreeFlagInfo(NtVdmFlags.pFlagsInfo);
|
|
|
|
// Clean up starts here
|
|
return fReturn;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
// Welcome to Win2kPropagateLayer which manages everything in the environment
|
|
// related to shims/detection
|
|
//
|
|
|
|
//
|
|
// this function is called during the parent tasks pass_environment call
|
|
// we are still in the context of the parent, which means that:
|
|
// CURRENTPTD() gives us parents' TD
|
|
// *pCurTDB gives us parents' TDB
|
|
//
|
|
|
|
//
|
|
// get the pointer to task database block from hTask
|
|
//
|
|
|
|
|
|
PTDB
|
|
GetTDB(
|
|
HAND16 hTask
|
|
)
|
|
{
|
|
PTDB pTDB;
|
|
|
|
pTDB = (PTDB)SEGPTR(hTask, 0);
|
|
if (NULL == pTDB || TDB_SIGNATURE != pTDB->TDB_sig) {
|
|
return NULL;
|
|
}
|
|
|
|
return pTDB;
|
|
}
|
|
|
|
PSZ
|
|
GetTaskEnvptr(
|
|
HAND16 hTask
|
|
)
|
|
{
|
|
PTDB pTDB = GetTDB(hTask);
|
|
PSZ pszEnv = NULL;
|
|
PDOSPDB pPSP;
|
|
|
|
if (NULL == pTDB) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Prepare environment data - this buffer is used when we're starting a new task from the
|
|
// root of the chain (as opposed to spawning from an existing 16-bit task)
|
|
//
|
|
|
|
pPSP = (PDOSPDB)SEGPTR(pTDB->TDB_PDB, 0); // psp
|
|
|
|
if (pPSP != NULL) {
|
|
pszEnv = (PCH)SEGPTR(pPSP->PDB_environ, 0);
|
|
}
|
|
|
|
return pszEnv;
|
|
}
|
|
|
|
|
|
PTD
|
|
GetParentTD(
|
|
HAND16 hTask
|
|
)
|
|
{
|
|
PTDB pTDB = GetTDB(hTask);
|
|
PTDB pTDBParent;
|
|
PTD ptdCur = NULL;
|
|
HAND16 hTaskParent;
|
|
|
|
if (NULL == pTDB) {
|
|
return NULL; // cannot get that task
|
|
}
|
|
|
|
//
|
|
// now, retrieve the TD for the parent.
|
|
//
|
|
|
|
hTaskParent = pTDB->TDB_Parent;
|
|
|
|
pTDBParent = GetTDB(hTaskParent);
|
|
|
|
if (NULL == pTDBParent) {
|
|
// can's see the parent
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// so we can see what the parent is up to
|
|
//
|
|
// pTDBParent->TDB_ThreadID and
|
|
// hTaskParent are the dead giveaway
|
|
//
|
|
|
|
ptdCur = gptdTaskHead;
|
|
while (NULL != ptdCur) {
|
|
if (ptdCur->dwThreadID == pTDBParent->TDB_ThreadID &&
|
|
ptdCur->htask16 == hTaskParent) {
|
|
|
|
break;
|
|
}
|
|
ptdCur = ptdCur->ptdNext;
|
|
}
|
|
|
|
//
|
|
// if ptdCur == NULL -- we have not been able to locate the parent tasks' ptd
|
|
//
|
|
|
|
return ptdCur;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
IsWowExec(
|
|
WORD wTask
|
|
)
|
|
{
|
|
return (ghShellTDB == wTask);
|
|
}
|
|
|
|
|
|
//
|
|
// This function is called in the context of pass_environment
|
|
//
|
|
//
|
|
//
|
|
|
|
|
|
BOOL
|
|
CreateWowChildEnvInformation(
|
|
PSZ pszEnvParent
|
|
)
|
|
{
|
|
PTD pTD; // parent TD
|
|
|
|
WOWENVDATA EnvData;
|
|
PWOWENVDATA pData = NULL;
|
|
PWOWENVDATA pEnvChildData = NULL;
|
|
DWORD dwLength;
|
|
PCH pBuffer;
|
|
|
|
|
|
RtlZeroMemory(&EnvData, sizeof(EnvData));
|
|
|
|
//
|
|
// check where we should inherit process history and layers from
|
|
//
|
|
pTD = CURRENTPTD();
|
|
|
|
if (pTD->pWowEnvDataChild) {
|
|
free_w(pTD->pWowEnvDataChild);
|
|
pTD->pWowEnvDataChild = NULL;
|
|
}
|
|
|
|
//
|
|
// check whether we are starting the root task (meaning that this task IS wowexec)
|
|
// if so, we shall inherit things from pParamBlk->envseg
|
|
// else we use TD of the parent task (this TD that is) to
|
|
// inherit things
|
|
// How to detect that this is wowexec:
|
|
// ghShellTDB could we compared to *pCurTDB
|
|
// gptdShell->hTask16 could be compared to *pCurTDB
|
|
// we check for not having wowexec -- if gptdShell == NULL then we're doing boot
|
|
//
|
|
|
|
if (pCurTDB == NULL || IsWowExec(*pCurTDB)) {
|
|
//
|
|
// presumably we are wowexec
|
|
// use current environment ptr to get stuff (like pParamBlk->envseg)
|
|
// or the original ntvdm environment
|
|
//
|
|
pData = &EnvData;
|
|
|
|
pData->pszProcessHistory = WOWFindEnvironmentVar(g_szProcessHistoryVar,
|
|
pszEnvParent,
|
|
&pData->pszProcessHistoryVal);
|
|
pData->pszCompatLayer = WOWFindEnvironmentVar(g_szCompatLayerVar,
|
|
pszEnvParent,
|
|
&pData->pszCompatLayerVal);
|
|
pData->pszShimFileLog = WOWFindEnvironmentVar(g_szShimFileLogVar,
|
|
pszEnvParent,
|
|
&pData->pszShimFileLogVal);
|
|
|
|
} else {
|
|
|
|
//
|
|
// this current task is not a dastardly wowexec
|
|
// clone current + enhance process history
|
|
//
|
|
|
|
pData = pTD->pWowEnvData; // if this is NULL
|
|
if (pData == NULL) {
|
|
pData = &EnvData; // all the vars are empty
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
//
|
|
|
|
dwLength = sizeof(WOWENVDATA) +
|
|
(NULL == pData->pszProcessHistory ? 0 : (strlen(pData->pszProcessHistory) + 1) * sizeof(CHAR)) +
|
|
(NULL == pData->pszCompatLayer ? 0 : (strlen(pData->pszCompatLayer) + 1) * sizeof(CHAR)) +
|
|
(NULL == pData->pszShimFileLog ? 0 : (strlen(pData->pszShimFileLog) + 1) * sizeof(CHAR)) +
|
|
(NULL == pData->pszCurrentProcessHistory ? 0 : (strlen(pData->pszCurrentProcessHistory) + 1) * sizeof(CHAR));
|
|
|
|
|
|
pEnvChildData = (PWOWENVDATA)malloc_w(dwLength);
|
|
|
|
if (pEnvChildData == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(pEnvChildData, dwLength);
|
|
|
|
//
|
|
// now this entry has to be setup
|
|
// process history is first
|
|
//
|
|
|
|
pBuffer = (PCH)(pEnvChildData + 1);
|
|
|
|
if (pData->pszProcessHistory != NULL) {
|
|
|
|
//
|
|
// Copy process history. The processHistoryVal is a pointer into the buffer
|
|
// pointed to by pszProcessHistory: __PROCESS_HISTORY=c:\foo;c:\docs~1\install
|
|
// then pszProcessHistoryVal will point here ---------^
|
|
//
|
|
// we are copying the data and moving the pointer using the calculated offset
|
|
|
|
pEnvChildData->pszProcessHistory = pBuffer;
|
|
strcpy(pEnvChildData->pszProcessHistory, pData->pszProcessHistory);
|
|
pEnvChildData->pszProcessHistoryVal = pEnvChildData->pszProcessHistory +
|
|
(INT)(pData->pszProcessHistoryVal - pData->pszProcessHistory);
|
|
//
|
|
// There is enough space in the buffer to accomodate all the strings, so
|
|
// move pointer past current string to point at the "empty" space
|
|
//
|
|
|
|
pBuffer += strlen(pData->pszProcessHistory) + 1;
|
|
}
|
|
|
|
if (pData->pszCompatLayer != NULL) {
|
|
pEnvChildData->pszCompatLayer = pBuffer;
|
|
strcpy(pEnvChildData->pszCompatLayer, pData->pszCompatLayer);
|
|
pEnvChildData->pszCompatLayerVal = pEnvChildData->pszCompatLayer +
|
|
(INT)(pData->pszCompatLayerVal - pData->pszCompatLayer);
|
|
pBuffer += strlen(pData->pszCompatLayer) + 1;
|
|
}
|
|
|
|
if (pData->pszShimFileLog != NULL) {
|
|
pEnvChildData->pszShimFileLog = pBuffer;
|
|
strcpy(pEnvChildData->pszShimFileLog, pData->pszShimFileLog);
|
|
pEnvChildData->pszShimFileLogVal = pEnvChildData->pszShimFileLog +
|
|
(INT)(pData->pszShimFileLogVal - pData->pszShimFileLog);
|
|
pBuffer += strlen(pData->pszShimFileLog) + 1;
|
|
}
|
|
|
|
if (pData->pszCurrentProcessHistory != NULL) {
|
|
//
|
|
// Now process history
|
|
//
|
|
pEnvChildData->pszCurrentProcessHistory = pBuffer;
|
|
|
|
if (pData->pszCurrentProcessHistory != NULL) {
|
|
strcpy(pEnvChildData->pszCurrentProcessHistory, pData->pszCurrentProcessHistory);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// we are done, environment cloned
|
|
//
|
|
|
|
pTD->pWowEnvDataChild = pEnvChildData;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// In : pointer to environment(oem)
|
|
// out: pointer to unicode environment
|
|
//
|
|
|
|
NTSTATUS
|
|
WOWCloneEnvironment(
|
|
LPVOID* ppEnvOut,
|
|
PSZ lpEnvironment
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
|
DWORD dwEnvSize = 0;
|
|
LPVOID lpEnvNew = NULL;
|
|
|
|
MEMORY_BASIC_INFORMATION MemoryInformation;
|
|
|
|
if (NULL == lpEnvironment) {
|
|
Status = RtlCreateEnvironment(TRUE, &lpEnvNew);
|
|
} else {
|
|
dwEnvSize = WOWGetEnvironmentSize(lpEnvironment, NULL);
|
|
|
|
MemoryInformation.RegionSize = (dwEnvSize + 2) * sizeof(UNICODE_NULL);
|
|
Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
|
|
&lpEnvNew,
|
|
0,
|
|
&MemoryInformation.RegionSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
}
|
|
|
|
if (NULL != lpEnvironment) {
|
|
|
|
UNICODE_STRING UnicodeBuffer;
|
|
OEM_STRING OemBuffer;
|
|
|
|
OemBuffer.Buffer = (CHAR*)lpEnvironment;
|
|
OemBuffer.Length = OemBuffer.MaximumLength = (USHORT)dwEnvSize; // size in bytes = size in chars, includes \0\0
|
|
|
|
UnicodeBuffer.Buffer = (WCHAR*)lpEnvNew;
|
|
UnicodeBuffer.Length = (USHORT)dwEnvSize * sizeof(UNICODE_NULL);
|
|
UnicodeBuffer.MaximumLength = (USHORT)(dwEnvSize + 2) * sizeof(UNICODE_NULL); // leave room for \0
|
|
|
|
Status = RtlOemStringToUnicodeString(&UnicodeBuffer, &OemBuffer, FALSE);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
*ppEnvOut = lpEnvNew;
|
|
} else {
|
|
if (NULL != lpEnvNew) {
|
|
RtlDestroyEnvironment(lpEnvNew);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
WOWFreeUnicodeEnvironment(
|
|
LPVOID lpEnvironment
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = RtlDestroyEnvironment(lpEnvironment);
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Set environment variable, possibly create or clone provided environment
|
|
//
|
|
|
|
NTSTATUS
|
|
WOWSetEnvironmentVar_U(
|
|
LPVOID* ppEnvironment,
|
|
WCHAR* pwszVarName,
|
|
WCHAR* pwszVarValue
|
|
)
|
|
{
|
|
UNICODE_STRING ustrVarName;
|
|
UNICODE_STRING ustrVarValue;
|
|
NTSTATUS Status;
|
|
|
|
RtlInitUnicodeString(&ustrVarName, pwszVarName);
|
|
|
|
if (NULL != pwszVarValue) {
|
|
RtlInitUnicodeString(&ustrVarValue, pwszVarValue);
|
|
}
|
|
|
|
Status = RtlSetEnvironmentVariable(ppEnvironment,
|
|
&ustrVarName,
|
|
(NULL == pwszVarValue) ? NULL : &ustrVarValue);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
WOWSetEnvironmentVar_Oem(
|
|
LPVOID* ppEnvironment,
|
|
PUNICODE_STRING pustrVarName, // pre-made (cheap)
|
|
PSZ pszVarValue
|
|
)
|
|
{
|
|
OEM_STRING OemString = { 0 };
|
|
UNICODE_STRING ustrVarValue = { 0 };
|
|
NTSTATUS Status;
|
|
|
|
if (pszVarValue != NULL) {
|
|
RtlInitString(&OemString, pszVarValue);
|
|
|
|
Status = RtlOemStringToUnicodeString(&ustrVarValue, &OemString, TRUE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
Status = RtlSetEnvironmentVariable(ppEnvironment,
|
|
pustrVarName,
|
|
(NULL == pszVarValue) ? NULL : &ustrVarValue);
|
|
|
|
if (NULL != pszVarValue) {
|
|
RtlFreeUnicodeString(&ustrVarValue);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Call this function to produce a "good" unicode environment
|
|
//
|
|
//
|
|
|
|
|
|
LPWSTR
|
|
WOWForgeUnicodeEnvironment(
|
|
PSZ pEnvironment, // this task's sanitized environment
|
|
PWOWENVDATA pEnvData // parent-made environment data
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
LPVOID lpEnvNew = NULL;
|
|
|
|
DWORD dwProcessHistoryLength = 0;
|
|
PSZ pszFullProcessHistory = NULL;
|
|
|
|
|
|
Status = WOWCloneEnvironment(&lpEnvNew, pEnvironment);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// we do have an env to work with
|
|
//
|
|
RtlSetEnvironmentVariable(&lpEnvNew, &g_ustrProcessHistoryVar, NULL);
|
|
RtlSetEnvironmentVariable(&lpEnvNew, &g_ustrCompatLayerVar, NULL);
|
|
RtlSetEnvironmentVariable(&lpEnvNew, &g_ustrShimFileLogVar, NULL);
|
|
|
|
//
|
|
// get stuff from envdata
|
|
//
|
|
|
|
if (pEnvData == NULL) {
|
|
goto Done;
|
|
}
|
|
|
|
if (pEnvData->pszProcessHistory != NULL || pEnvData->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 = ((pEnvData->pszProcessHistory == NULL) ? 0 : (strlen(pEnvData->pszProcessHistoryVal) + 1)) +
|
|
((pEnvData->pszCurrentProcessHistory == NULL) ? 0 : strlen(pEnvData->pszCurrentProcessHistory)) + 1;
|
|
|
|
//
|
|
// Allocate process history buffer and convert it, allocating resulting unicode string.
|
|
//
|
|
pszFullProcessHistory = (PCHAR)malloc_w(dwProcessHistoryLength);
|
|
|
|
if (NULL == pszFullProcessHistory) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Done;
|
|
}
|
|
|
|
*pszFullProcessHistory = '\0';
|
|
|
|
if (pEnvData->pszProcessHistory != NULL) {
|
|
strcpy(pszFullProcessHistory, pEnvData->pszProcessHistoryVal);
|
|
}
|
|
|
|
if (pEnvData->pszCurrentProcessHistory != NULL) {
|
|
|
|
//
|
|
// Append ';' if the string was not empty.
|
|
//
|
|
if (*pszFullProcessHistory) {
|
|
strcat(pszFullProcessHistory, ";");
|
|
}
|
|
|
|
strcat(pszFullProcessHistory, pEnvData->pszCurrentProcessHistory);
|
|
}
|
|
|
|
Status = WOWSetEnvironmentVar_Oem(&lpEnvNew,
|
|
&g_ustrProcessHistoryVar,
|
|
pszFullProcessHistory);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// deal with compatLayer
|
|
//
|
|
if (pEnvData->pszCompatLayerVal != NULL) {
|
|
|
|
Status = WOWSetEnvironmentVar_Oem(&lpEnvNew,
|
|
&g_ustrCompatLayerVar,
|
|
pEnvData->pszCompatLayerVal);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
}
|
|
|
|
if (pEnvData->pszShimFileLog != NULL) {
|
|
Status = WOWSetEnvironmentVar_Oem(&lpEnvNew,
|
|
&g_ustrShimFileLogVar,
|
|
pEnvData->pszShimFileLogVal);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
Done:
|
|
|
|
if (pszFullProcessHistory != NULL) {
|
|
free_w(pszFullProcessHistory);
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(Status) && lpEnvNew != NULL) {
|
|
//
|
|
// This points to the cloned environment ALWAYS.
|
|
//
|
|
RtlDestroyEnvironment(lpEnvNew);
|
|
lpEnvNew = NULL;
|
|
}
|
|
|
|
return(LPWSTR)lpEnvNew;
|
|
}
|
|
|
|
//
|
|
// GetModName
|
|
// wTDB - TDB entry
|
|
// szModName - pointer to the buffer that receives module name
|
|
// buffer should be at least 9 characters long
|
|
//
|
|
// returns FALSE if the entry is invalid
|
|
|
|
|
|
BOOL
|
|
GetWOWModName(
|
|
WORD wTDB,
|
|
PCH szModName
|
|
)
|
|
{
|
|
PTDB pTDB;
|
|
PCH pch;
|
|
|
|
pTDB = GetTDB(wTDB);
|
|
if (NULL == pTDB) {
|
|
return FALSE;
|
|
}
|
|
|
|
RtlCopyMemory(szModName, pTDB->TDB_ModName, 8 * sizeof(CHAR)); // we have modname now
|
|
szModName[8] = '\0';
|
|
|
|
pch = &szModName[8];
|
|
while (*(--pch) == ' ') {
|
|
*pch = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// IsWowExec
|
|
// IN wTDB - entry into the task database
|
|
// Returns:
|
|
// TRUE if this particular entry points to WOWEXEC
|
|
//
|
|
// Note:
|
|
// WOWEXEC is a special stub module that always runs on NTVDM
|
|
// new tasks are spawned by wowexec (in the most typical case)
|
|
// it is therefore the "root" module and it's environment's contents
|
|
// should not be counted, since we don't know what was ntvdm's parent process
|
|
//
|
|
|
|
BOOL
|
|
IsWOWExecBoot(
|
|
WORD wTDB
|
|
)
|
|
{
|
|
PTDB pTDB;
|
|
CHAR szModName[9];
|
|
|
|
pTDB = GetTDB(wTDB);
|
|
if (NULL == pTDB) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!GetWOWModName(wTDB, szModName)) { // can we get modname ?
|
|
return FALSE;
|
|
}
|
|
|
|
return (0 == _strcmpi(szModName, "wowexec")); // is the module named WOWEXEC ?
|
|
}
|
|
|
|
|
|
BOOL
|
|
WOWInheritEnvironment(
|
|
PTD pTD, // this TD
|
|
PTD pTDParent, // parent TD
|
|
LPCWSTR pwszLayers, // new layers var
|
|
LPCSTR pszFileName // exe filename
|
|
)
|
|
{
|
|
UNICODE_STRING ustrLayers = { 0 };
|
|
OEM_STRING oemLayers = { 0 };
|
|
PWOWENVDATA pEnvData = NULL;
|
|
PWOWENVDATA pEnvDataParent = NULL;
|
|
DWORD dwLength = sizeof(WOWENVDATA);
|
|
BOOL bSuccess = FALSE;
|
|
PCH pBuffer;
|
|
|
|
|
|
// assert (pszFileName != NULL)
|
|
|
|
// check if this is dreaded wowexec
|
|
if (IsWOWExecBoot(pTD->htask16)) {
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
if (NULL != pwszLayers) {
|
|
RtlInitUnicodeString(&ustrLayers, pwszLayers);
|
|
RtlUnicodeStringToOemString(&oemLayers, &ustrLayers, TRUE);
|
|
}
|
|
|
|
if (pTDParent != NULL) {
|
|
pEnvDataParent = pTDParent->pWowEnvDataChild; // from parent, created for our consumption
|
|
}
|
|
|
|
//
|
|
// inherit process history (normal that is)
|
|
//
|
|
if (pEnvDataParent != NULL) {
|
|
dwLength += pEnvDataParent->pszProcessHistory == NULL ? 0 : strlen(pEnvDataParent->pszProcessHistory) + 1;
|
|
dwLength += pEnvDataParent->pszShimFileLog == NULL ? 0 : strlen(pEnvDataParent->pszShimFileLog) + 1;
|
|
dwLength += pEnvDataParent->pszCurrentProcessHistory == NULL ? 0 : strlen(pEnvDataParent->pszCurrentProcessHistory) + 1;
|
|
}
|
|
|
|
dwLength += oemLayers.Length != 0 ? oemLayers.Length + 1 + strlen(g_szCompatLayerVar) + 1 : 0; // length for layers
|
|
dwLength += strlen(pszFileName) + 1;
|
|
|
|
//
|
|
// now all components are done, allocate
|
|
//
|
|
|
|
pEnvData = (PWOWENVDATA)malloc_w(dwLength);
|
|
if (pEnvData == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
RtlZeroMemory(pEnvData, dwLength);
|
|
|
|
pBuffer = (PCH)(pEnvData + 1);
|
|
|
|
if (pEnvDataParent != NULL) {
|
|
if (pEnvDataParent->pszProcessHistory) {
|
|
pEnvData->pszProcessHistory = pBuffer;
|
|
strcpy(pBuffer, pEnvDataParent->pszProcessHistory);
|
|
pEnvData->pszProcessHistoryVal = pEnvData->pszProcessHistory +
|
|
(INT)(pEnvDataParent->pszProcessHistoryVal - pEnvDataParent->pszProcessHistory);
|
|
pBuffer += strlen(pBuffer) + 1;
|
|
}
|
|
|
|
if (pEnvDataParent->pszShimFileLog) {
|
|
pEnvData->pszShimFileLog = pBuffer;
|
|
strcpy(pBuffer, pEnvDataParent->pszShimFileLog);
|
|
pEnvData->pszShimFileLogVal = pEnvData->pszShimFileLog +
|
|
(INT)(pEnvDataParent->pszShimFileLogVal - pEnvDataParent->pszShimFileLog);
|
|
pBuffer += strlen(pBuffer) + 1;
|
|
}
|
|
|
|
}
|
|
|
|
if (oemLayers.Length) {
|
|
pEnvData->pszCompatLayer = pBuffer;
|
|
strcpy(pBuffer, g_szCompatLayerVar); // __COMPAT_LAYER
|
|
strcat(pBuffer, "=");
|
|
pEnvData->pszCompatLayerVal = pBuffer + strlen(pBuffer);
|
|
strcpy(pEnvData->pszCompatLayerVal, oemLayers.Buffer);
|
|
|
|
pBuffer += strlen(pBuffer) + 1;
|
|
}
|
|
|
|
//
|
|
// Process History HAS to be the last item
|
|
//
|
|
|
|
pEnvData->pszCurrentProcessHistory = pBuffer;
|
|
*pBuffer = '\0';
|
|
|
|
if (pEnvDataParent != NULL) {
|
|
if (pEnvDataParent->pszCurrentProcessHistory) {
|
|
pEnvData->pszCurrentProcessHistory = pBuffer;
|
|
strcat(pBuffer, pEnvDataParent->pszCurrentProcessHistory);
|
|
strcat(pBuffer, ";");
|
|
}
|
|
}
|
|
|
|
strcat(pEnvData->pszCurrentProcessHistory, pszFileName);
|
|
|
|
bSuccess = TRUE;
|
|
|
|
out:
|
|
RtlFreeOemString(&oemLayers);
|
|
|
|
pTD->pWowEnvData = pEnvData;
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
PFLAGINFOBITS CheckFlagInfo(DWORD FlagType, DWORD dwFlag) {
|
|
PFLAGINFOBITS pFlagInfoBits;
|
|
switch(FlagType){
|
|
case WOWCOMPATFLAGSEX:
|
|
pFlagInfoBits = CURRENTPTD()->pWOWCompatFlagsEx_Info;
|
|
break;
|
|
case WOWCOMPATFLAGS2:
|
|
pFlagInfoBits = CURRENTPTD()->pWOWCompatFlags2_Info;
|
|
break;
|
|
default:
|
|
WOW32ASSERTMSG((FALSE), ("CheckFlagInfo called with invalid FlagType!"));
|
|
return NULL;
|
|
}
|
|
while(pFlagInfoBits) {
|
|
if(pFlagInfoBits->dwFlag == dwFlag) {
|
|
return pFlagInfoBits;
|
|
}
|
|
pFlagInfoBits = pFlagInfoBits->pNextFlagInfoBits;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
PFLAGINFOBITS InitFlagInfo(PVOID pFlagInfo,DWORD FlagType,DWORD dwFlag) {
|
|
UNICODE_STRING uCmdLine = { 0 };
|
|
OEM_STRING oemCmdLine = { 0 };
|
|
LPWSTR lpwCmdLine;
|
|
|
|
|
|
DWORD dwMark = 0x80000000;
|
|
LPSTR pszTmp;
|
|
PFLAGINFOBITS pFlagInfoBits,pFlagInfoBitsHead = NULL;
|
|
LPSTR pszCmdLine;
|
|
LPSTR *pFlagArgv;
|
|
DWORD dwFlagArgc;
|
|
|
|
|
|
if(pFlagInfo == NULL || 0 == dwFlag) {
|
|
return NULL;
|
|
}
|
|
|
|
while(dwMark) {
|
|
if(dwFlag & dwMark) {
|
|
|
|
pFlagArgv = NULL;
|
|
pszCmdLine = NULL;
|
|
pFlagInfoBits = NULL;
|
|
lpwCmdLine = NULL;
|
|
|
|
switch(FlagType) {
|
|
case WOWCOMPATFLAGSEX:
|
|
GET_WOWCOMPATFLAGSEX_CMDLINE(pFlagInfo, dwMark, &lpwCmdLine);
|
|
break;
|
|
case WOWCOMPATFLAGS2:
|
|
GET_WOWCOMPATFLAGS2_CMDLINE(pFlagInfo, dwMark, &lpwCmdLine);
|
|
break;
|
|
default:
|
|
WOW32ASSERTMSG((FALSE), ("InitFlagInfo called with invalid FlagType!"));
|
|
return NULL;
|
|
}
|
|
|
|
if(lpwCmdLine) {
|
|
|
|
//
|
|
// Convert to oem string
|
|
//
|
|
|
|
RtlInitUnicodeString(&uCmdLine, lpwCmdLine);
|
|
|
|
pszCmdLine = malloc_w(uCmdLine.Length+1);
|
|
if(NULL == pszCmdLine) {
|
|
goto GFIerror;
|
|
}
|
|
|
|
oemCmdLine.Buffer = pszCmdLine;
|
|
oemCmdLine.MaximumLength = uCmdLine.Length+1;
|
|
RtlUnicodeStringToOemString(&oemCmdLine, &uCmdLine, FALSE);
|
|
|
|
|
|
pFlagInfoBits = malloc_w(sizeof(FLAGINFOBITS));
|
|
if(NULL == pFlagInfoBits) {
|
|
goto GFIerror;
|
|
}
|
|
pFlagInfoBits->pNextFlagInfoBits = NULL;
|
|
pFlagInfoBits->dwFlag = dwMark;
|
|
pFlagInfoBits->dwFlagType = FlagType;
|
|
pFlagInfoBits->pszCmdLine = pszCmdLine;
|
|
|
|
//
|
|
// Parse commandline to argv, argc format
|
|
//
|
|
|
|
dwFlagArgc = 1;
|
|
pszTmp = pszCmdLine;
|
|
while(*pszTmp) {
|
|
if(*pszTmp == ';') {
|
|
dwFlagArgc++;
|
|
}
|
|
pszTmp++;
|
|
}
|
|
|
|
pFlagInfoBits->dwFlagArgc = dwFlagArgc;
|
|
|
|
pFlagArgv = malloc_w(sizeof(LPSTR)*dwFlagArgc);
|
|
|
|
if (NULL == pFlagArgv) {
|
|
goto GFIerror;
|
|
}
|
|
|
|
pFlagInfoBits->pFlagArgv = pFlagArgv;
|
|
|
|
pszTmp = pszCmdLine;
|
|
while(*pszTmp) {
|
|
if(*pszTmp == ';'){
|
|
if(pszCmdLine != pszTmp) {
|
|
*pFlagArgv++ = pszCmdLine;
|
|
}
|
|
else {
|
|
*pFlagArgv++ = NULL;
|
|
}
|
|
*pszTmp = '\0';
|
|
pszCmdLine = pszTmp+1;
|
|
}
|
|
pszTmp++;
|
|
}
|
|
*pFlagArgv = pszCmdLine;
|
|
|
|
if(pFlagInfoBitsHead) {
|
|
pFlagInfoBits->pNextFlagInfoBits = pFlagInfoBitsHead;
|
|
pFlagInfoBitsHead = pFlagInfoBits;
|
|
}
|
|
else {
|
|
pFlagInfoBitsHead = pFlagInfoBits;
|
|
}
|
|
}
|
|
}
|
|
// Check next bit
|
|
dwMark = dwMark>>1;
|
|
}
|
|
|
|
return pFlagInfoBitsHead;
|
|
|
|
GFIerror:
|
|
if (pszCmdLine) {
|
|
free_w(pszCmdLine);
|
|
}
|
|
if (pFlagInfoBits) {
|
|
free_w(pFlagInfoBits);
|
|
}
|
|
if (pFlagArgv) {
|
|
free_w(pFlagArgv);
|
|
}
|
|
return pFlagInfoBitsHead;
|
|
}
|
|
|
|
VOID FreeFlagInfo(PFLAGINFOBITS pFlagInfoBits){
|
|
PFLAGINFOBITS pFlagInfoBitsTmp;
|
|
while(pFlagInfoBits) {
|
|
pFlagInfoBitsTmp = pFlagInfoBits->pNextFlagInfoBits;
|
|
if(pFlagInfoBits->pszCmdLine) {
|
|
free_w(pFlagInfoBits->pszCmdLine);
|
|
}
|
|
if(pFlagInfoBits->pFlagArgv) {
|
|
free_w(pFlagInfoBits->pFlagArgv);
|
|
}
|
|
free_w(pFlagInfoBits);
|
|
pFlagInfoBits = pFlagInfoBitsTmp;
|
|
}
|
|
}
|