Lately I've been messing with Windows, and I came across this function in ntdll.dll
.
I tried searching, but there seems to be no documentation at all on it.
Could anybody hint me what kind of environment variables does it query?
Seeing that most probably I'd get little help on something weird like this (or rather, I'm impatient), I decided to do some research on it myself.
Here's some pseudocode I made up to understand the function: (scroll down for TL;DR)
// Credits to:
// -> http://filelog.net/func/RtlQueryEnvironmentVariable
// For helping with the arguments' names and types
// -> IDA Pro
// -> NirSoft for the RTL_CRITICAL_SECTION structure
// http://www.nirsoft.net/kernel_struct/vista/RTL_CRITICAL_SECTION.html
// https://msdn.microsoft.com/en-us/library/cc704588.aspx
#ifndef STATUS_VARIABLE_NOT_FOUND
#define STATUS_VARIABLE_NOT_FOUND 0xC0000100
#endif
NTSTATUS __stdcall RtlQueryEnvironmentVariable(PVOID Environment, PWSTR Name, size_t NameLength, PWSTR Value, size_t ValueLength, PSIZE_T ReturnLength){
// Here happens some exception stuff
// ...
// Return variable
NTSTATUS ret;
// PEB environment
PVOID pEnv = &teb->ProcessEnvironmentBlock->ProcessParameters->Environment;
// mov ebx, [ebp+ReturnLength]
// xor esi, esi
// mov [ebx], esi
*ReturnLength = 0;
// Sanity check
if ( !NameLength )
return STATUS_VARIABLE_NOT_FOUND;
// Here happens some exception stuff
// ...
// Check the variable
NTSTATUS envVar = RtlpCheckPseudoEnvironmentVariable(Name, NameLength, Value, ValueLength, ReturnLength);
// If the variable exists, fail.
if ( envVar >= 0 ) {
goto sehReturn;
}
/* Only process variables that haven't been set as pseudo.
I could've joined this with the previous if () with ||,
but I'd rather leave it as is for easier comprehension.
Do note that this is a signed comparison, and it's why
this doesn't always simply jump to 'sehReturn'.
P.S. typedef long NTSTATUS; 'long' is signed by default */
if ( envVar != (signed int) STATUS_VARIABLE_NOT_FOUND ) {
goto sehReturn;
}
// In case there's no environment supplied, just take it from TEB->PEB->ProcessParameters->Environment
if ( !Environment ) {
// Get a pointer to TEB
TEB* teb = __readfsdword(0x18);
// Wait till we get thread-safe access to PEB
RtlEnterCriticalSection(&FastPebLock);
// Exception handling stuff
// ...
// Try to load it from cache
NTSTATUS varFromCache = RtlpQueryEnvironmentCache(
pEnv,
Name, NameLength,
Value, ValueLength,
ReturnLength
);
// Get it from the actual environment if it isn't in the cache
if ( varFromCache == STATUS_VARIABLE_NOT_FOUND ) {
varFromCache = RtlpScanEnvironment(
pEnv,
Name, NameLength,
Value, ValueLength,
ReturnLength,
// I'm not really sure what this argument is for,
// but it's set to FALSE when the environment is not
// the same as the current process or the critical
// section is locked by a thread. mainly corner cases.
TRUE
);
}
// Save return value
ret = varFromCache;
// Exception handling stuff
// ...
RtlLeaveCriticalSection(&FastPebLock);
goto sehReturn;
}
// Try to determine if environment is valid
if ( !*PWORD(Environment) ) {
ret = STATUS_VARIABLE_NOT_FOUND;
goto sehReturn;
}
RTL_CRITICAL_SECTION* pCriticalSection = teb->ProcessEnvironmentBlock->FastPebLock;
BOOL cornerCase;
// Set 'cornerCase' to FALSE if the environment is not our process'
// environment or if it is locked by a thread.
if ( pEnv != Environment || (pCriticalSection != NULL && !RtlIsCriticalSectionLockedByThread(pCriticalSection)) ) {
cornerCase = FALSE;
} else {
// If the variable is in the current process, try to take it from the cache.
NTSTATUS varFromCache = RtlpQueryEnvironmentCache(
Environment,
Name, NameLength,
Value, ValueLength,
ReturnLength
);
if ( varFromCache != STATUS_VARIABLE_NOT_FOUND ) {
ret = varFromCache;
goto sehReturn;
}
cornerCase = TRUE;
}
// Take the variable from the environment
RtlpScanEnvironment(
Environment,
Name, NameLength,
Value, ValueLength,
ReturnLength,
cornerCase
);
// Structured Exception Handler return; does some SEH stuff and returns.
sehReturn:
ms_exc.registration.TryLevel = -2;
return ret;
}
TL;DR This basically checks for environment variables in PEB->ProcessParameters->Reserved2 (Environment), and returns it in the passed pointers.
It checks the cache, and normalizes the case.