/*++ Copyright (c) 2000 Microsoft Corporation Module Name: Environment.Cpp Abstract: Various environment-related function Notes: Cloning environment for the purpose of using it in Rtl* environment-related functions History: 10/26/00 VadimB Created --*/ #include "precomp.h" IMPLEMENT_SHIM_BEGIN(Win2kPropagateLayer) #include "ShimHookMacro.h" #include "Win2kPropagateLayer.h" // // This is so we can compare offsets if we know the segments are equal // #define OFFSET(x) (LOWORD((DWORD)(x))) // // I'm cheating here to make some functions a little faster; // we won't have to push a word on the stack every time // static WORD gwMatch; // // ChrCmp - Case sensitive character comparison for DBCS // Assumes w1, gwMatch are characters to be compared // Return FALSE if they match, TRUE if no match // static BOOL ChrCmp( WORD w1 ) { // // Most of the time this won't match, so test it first for speed. // if( LOBYTE( w1 ) == LOBYTE( gwMatch ) ) { if( IsDBCSLeadByte( LOBYTE( w1 ) ) ) { return( w1 != gwMatch ); } return FALSE; } return TRUE; } // // StrRChr - Find last occurrence of character in string // Assumes lpStart points to start of string // lpEnd points to end of string (NOT included in search) // wMatch is the character to match // returns ptr to the last occurrence of ch in str, NULL if not found. // static LPSTR StrRChr( LPSTR lpStart, LPSTR lpEnd, WORD wMatch ) { LPSTR lpFound = NULL; if( !lpEnd ) lpEnd = lpStart + strlen( lpStart ); gwMatch = wMatch; for( ; OFFSET( lpStart ) < OFFSET( lpEnd ); lpStart = CharNextA( lpStart ) ) { if( !ChrCmp( *(LPWORD)lpStart ) ) lpFound = lpStart; } return( lpFound ); } // // Find environment variable pszName within the buffer pszEnv // ppszVal receives pointer to the variable's value // PSZ ShimFindEnvironmentVar( PSZ pszName, PSZ pszEnv, PSZ* ppszVal ) { int nNameLen = strlen(pszName); PSZ pTemp; if (pszEnv != NULL) { while (*pszEnv != '\0') { // // Check the first char to be speedy. // if (*pszName == *pszEnv) { // // Compare the rest now. // if ((pTemp = StrRChr(pszEnv, NULL, '=')) != NULL && (int)(pTemp - pszEnv) == nNameLen && !_strnicmp(pszEnv, pszName, nNameLen)) { // // Found it. // if (ppszVal != NULL) { *ppszVal = pTemp + 1; } return pszEnv; } } pszEnv += strlen(pszEnv) + 1; } } return NULL; } // // returns size in characters // of an env block // pStrCount receives the number of env strings // DWORD ShimGetEnvironmentSize( PSZ pszEnv, LPDWORD pStrCount ) { PSZ pTemp = pszEnv; DWORD dwCount = 0; while (*pTemp != '\0') { dwCount++; pTemp += strlen(pTemp) + 1; } pTemp++; if (pStrCount != NULL) { *pStrCount = dwCount; } return (DWORD)(pTemp - pszEnv); } // returns size (in characters) of an environment block DWORD ShimGetEnvironmentSize( WCHAR* pwszEnv, LPDWORD pStrCount ) { WCHAR* pTemp = pwszEnv; DWORD dwCount = 0; while(*pTemp != L'\0') { dwCount++; pTemp += wcslen(pTemp) + 1; } pTemp++; // include terminating '\0' if (pStrCount != NULL) { *pStrCount = dwCount; } return (DWORD)(pTemp - pwszEnv); } // // returns cloned (unicode) environment // NTSTATUS ShimCloneEnvironment( LPVOID* ppEnvOut, LPVOID lpEnvironment, BOOL bUnicode ) { NTSTATUS Status = STATUS_INVALID_PARAMETER; DWORD dwEnvSize = 0; LPVOID lpEnvNew = NULL; MEMORY_BASIC_INFORMATION MemoryInformation; if (lpEnvironment == NULL) { Status = RtlCreateEnvironment(TRUE, &lpEnvNew); } else { // // Find the environment's size in characters but recalc in unicode. // dwEnvSize = (bUnicode ? ShimGetEnvironmentSize((WCHAR*)lpEnvironment, NULL) : ShimGetEnvironmentSize((PSZ)lpEnvironment, NULL)); // // Allocate memory -- using Zw routines (that is what rtl is using). // MemoryInformation.RegionSize = (dwEnvSize + 2) * sizeof(UNICODE_NULL); Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &lpEnvNew, 0, &MemoryInformation.RegionSize, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(Status)) { LOGN( eDbgLevelError, "[ShimCloneEnvironment] Failed to allocate %d bytes for the environment block.", dwEnvSize * sizeof(UNICODE_NULL)); return Status; } if (bUnicode) { // // Unicode, just copy the environment // RtlMoveMemory(lpEnvNew, lpEnvironment, dwEnvSize * sizeof(UNICODE_NULL)); } else { // // The environment is ANSI, so we need to convert. // UNICODE_STRING UnicodeBuffer; ANSI_STRING AnsiBuffer; AnsiBuffer.Buffer = (CHAR*)lpEnvironment; AnsiBuffer.Length = AnsiBuffer.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 = RtlAnsiStringToUnicodeString(&UnicodeBuffer, &AnsiBuffer, FALSE); if (!NT_SUCCESS(Status)) { LOGN( eDbgLevelError, "[ShimCloneEnvironment] Failed to convert ANSI environment to UNICODE. Status = 0x%x", Status); } } } if (NT_SUCCESS(Status)) { *ppEnvOut = lpEnvNew; } else { if (lpEnvNew != NULL) { RtlDestroyEnvironment(lpEnvNew); } } return Status; } NTSTATUS ShimFreeEnvironment( LPVOID lpEnvironment ) { NTSTATUS Status; __try { Status = RtlDestroyEnvironment(lpEnvironment); if (!NT_SUCCESS(Status)) { LOGN( eDbgLevelError, "[ShimFreeEnvironment] RtlDestroyEnvironment failed. Status = 0x%x", Status); } } __except(WOWPROCESSHISTORYEXCEPTIONFILTER) { Status = STATUS_ACCESS_VIOLATION; } return Status; } // // Set environment variable, possibly create or clone provided environment // NTSTATUS ShimSetEnvironmentVar( 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); if (!NT_SUCCESS(Status)) { LOGN( eDbgLevelError, "[ShimSetEnvironmentVar] RtlSetEnvironmentVariable failed. Status = 0x%x", Status); } return Status; } IMPLEMENT_SHIM_END