/*++ * * 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 #include "wshimdb.h" MODNAME(wshimdb.c); CHAR g_szCompatLayerVar[] = "__COMPAT_LAYER"; CHAR g_szProcessHistoryVar[] = "__PROCESS_HISTORY"; CHAR g_szShimFileLogVar[] = "SHIM_FILE_LOG"; 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; 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); if (pwszTempEnv != NULL) { WOWFreeUnicodeEnvironment(pwszTempEnv); } if (fReturn && AHInfo.tiExe) { fReturn = ApphelpShowDialog(&AHInfo,&hProcess); if (fReturn && hProcess) { ghTaskAppHelp = hProcess; } } 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 // 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; }