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.
1883 lines
66 KiB
1883 lines
66 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
regutil.c
|
|
|
|
Abstract:
|
|
|
|
This file contains support routines for accessing the registry.
|
|
|
|
Author:
|
|
|
|
Steve Wood (stevewo) 15-Apr-1992
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "ntrtlp.h"
|
|
#include <ctype.h>
|
|
|
|
NTSTATUS
|
|
RtlpGetRegistryHandle(
|
|
IN ULONG RelativeTo,
|
|
IN PCWSTR KeyName,
|
|
IN BOOLEAN WriteAccess,
|
|
OUT PHANDLE Key
|
|
);
|
|
|
|
NTSTATUS
|
|
RtlpQueryRegistryDirect(
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN OUT PVOID Destination
|
|
);
|
|
|
|
NTSTATUS
|
|
RtlpCallQueryRegistryRoutine(
|
|
IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
|
|
IN PKEY_VALUE_FULL_INFORMATION KeyValueInformation,
|
|
IN OUT PULONG PKeyValueInfoLength,
|
|
IN PVOID Context,
|
|
IN PVOID Environment OPTIONAL
|
|
);
|
|
|
|
PVOID
|
|
RtlpAllocDeallocQueryBuffer(
|
|
IN OUT SIZE_T *PAllocLength OPTIONAL,
|
|
IN PVOID OldKeyValueInformation OPTIONAL,
|
|
IN SIZE_T OldAllocLength OPTIONAL,
|
|
OUT NTSTATUS *pStatus OPTIONAL
|
|
);
|
|
|
|
NTSTATUS
|
|
RtlpInitCurrentUserString(
|
|
OUT PUNICODE_STRING UserString
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
RtlpGetTimeZoneInfoHandle(
|
|
IN BOOLEAN WriteAccess,
|
|
OUT PHANDLE Key
|
|
);
|
|
|
|
#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
|
|
#pragma alloc_text(PAGE,RtlpGetRegistryHandle)
|
|
#pragma alloc_text(PAGE,RtlpQueryRegistryDirect)
|
|
#pragma alloc_text(PAGE,RtlpCallQueryRegistryRoutine)
|
|
#pragma alloc_text(PAGE,RtlpAllocDeallocQueryBuffer)
|
|
#pragma alloc_text(PAGE,RtlQueryRegistryValues)
|
|
#pragma alloc_text(PAGE,RtlWriteRegistryValue)
|
|
#pragma alloc_text(PAGE,RtlCheckRegistryKey)
|
|
#pragma alloc_text(PAGE,RtlCreateRegistryKey)
|
|
#pragma alloc_text(PAGE,RtlDeleteRegistryValue)
|
|
#pragma alloc_text(PAGE,RtlExpandEnvironmentStrings_U)
|
|
#pragma alloc_text(PAGE,RtlFormatCurrentUserKeyPath)
|
|
#pragma alloc_text(PAGE,RtlGetNtGlobalFlags)
|
|
#pragma alloc_text(PAGE,RtlpInitCurrentUserString)
|
|
#pragma alloc_text(PAGE,RtlOpenCurrentUser)
|
|
#pragma alloc_text(PAGE,RtlpGetTimeZoneInfoHandle)
|
|
#pragma alloc_text(PAGE,RtlQueryTimeZoneInformation)
|
|
#pragma alloc_text(PAGE,RtlSetTimeZoneInformation)
|
|
#pragma alloc_text(PAGE,RtlSetActiveTimeBias)
|
|
#endif
|
|
|
|
extern const PWSTR RtlpRegistryPaths[ RTL_REGISTRY_MAXIMUM ];
|
|
|
|
NTSTATUS
|
|
RtlpGetRegistryHandle(
|
|
IN ULONG RelativeTo,
|
|
IN PCWSTR KeyName,
|
|
IN BOOLEAN WriteAccess,
|
|
OUT PHANDLE Key
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
WCHAR KeyPathBuffer[ MAXIMUM_FILENAME_LENGTH+6 ];
|
|
UNICODE_STRING KeyPath;
|
|
UNICODE_STRING CurrentUserKeyPath;
|
|
BOOLEAN OptionalPath;
|
|
|
|
if (RelativeTo & RTL_REGISTRY_HANDLE) {
|
|
*Key = (HANDLE)KeyName;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (RelativeTo & RTL_REGISTRY_OPTIONAL) {
|
|
RelativeTo &= ~RTL_REGISTRY_OPTIONAL;
|
|
OptionalPath = TRUE;
|
|
} else {
|
|
OptionalPath = FALSE;
|
|
}
|
|
|
|
if (RelativeTo >= RTL_REGISTRY_MAXIMUM) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
KeyPath.Buffer = KeyPathBuffer;
|
|
KeyPath.Length = 0;
|
|
KeyPath.MaximumLength = sizeof( KeyPathBuffer );
|
|
if (RelativeTo != RTL_REGISTRY_ABSOLUTE) {
|
|
if (RelativeTo == RTL_REGISTRY_USER &&
|
|
NT_SUCCESS( RtlFormatCurrentUserKeyPath( &CurrentUserKeyPath ) )
|
|
) {
|
|
Status = RtlAppendUnicodeStringToString( &KeyPath, &CurrentUserKeyPath );
|
|
RtlFreeUnicodeString( &CurrentUserKeyPath );
|
|
} else {
|
|
Status = RtlAppendUnicodeToString( &KeyPath, RtlpRegistryPaths[ RelativeTo ] );
|
|
}
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
Status = RtlAppendUnicodeToString( &KeyPath, L"\\" );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
Status = RtlAppendUnicodeToString( &KeyPath, KeyName );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Use a kernel-mode handle for the registry key to prevent
|
|
// malicious apps from hijacking it.
|
|
//
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&KeyPath,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (WriteAccess) {
|
|
Status = ZwCreateKey( Key,
|
|
GENERIC_WRITE,
|
|
&ObjectAttributes,
|
|
0,
|
|
(PUNICODE_STRING) NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
} else {
|
|
Status = ZwOpenKey( Key,
|
|
MAXIMUM_ALLOWED | GENERIC_READ,
|
|
&ObjectAttributes
|
|
);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// This is the maximum MaximumLength for a UNICODE_STRING that still leaves
|
|
// room for a UNICODE_NULL.
|
|
//
|
|
#define MAX_NONNULL_USTRING ( MAX_USTRING - sizeof(UNICODE_NULL) )
|
|
|
|
//
|
|
// Return a registry value for RTL_QUERY_REGISTRY_DIRECT.
|
|
// For string values, ValueLength includes the UNICODE_NULL.
|
|
// Truncate string values if they don't fit within a UNICODE_STRING.
|
|
//
|
|
NTSTATUS
|
|
RtlpQueryRegistryDirect(
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN OUT PVOID Destination
|
|
)
|
|
{
|
|
|
|
if (ValueType == REG_SZ ||
|
|
ValueType == REG_EXPAND_SZ ||
|
|
ValueType == REG_MULTI_SZ
|
|
) {
|
|
PUNICODE_STRING DestinationString;
|
|
USHORT TruncValueLength;
|
|
|
|
//
|
|
// Truncate ValueLength to be represented in a UNICODE_STRING
|
|
//
|
|
if ( ValueLength <= MAX_USTRING ) {
|
|
TruncValueLength = (USHORT)ValueLength;
|
|
} else {
|
|
TruncValueLength = MAX_USTRING;
|
|
|
|
#if DBG
|
|
DbgPrint("RtlpQueryRegistryDirect: truncating SZ Value length: %x -> %x\n",
|
|
ValueLength, TruncValueLength);
|
|
#endif //DBG
|
|
}
|
|
|
|
DestinationString = (PUNICODE_STRING)Destination;
|
|
if (DestinationString->Buffer == NULL) {
|
|
|
|
DestinationString->Buffer = RtlAllocateStringRoutine( TruncValueLength );
|
|
if (!DestinationString->Buffer) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
DestinationString->MaximumLength = TruncValueLength;
|
|
} else if (TruncValueLength > DestinationString->MaximumLength) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
RtlCopyMemory( DestinationString->Buffer, ValueData, TruncValueLength );
|
|
DestinationString->Length = (TruncValueLength - sizeof(UNICODE_NULL));
|
|
|
|
} else if (ValueLength <= sizeof( ULONG )) {
|
|
RtlCopyMemory( Destination, ValueData, ValueLength );
|
|
|
|
} else {
|
|
PULONG DestinationLength;
|
|
|
|
DestinationLength = (PULONG)Destination;
|
|
if ((LONG)*DestinationLength < 0) {
|
|
ULONG n = -(LONG)*DestinationLength;
|
|
|
|
if (n < ValueLength) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
RtlCopyMemory( DestinationLength, ValueData, ValueLength );
|
|
|
|
} else {
|
|
if (*DestinationLength < (2 * sizeof(*DestinationLength) + ValueLength)) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
*DestinationLength++ = ValueLength;
|
|
*DestinationLength++ = ValueType;
|
|
RtlCopyMemory( DestinationLength, ValueData, ValueLength );
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#define QuadAlignPtr(P) ( \
|
|
(PVOID)((((ULONG_PTR)(P)) + 7) & (-8)) \
|
|
)
|
|
|
|
NTSTATUS
|
|
RtlpCallQueryRegistryRoutine(
|
|
IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
|
|
IN PKEY_VALUE_FULL_INFORMATION KeyValueInformation,
|
|
IN OUT PULONG PKeyValueInfoLength,
|
|
IN PVOID Context,
|
|
IN PVOID Environment OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements the caller out the a caller specified
|
|
routine. It is reponsible for capturing the arguments for the
|
|
routine and then calling it. If not specifically disabled, this
|
|
routine will converted REG_EXPAND_SZ Registry values to REG_SZ by
|
|
calling RtlExpandEnvironmentStrings_U prior to calling the routine.
|
|
It will also converted REG_MULTI_SZ registry values into multiple
|
|
REG_SZ calls to the specified routine.
|
|
|
|
N.B. UNICODE_STRINGs cannot handle strings exceeding MAX_USTRING bytes. This creates
|
|
issues both for expansion and for returning queries. Whenever this limitation
|
|
is a encountered, we punt as best we can -- often returning an unexpanded, or perhaps
|
|
truncated stream -- since this seems to create fewer problems for our callers than
|
|
if we unexpectedly fail.
|
|
|
|
Arguments:
|
|
|
|
QueryTable - specifies the current query table entry.
|
|
|
|
KeyValueInformation - points to a buffer that contains the information
|
|
about the current registry value.
|
|
|
|
PKeyValueInfoLength - pointer to the maximum length of the KeyValueInformation
|
|
buffer. This function will use the
|
|
unused portion at the end of this buffer for storing null terminated
|
|
value name strings and the expanded version of REG_EXPAND_SZ values.
|
|
PKeyValueInfoLength returns an estimate of the space required if
|
|
STATUS_BUFFER_TOO_SMALL is returned. This estimate can be used to retry
|
|
with a larger buffer. Two retries may be required if REG_EXPAND_SZ is specified.
|
|
|
|
Context - specifies a 32-bit quantity that is passed uninterpreted to
|
|
each QueryRoutine called.
|
|
|
|
Environment - optional parameter, that if specified is the environment
|
|
used when expanding variable values in REG_EXPAND_SZ registry
|
|
values.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ValueType;
|
|
PWSTR ValueName;
|
|
PVOID ValueData;
|
|
ULONG ValueLength;
|
|
PWSTR s;
|
|
PCHAR FreeMem;
|
|
PCHAR EndFreeMem;
|
|
LONG FreeMemSize;
|
|
ULONG KeyValueInfoLength;
|
|
int retries;
|
|
|
|
|
|
//
|
|
// Return 0 length unless we return STATUS_BUFFER_TOO_SMALL.
|
|
//
|
|
KeyValueInfoLength = *PKeyValueInfoLength;
|
|
*PKeyValueInfoLength = 0;
|
|
|
|
//
|
|
// the registry has signaled no data for this value
|
|
//
|
|
if( KeyValueInformation->DataOffset == (ULONG)-1 ) {
|
|
//
|
|
// Return success unless this is a required value.
|
|
//
|
|
if ( QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED ) {
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
} else {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initially assume the entire KeyValueInformation buffer is unused.
|
|
//
|
|
|
|
FreeMem = (PCHAR)KeyValueInformation;
|
|
FreeMemSize = KeyValueInfoLength;
|
|
EndFreeMem = FreeMem + FreeMemSize;
|
|
|
|
if (KeyValueInformation->Type == REG_NONE ||
|
|
(KeyValueInformation->DataLength == 0 &&
|
|
KeyValueInformation->Type == QueryTable->DefaultType)
|
|
) {
|
|
|
|
//
|
|
// If there is no registry value then see if they want to default
|
|
// this value.
|
|
//
|
|
if (QueryTable->DefaultType == REG_NONE) {
|
|
//
|
|
// No default value specified. Return success unless this is
|
|
// a required value.
|
|
//
|
|
if ( QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED ) {
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
} else {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Default requested. Setup the value data pointers from the
|
|
// information in the table entry.
|
|
//
|
|
|
|
ValueName = QueryTable->Name,
|
|
ValueType = QueryTable->DefaultType;
|
|
ValueData = QueryTable->DefaultData;
|
|
ValueLength = QueryTable->DefaultLength;
|
|
if (ValueLength == 0) {
|
|
//
|
|
// If the length of the value is zero, then calculate the
|
|
// actual length for REG_SZ, REG_EXPAND_SZ and REG_MULTI_SZ
|
|
// value types.
|
|
//
|
|
|
|
s = (PWSTR)ValueData;
|
|
if (ValueType == REG_SZ || ValueType == REG_EXPAND_SZ) {
|
|
while (*s++ != UNICODE_NULL) {
|
|
}
|
|
ValueLength = (ULONG)((PCHAR)s - (PCHAR)ValueData);
|
|
|
|
} else if (ValueType == REG_MULTI_SZ) {
|
|
while (*s != UNICODE_NULL) {
|
|
while (*s++ != UNICODE_NULL) {
|
|
}
|
|
}
|
|
ValueLength = (ULONG)((PCHAR)s - (PCHAR)ValueData) + sizeof( UNICODE_NULL );
|
|
}
|
|
}
|
|
|
|
} else {
|
|
if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT)) {
|
|
LONG ValueSpaceNeeded;
|
|
|
|
//
|
|
// There is a registry value. Calculate a pointer to the
|
|
// free memory at the end of the value information buffer,
|
|
// and its size.
|
|
//
|
|
if (KeyValueInformation->DataLength) {
|
|
FreeMem += KeyValueInformation->DataOffset +
|
|
KeyValueInformation->DataLength;
|
|
} else {
|
|
FreeMem += FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) +
|
|
KeyValueInformation->NameLength;
|
|
}
|
|
FreeMem = (PCHAR)QuadAlignPtr(FreeMem);
|
|
FreeMemSize = (ULONG) (EndFreeMem - FreeMem);
|
|
|
|
//
|
|
// See if there is room in the free memory area for a null
|
|
// terminated copy of the value name string. If not return
|
|
// the length we require (so far) and an error.
|
|
//
|
|
ValueSpaceNeeded = KeyValueInformation->NameLength + sizeof(UNICODE_NULL);
|
|
if ( FreeMemSize < ValueSpaceNeeded ) {
|
|
|
|
*PKeyValueInfoLength = (ULONG)(((PCHAR)FreeMem - (PCHAR)KeyValueInformation) + ValueSpaceNeeded);
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// There is room, so copy the string, and null terminate it.
|
|
//
|
|
|
|
ValueName = (PWSTR)FreeMem;
|
|
RtlCopyMemory( ValueName,
|
|
KeyValueInformation->Name,
|
|
KeyValueInformation->NameLength
|
|
);
|
|
*(PWSTR)((PCHAR)ValueName + KeyValueInformation->NameLength) = UNICODE_NULL;
|
|
|
|
//
|
|
// Update the free memory pointer and size to reflect the space we
|
|
// just used for the null terminated value name.
|
|
//
|
|
FreeMem += ValueSpaceNeeded;
|
|
FreeMem = (PCHAR)QuadAlignPtr(FreeMem);
|
|
FreeMemSize = (LONG) (EndFreeMem - FreeMem);
|
|
|
|
} else {
|
|
ValueName = QueryTable->Name;
|
|
}
|
|
|
|
//
|
|
// Get the remaining data for the registry value.
|
|
//
|
|
|
|
ValueType = KeyValueInformation->Type;
|
|
ValueData = (PCHAR)KeyValueInformation + KeyValueInformation->DataOffset;
|
|
ValueLength = KeyValueInformation->DataLength;
|
|
}
|
|
|
|
//
|
|
// Unless specifically disabled for this table entry, preprocess
|
|
// registry values of type REG_EXPAND_SZ and REG_MULTI_SZ
|
|
//
|
|
|
|
if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_NOEXPAND)) {
|
|
if (ValueType == REG_MULTI_SZ) {
|
|
PWSTR ValueEnd;
|
|
|
|
//
|
|
// For REG_MULTI_SZ value type, call the query routine once
|
|
// for each null terminated string in the registry value. Fake
|
|
// like this is multiple REG_SZ values with the same value name.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
ValueEnd = (PWSTR)((PCHAR)ValueData + ValueLength) - sizeof(UNICODE_NULL);
|
|
s = (PWSTR)ValueData;
|
|
while (s < ValueEnd) {
|
|
while (*s++ != UNICODE_NULL) {
|
|
}
|
|
|
|
ValueLength = (ULONG)((PCHAR)s - (PCHAR)ValueData);
|
|
if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) {
|
|
Status = RtlpQueryRegistryDirect( REG_SZ,
|
|
ValueData,
|
|
ValueLength,
|
|
QueryTable->EntryContext
|
|
);
|
|
(PUNICODE_STRING)(QueryTable->EntryContext) += 1;
|
|
|
|
} else {
|
|
Status = (QueryTable->QueryRoutine)( ValueName,
|
|
REG_SZ,
|
|
ValueData,
|
|
ValueLength,
|
|
Context,
|
|
QueryTable->EntryContext
|
|
);
|
|
}
|
|
|
|
//
|
|
// We ignore failures where the buffer is too small.
|
|
//
|
|
if (Status == STATUS_BUFFER_TOO_SMALL) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
break;
|
|
}
|
|
|
|
ValueData = (PVOID)s;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// If requested, expand the Value -- but only if the unexpanded value
|
|
// can be represented with a UNICODE_STRING.
|
|
//
|
|
if ((ValueType == REG_EXPAND_SZ) &&
|
|
(ValueLength >= sizeof(WCHAR)) &&
|
|
(ValueLength <= MAX_NONNULL_USTRING)) {
|
|
//
|
|
// For REG_EXPAND_SZ value type, expand any environment variable
|
|
// references in the registry value string using the Rtl function.
|
|
//
|
|
|
|
UNICODE_STRING Source;
|
|
UNICODE_STRING Destination;
|
|
PWCHAR Src;
|
|
ULONG SrcLength;
|
|
ULONG RequiredLength;
|
|
BOOLEAN PercentFound;
|
|
|
|
//
|
|
// Don't expand unless we have to since expansion doubles buffer usage.
|
|
//
|
|
|
|
PercentFound = FALSE;
|
|
SrcLength = ValueLength - sizeof(WCHAR);
|
|
Src = (PWSTR)ValueData;
|
|
while (SrcLength) {
|
|
if (*Src == L'%') {
|
|
PercentFound = TRUE;
|
|
break;
|
|
}
|
|
Src++;
|
|
SrcLength -= sizeof(WCHAR);
|
|
}
|
|
|
|
if ( PercentFound ) {
|
|
Source.Buffer = (PWSTR)ValueData;
|
|
Source.MaximumLength = (USHORT)ValueLength;
|
|
Source.Length = (USHORT)(Source.MaximumLength - sizeof(UNICODE_NULL));
|
|
Destination.Buffer = (PWSTR)FreeMem;
|
|
Destination.Length = 0;
|
|
|
|
if (FreeMemSize <= 0) {
|
|
Destination.MaximumLength = 0;
|
|
} else if (FreeMemSize <= MAX_USTRING) {
|
|
Destination.MaximumLength = (USHORT)FreeMemSize;
|
|
Destination.Buffer[FreeMemSize/sizeof(WCHAR) - 1] = UNICODE_NULL;
|
|
} else {
|
|
Destination.MaximumLength = MAX_USTRING;
|
|
Destination.Buffer[MAX_USTRING/sizeof(WCHAR) - 1] = UNICODE_NULL;
|
|
}
|
|
|
|
Status = RtlExpandEnvironmentStrings_U( Environment,
|
|
&Source,
|
|
&Destination,
|
|
&RequiredLength
|
|
);
|
|
ValueType = REG_SZ;
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
ValueData = Destination.Buffer;
|
|
ValueLength = Destination.Length + sizeof( UNICODE_NULL );
|
|
} else {
|
|
if (Status == STATUS_BUFFER_TOO_SMALL) {
|
|
*PKeyValueInfoLength = (ULONG)((PCHAR)FreeMem - (PCHAR)KeyValueInformation) + RequiredLength;
|
|
}
|
|
//#if DBG
|
|
if (Status == STATUS_BUFFER_TOO_SMALL) {
|
|
DbgPrint( "RTL: Expand variables for %wZ failed - Status == %lx Size %x > %x <%x>\n",
|
|
&Source, Status, *PKeyValueInfoLength, KeyValueInfoLength,
|
|
Destination.MaximumLength );
|
|
} else {
|
|
DbgPrint( "RTL: Expand variables for %wZ failed - Status == %lx\n", &Source, Status );
|
|
}
|
|
//#endif // DBG
|
|
if ( Status == STATUS_BUFFER_OVERFLOW ||
|
|
Status == STATUS_BUFFER_TOO_SMALL &&
|
|
( Destination.MaximumLength == MAX_USTRING
|
|
|| RequiredLength > MAX_NONNULL_USTRING ) ) {
|
|
|
|
// We can't do variable expansion because the required buffer can't be described
|
|
// by a UNICODE_STRING, so we silently ignore expansion.
|
|
//#if DBG
|
|
DbgPrint("RtlpCallQueryRegistryRoutine: skipping expansion. Status=%x RequiredLength=%x\n",
|
|
Status, RequiredLength);
|
|
//#endif //DBG
|
|
} else {
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//#if DBG
|
|
else if (ValueType == REG_EXPAND_SZ && ValueLength > MAX_NONNULL_USTRING) {
|
|
DbgPrint("RtlpCallQueryRegistryRoutine: skipping environment expansion. ValueLength=%x\n",
|
|
ValueLength);
|
|
}
|
|
//#endif //DBG
|
|
}
|
|
|
|
//
|
|
// No special process of the registry value required so just call
|
|
// the query routine.
|
|
//
|
|
if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) {
|
|
Status = RtlpQueryRegistryDirect( ValueType,
|
|
ValueData,
|
|
ValueLength,
|
|
QueryTable->EntryContext
|
|
);
|
|
} else {
|
|
Status = (QueryTable->QueryRoutine)( ValueName,
|
|
ValueType,
|
|
ValueData,
|
|
ValueLength,
|
|
Context,
|
|
QueryTable->EntryContext
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// At this point we fail silently if the buffer is too small.
|
|
//
|
|
if (Status == STATUS_BUFFER_TOO_SMALL) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Most of the registry queries in the kernel are small (40-50 bytes).
|
|
// User queries use ZwAllocateVirtualMemory, so nothing less than a page will do.
|
|
//
|
|
#ifdef NTOS_KERNEL_RUNTIME
|
|
#if defined(ALLOC_DATA_PRAGMA)
|
|
#pragma const_seg("PAGECONST")
|
|
#endif
|
|
const SIZE_T RtlpRegistryQueryInitialBuffersize = 0x80 + sizeof(PVOID);
|
|
#else
|
|
const SIZE_T RtlpRegistryQueryInitialBuffersize = PAGE_SIZE;
|
|
#endif
|
|
|
|
//
|
|
// Allocate, Free, or Free/Allocate space for registry queries.
|
|
//
|
|
PVOID
|
|
RtlpAllocDeallocQueryBuffer(
|
|
IN OUT SIZE_T *PAllocLength OPTIONAL,
|
|
IN PVOID OldKeyValueInformation OPTIONAL,
|
|
IN SIZE_T OldAllocLength OPTIONAL,
|
|
OUT NTSTATUS *pStatus OPTIONAL
|
|
)
|
|
{
|
|
PVOID Ptr = NULL;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
#ifdef NTOS_KERNEL_RUNTIME
|
|
|
|
//
|
|
// Kernel version
|
|
//
|
|
|
|
UNREFERENCED_PARAMETER( OldAllocLength );
|
|
|
|
if ( ARGUMENT_PRESENT(OldKeyValueInformation) ) {
|
|
ExFreePool( OldKeyValueInformation );
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(PAllocLength) ) {
|
|
Ptr = ExAllocatePoolWithTag( PagedPool, *PAllocLength, 'vrqR' );
|
|
if (Ptr == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
//
|
|
// User version
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT(OldKeyValueInformation) ) {
|
|
Status = ZwFreeVirtualMemory( NtCurrentProcess(),
|
|
&OldKeyValueInformation,
|
|
&OldAllocLength,
|
|
MEM_RELEASE );
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(PAllocLength) ) {
|
|
|
|
Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
|
|
&Ptr,
|
|
0,
|
|
PAllocLength,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE );
|
|
if (!NT_SUCCESS(Status)) {
|
|
Ptr = NULL;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
if ( ARGUMENT_PRESENT(pStatus) ) {
|
|
*pStatus = Status;
|
|
}
|
|
|
|
return Ptr;
|
|
}
|
|
|
|
NTSTATUS
|
|
RtlQueryRegistryValues(
|
|
IN ULONG RelativeTo,
|
|
IN PCWSTR Path,
|
|
IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
|
|
IN PVOID Context,
|
|
IN PVOID Environment OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allows the caller to query multiple values from the registry
|
|
sub-tree with a single call. The caller specifies an initial key path,
|
|
and a table. The table contains one or more entries that describe the
|
|
key values and subkey names the caller is interested in. This function
|
|
starts at the initial key and enumerates the entries in the table. For
|
|
each entry that specifies a value name or subkey name that exists in
|
|
the registry, this function calls the caller's query routine associated
|
|
with each table entry. The caller's query routine is passed the value
|
|
name, type, data and data length, to do with what they wish.
|
|
|
|
Arguments:
|
|
|
|
RelativeTo - specifies that the Path parameter is either an absolute
|
|
registry path, or a path relative to a predefined key path. The
|
|
following values are defined:
|
|
|
|
RTL_REGISTRY_ABSOLUTE - Path is an absolute registry path
|
|
RTL_REGISTRY_SERVICES - Path is relative to \Registry\Machine\System\CurrentControlSet\Services
|
|
RTL_REGISTRY_CONTROL - Path is relative to \Registry\Machine\System\CurrentControlSet\Control
|
|
RTL_REGISTRY_WINDOWS_NT - Path is relative to \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion
|
|
RTL_REGISTRY_DEVICEMAP - Path is relative to \Registry\Machine\Hardware\DeviceMap
|
|
RTL_REGISTRY_USER - Path is relative to \Registry\User\CurrentUser
|
|
|
|
RTL_REGISTRY_OPTIONAL - Bit that specifies the key referenced by
|
|
this parameter and the Path parameter is
|
|
optional.
|
|
|
|
RTL_REGISTRY_HANDLE - Bit that specifies that the Path parameter
|
|
is actually a registry handle to use.
|
|
optional.
|
|
|
|
Path - specifies either an absolute registry path, or a path relative to the
|
|
known location specified by the RelativeTo parameter. If the the
|
|
RTL_REGISTRY_HANDLE flag is specified, then this parameter is a
|
|
registry handle to use directly.
|
|
|
|
QueryTable - specifies a table of one or more value names and subkey names
|
|
that the caller is interested. Each table entry contains a query routine
|
|
that will be called for each value name that exists in the registry.
|
|
The table is terminated when a NULL table entry is reached. A NULL
|
|
table entry is defined as a table entry with a NULL QueryRoutine
|
|
and a NULL Name field.
|
|
|
|
QueryTable entry fields:
|
|
|
|
PRTL_QUERY_REGISTRY_ROUTINE QueryRoutine - This routine is
|
|
called with the name, type, data and data length of a
|
|
registry value. If this field is NULL, then it marks the
|
|
end of the table.
|
|
|
|
ULONG Flags - These flags control how the following fields are
|
|
interpreted. The following flags are defined:
|
|
|
|
RTL_QUERY_REGISTRY_SUBKEY - says the Name field of this
|
|
table entry is another path to a registry key and all
|
|
following table entries are for that key rather than the
|
|
key specified by the Path parameter. This change in
|
|
focus lasts until the end of the table or another
|
|
RTL_QUERY_REGISTRY_SUBKEY entry is seen or
|
|
RTL_QUERY_REGISTRY_TOPKEY entry is seen. Each such
|
|
entry must specify a path that is relative to the Path
|
|
specified on the call to this function.
|
|
|
|
RTL_QUERY_REGISTRY_TOPKEY - resets the current registry key
|
|
handle to the original one specified by the RelativeTo
|
|
and Path parameters. Useful for getting back to the
|
|
original node after descending into subkeys with the
|
|
RTL_QUERY_REGISTRY_SUBKEY flag.
|
|
|
|
RTL_QUERY_REGISTRY_REQUIRED - specifies that this value is
|
|
required and if not found then STATUS_OBJECT_NAME_NOT_FOUND
|
|
is returned. For a table entry that specifies a NULL
|
|
name so that this function will enumerate all of the
|
|
value names under a key, STATUS_OBJECT_NAME_NOT_FOUND
|
|
will be returned only if there are no value keys under
|
|
the current key.
|
|
|
|
RTL_QUERY_REGISTRY_NOVALUE - specifies that even though
|
|
there is no Name field for this table entry, all the
|
|
caller wants is a call back, it does NOT want to
|
|
enumerate all the values under the current key. The
|
|
query routine is called with NULL for ValueData,
|
|
REG_NONE for ValueType and zero for ValueLength.
|
|
|
|
RTL_QUERY_REGISTRY_NOEXPAND - specifies that if the value
|
|
type of this registry value is REG_EXPAND_SZ or
|
|
REG_MULTI_SZ, then this function is NOT to do any
|
|
preprocessing of the registry values prior to calling
|
|
the query routine. Default behavior is to expand
|
|
environment variable references in REG_EXPAND_SZ
|
|
values and to enumerate the NULL terminated strings
|
|
in a REG_MULTI_SZ value and call the query routine
|
|
once for each, making it look like multiple REG_SZ
|
|
values with the same ValueName.
|
|
|
|
RTL_QUERY_REGISTRY_DIRECT QueryRoutine field ignored.
|
|
EntryContext field points to location to store value.
|
|
For null terminated strings, EntryContext points to
|
|
UNICODE_STRING structure that that describes maximum
|
|
size of buffer. If .Buffer field is NULL then a buffer
|
|
is allocated.
|
|
|
|
RTL_QUERY_REGISTRY_DELETE Used to delete value keys after
|
|
they are queried.
|
|
|
|
PWSTR Name - This field gives the name of a Value the caller
|
|
wants to query the value of. If this field is NULL, then
|
|
the QueryRoutine specified for this table entry is called
|
|
for all values associated with the current registry key.
|
|
|
|
PVOID EntryContext - This field is an arbitrary 32-bit field
|
|
that is passed uninterpreted to each QueryRoutine called.
|
|
|
|
ULONG DefaultType
|
|
PVOID DefaultData
|
|
ULONG DefaultLength If there is no value name that matches the
|
|
name given by the Name field, and the DefaultType field is
|
|
not REG_NONE, then the QueryRoutine for this table entry is
|
|
called with the contents of the following fields as if the
|
|
value had been found in the registry. If the DefaultType is
|
|
REG_SZ, REG_EXPANDSZ or REG_MULTI_SZ and the DefaultLength
|
|
is 0 then the value of DefaultLength will be computed based
|
|
on the length of unicode string pointed to by DefaultData
|
|
|
|
Context - specifies a 32-bit quantity that is passed uninterpreted to
|
|
each QueryRoutine called.
|
|
|
|
Environment - optional parameter, that if specified is the environment
|
|
used when expanding variable values in REG_EXPAND_SZ registry
|
|
values.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING KeyPath, KeyValueName;
|
|
HANDLE Key, Key1;
|
|
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
|
SIZE_T KeyValueInfoLength;
|
|
ULONG ValueIndex;
|
|
SIZE_T AllocLength;
|
|
ULONG KeyResultLength;
|
|
int retries;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
KeyValueInformation = NULL;
|
|
|
|
Status = RtlpGetRegistryHandle( RelativeTo, Path, FALSE, &Key );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
if ((RelativeTo & RTL_REGISTRY_HANDLE) == 0) {
|
|
RtlInitUnicodeString(&KeyPath, Path);
|
|
} else {
|
|
RtlInitUnicodeString(&KeyPath, NULL);
|
|
}
|
|
|
|
AllocLength = RtlpRegistryQueryInitialBuffersize;
|
|
|
|
KeyValueInformation = RtlpAllocDeallocQueryBuffer( &AllocLength, NULL, 0, &Status );
|
|
if ( KeyValueInformation == NULL ) {
|
|
if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
|
|
ZwClose( Key );
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
KeyValueInformation->DataOffset = 0;
|
|
KeyValueInfoLength = AllocLength - sizeof(UNICODE_NULL);
|
|
Key1 = Key;
|
|
while (QueryTable->QueryRoutine != NULL ||
|
|
(QueryTable->Flags & (RTL_QUERY_REGISTRY_SUBKEY | RTL_QUERY_REGISTRY_DIRECT))
|
|
) {
|
|
|
|
if ((QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) &&
|
|
(QueryTable->Name == NULL ||
|
|
(QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY) ||
|
|
QueryTable->QueryRoutine != NULL)
|
|
) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
if (QueryTable->Flags & (RTL_QUERY_REGISTRY_TOPKEY | RTL_QUERY_REGISTRY_SUBKEY)) {
|
|
if (Key1 != Key) {
|
|
NtClose( Key1 );
|
|
Key1 = Key;
|
|
}
|
|
}
|
|
|
|
if (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY) {
|
|
if (QueryTable->Name == NULL) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
} else {
|
|
RtlInitUnicodeString( &KeyPath, QueryTable->Name );
|
|
//
|
|
// Use a kernel-mode handle for the registry key to prevent
|
|
// malicious apps from hijacking it.
|
|
//
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&KeyPath,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
|
|
Key,
|
|
NULL
|
|
);
|
|
|
|
Status = ZwOpenKey( &Key1,
|
|
MAXIMUM_ALLOWED,
|
|
&ObjectAttributes
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
if (QueryTable->QueryRoutine != NULL) {
|
|
goto enumvalues;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (QueryTable->Name != NULL) {
|
|
RtlInitUnicodeString( &KeyValueName, QueryTable->Name );
|
|
retries = 0;
|
|
retryqueryvalue:
|
|
//
|
|
// A maximum of two retries is expected. If we see more we must
|
|
// have miscomputed how much is required for the query buffer.
|
|
//
|
|
if (retries++ > 4) {
|
|
//#if DBG
|
|
DbgPrint("RtlQueryRegistryValues: Miscomputed buffer size at line %d\n", __LINE__);
|
|
//#endif
|
|
break;
|
|
}
|
|
|
|
Status = ZwQueryValueKey( Key1,
|
|
&KeyValueName,
|
|
KeyValueFullInformation,
|
|
KeyValueInformation,
|
|
(ULONG) KeyValueInfoLength,
|
|
&KeyResultLength
|
|
);
|
|
//
|
|
// ZwQueryValueKey returns overflow even though the problem is that
|
|
// the specified buffer was too small, so we fix that up here so we
|
|
// can decide correctly whether to retry or not below.
|
|
//
|
|
if (Status == STATUS_BUFFER_OVERFLOW) {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
KeyValueInformation->Type = REG_NONE;
|
|
KeyValueInformation->DataLength = 0;
|
|
KeyResultLength = (ULONG)KeyValueInfoLength;
|
|
Status = RtlpCallQueryRegistryRoutine( QueryTable,
|
|
KeyValueInformation,
|
|
&KeyResultLength,
|
|
Context,
|
|
Environment
|
|
);
|
|
}
|
|
|
|
if (Status == STATUS_BUFFER_TOO_SMALL) {
|
|
//
|
|
// Try to allocate a larger buffer as this is one humongous
|
|
// value.
|
|
//
|
|
AllocLength = KeyResultLength + sizeof(PVOID) + sizeof(UNICODE_NULL);
|
|
KeyValueInformation = RtlpAllocDeallocQueryBuffer( &AllocLength,
|
|
KeyValueInformation,
|
|
AllocLength,
|
|
&Status
|
|
);
|
|
if ( KeyValueInformation == NULL) {
|
|
break;
|
|
}
|
|
KeyValueInformation->DataOffset = 0;
|
|
KeyValueInfoLength = AllocLength - sizeof(UNICODE_NULL);
|
|
goto retryqueryvalue;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// KeyResultLength holds the length of the data returned by ZwQueryKeyValue.
|
|
// If this is a MULTI_SZ value, catenate a NUL.
|
|
//
|
|
if ( KeyValueInformation->Type == REG_MULTI_SZ ) {
|
|
*(PWCHAR) ((PUCHAR)KeyValueInformation + KeyResultLength) = UNICODE_NULL;
|
|
KeyValueInformation->DataLength += sizeof(UNICODE_NULL);
|
|
}
|
|
|
|
KeyResultLength = (ULONG)KeyValueInfoLength;
|
|
Status = RtlpCallQueryRegistryRoutine( QueryTable,
|
|
KeyValueInformation,
|
|
&KeyResultLength,
|
|
Context,
|
|
Environment
|
|
);
|
|
|
|
if ( Status == STATUS_BUFFER_TOO_SMALL ) {
|
|
//
|
|
// Try to allocate a larger buffer as this is one humongous
|
|
// value.
|
|
//
|
|
AllocLength = KeyResultLength + sizeof(PVOID) + sizeof(UNICODE_NULL);
|
|
KeyValueInformation = RtlpAllocDeallocQueryBuffer( &AllocLength,
|
|
KeyValueInformation,
|
|
AllocLength,
|
|
&Status
|
|
);
|
|
if ( KeyValueInformation == NULL) {
|
|
break;
|
|
}
|
|
KeyValueInformation->DataOffset = 0;
|
|
KeyValueInfoLength = AllocLength - sizeof(UNICODE_NULL);
|
|
goto retryqueryvalue;
|
|
}
|
|
|
|
//
|
|
// If requested, delete the value key after it has been successfully queried.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status ) && QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE) {
|
|
ZwDeleteValueKey (Key1, &KeyValueName);
|
|
}
|
|
}
|
|
|
|
} else if (QueryTable->Flags & RTL_QUERY_REGISTRY_NOVALUE) {
|
|
Status = (QueryTable->QueryRoutine)( NULL,
|
|
REG_NONE,
|
|
NULL,
|
|
0,
|
|
Context,
|
|
QueryTable->EntryContext
|
|
);
|
|
} else {
|
|
|
|
enumvalues:
|
|
retries = 0;
|
|
for (ValueIndex = 0; TRUE; ValueIndex++) {
|
|
Status = ZwEnumerateValueKey( Key1,
|
|
ValueIndex,
|
|
KeyValueFullInformation,
|
|
KeyValueInformation,
|
|
(ULONG) KeyValueInfoLength,
|
|
&KeyResultLength
|
|
);
|
|
//
|
|
// ZwEnumerateValueKey returns overflow even though the problem is that
|
|
// the specified buffer was too small, so we fix that up here so we
|
|
// can decide correctly whether to retry or not below.
|
|
//
|
|
if (Status == STATUS_BUFFER_OVERFLOW) {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (Status == STATUS_NO_MORE_ENTRIES) {
|
|
if (ValueIndex == 0 && (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED)) {
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
KeyResultLength = (ULONG)KeyValueInfoLength;
|
|
Status = RtlpCallQueryRegistryRoutine( QueryTable,
|
|
KeyValueInformation,
|
|
&KeyResultLength,
|
|
Context,
|
|
Environment
|
|
);
|
|
}
|
|
|
|
if (Status == STATUS_BUFFER_TOO_SMALL) {
|
|
//
|
|
// Allocate a larger buffer and try again.
|
|
//
|
|
AllocLength = KeyResultLength + sizeof(PVOID) + sizeof(UNICODE_NULL);
|
|
KeyValueInformation = RtlpAllocDeallocQueryBuffer( &AllocLength,
|
|
KeyValueInformation,
|
|
AllocLength,
|
|
&Status
|
|
);
|
|
if (KeyValueInformation == NULL) {
|
|
break;
|
|
}
|
|
KeyValueInformation->DataOffset = 0;
|
|
KeyValueInfoLength = AllocLength - sizeof(UNICODE_NULL);
|
|
ValueIndex -= 1;
|
|
|
|
//
|
|
// A maximum of two retries is expected per loop iteration.
|
|
// If we see more we must have miscomputed
|
|
// how much is required for the query buffer.
|
|
//
|
|
if (retries++ <= 4) {
|
|
continue;
|
|
}
|
|
//#if DBG
|
|
DbgPrint("RtlQueryRegistryValues: Miscomputed buffer size at line %d\n", __LINE__);
|
|
//#endif
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
break;
|
|
}
|
|
|
|
retries = 0;
|
|
|
|
//
|
|
// If requested, delete the value key after it has been successfully queried.
|
|
// After deletion the current ValueIndex is for the next sub-key, so adjust it.
|
|
// KeyValueInformation->NameLength should fit in a USHORT, but we don't check since
|
|
// it only harms our caller.
|
|
//
|
|
|
|
if (QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE) {
|
|
KeyValueName.Buffer = KeyValueInformation->Name;
|
|
KeyValueName.Length = (USHORT)KeyValueInformation->NameLength;
|
|
KeyValueName.MaximumLength = (USHORT)KeyValueInformation->NameLength;
|
|
Status = ZwDeleteValueKey( Key1,
|
|
&KeyValueName
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
ValueIndex -= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
break;
|
|
}
|
|
|
|
QueryTable++;
|
|
}
|
|
|
|
if (Key != NULL && !(RelativeTo & RTL_REGISTRY_HANDLE)) {
|
|
ZwClose( Key );
|
|
}
|
|
|
|
if (Key1 != NULL && Key1 != Key) {
|
|
ZwClose( Key1 );
|
|
}
|
|
|
|
//
|
|
// Free any query buffer we allocated.
|
|
//
|
|
(void) RtlpAllocDeallocQueryBuffer( NULL, KeyValueInformation, AllocLength, NULL );
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlWriteRegistryValue(
|
|
IN ULONG RelativeTo,
|
|
IN PCWSTR Path,
|
|
IN PCWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING KeyValueName;
|
|
HANDLE Key;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
Status = RtlpGetRegistryHandle( RelativeTo, Path, TRUE, &Key );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
RtlInitUnicodeString( &KeyValueName, ValueName );
|
|
Status = ZwSetValueKey( Key,
|
|
&KeyValueName,
|
|
0,
|
|
ValueType,
|
|
ValueData,
|
|
ValueLength
|
|
);
|
|
if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
|
|
ZwClose( Key );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlCheckRegistryKey(
|
|
IN ULONG RelativeTo,
|
|
IN PWSTR Path
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Key;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
Status = RtlpGetRegistryHandle( RelativeTo, Path, FALSE, &Key );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
ZwClose( Key );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlCreateRegistryKey(
|
|
IN ULONG RelativeTo,
|
|
IN PWSTR Path
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Key;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
Status = RtlpGetRegistryHandle( RelativeTo, Path, TRUE, &Key );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
|
|
ZwClose( Key );
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlDeleteRegistryValue(
|
|
IN ULONG RelativeTo,
|
|
IN PCWSTR Path,
|
|
IN PCWSTR ValueName
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING KeyValueName;
|
|
HANDLE Key;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
Status = RtlpGetRegistryHandle( RelativeTo, Path, TRUE, &Key );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
RtlInitUnicodeString( &KeyValueName, ValueName );
|
|
Status = ZwDeleteValueKey( Key, &KeyValueName );
|
|
|
|
if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
|
|
ZwClose( Key );
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlExpandEnvironmentStrings_U(
|
|
IN PVOID Environment OPTIONAL,
|
|
IN PCUNICODE_STRING Source,
|
|
OUT PUNICODE_STRING Destination,
|
|
OUT PULONG ReturnedLength OPTIONAL
|
|
)
|
|
{
|
|
NTSTATUS Status, Status1;
|
|
PWCHAR Src, Src1, Dst;
|
|
UNICODE_STRING VariableName, VariableValue;
|
|
ULONG SrcLength, DstLength, VarLength, RequiredLength;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
Src = Source->Buffer;
|
|
SrcLength = Source->Length;
|
|
Dst = Destination->Buffer;
|
|
DstLength = Destination->MaximumLength;
|
|
Status = STATUS_SUCCESS;
|
|
RequiredLength = 0;
|
|
while (SrcLength >= sizeof(WCHAR)) {
|
|
if (*Src == L'%') {
|
|
Src1 = Src + 1;
|
|
VarLength = 0;
|
|
VariableName.Length = 0;
|
|
VariableName.Buffer = Src1;
|
|
|
|
while (VarLength < (SrcLength - sizeof(WCHAR))) {
|
|
if (*Src1 == L'%') {
|
|
if (VarLength) {
|
|
VariableName.Length = (USHORT)VarLength;
|
|
VariableName.MaximumLength = (USHORT)VarLength;
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
Src1++;
|
|
VarLength += sizeof(WCHAR);
|
|
}
|
|
|
|
if (VariableName.Length) {
|
|
VariableValue.Buffer = Dst;
|
|
VariableValue.Length = 0;
|
|
VariableValue.MaximumLength = (USHORT)DstLength;
|
|
Status1 = RtlQueryEnvironmentVariable_U( Environment,
|
|
&VariableName,
|
|
&VariableValue
|
|
);
|
|
if (NT_SUCCESS( Status1 ) || Status1 == STATUS_BUFFER_TOO_SMALL) {
|
|
RequiredLength += VariableValue.Length;
|
|
Src = Src1 + 1;
|
|
SrcLength -= (VarLength + 2*sizeof(WCHAR));
|
|
|
|
if (NT_SUCCESS( Status1 )) {
|
|
DstLength -= VariableValue.Length;
|
|
Dst += VariableValue.Length / sizeof(WCHAR);
|
|
|
|
} else {
|
|
Status = Status1;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
if (DstLength > sizeof(WCHAR)) {
|
|
DstLength -= sizeof(WCHAR);
|
|
*Dst++ = *Src;
|
|
|
|
} else {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
|
|
RequiredLength += sizeof(WCHAR);
|
|
SrcLength -= sizeof(WCHAR);
|
|
Src++;
|
|
}
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
if (DstLength) {
|
|
DstLength -= sizeof(WCHAR);
|
|
*Dst = UNICODE_NULL;
|
|
|
|
} else {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
|
|
RequiredLength += sizeof(WCHAR);
|
|
|
|
if (ARGUMENT_PRESENT( ReturnedLength )) {
|
|
*ReturnedLength = RequiredLength;
|
|
}
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
Destination->Length = (USHORT)(RequiredLength - sizeof(WCHAR));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
ULONG
|
|
RtlGetNtGlobalFlags( VOID )
|
|
{
|
|
#ifdef NTOS_KERNEL_RUNTIME
|
|
return NtGlobalFlag;
|
|
#else
|
|
return NtCurrentPeb()->NtGlobalFlag;
|
|
#endif
|
|
}
|
|
|
|
|
|
//
|
|
// Maximum size of TOKEN_USER information.
|
|
//
|
|
|
|
#define SIZE_OF_TOKEN_INFORMATION \
|
|
sizeof( TOKEN_USER ) \
|
|
+ sizeof( SID ) \
|
|
+ sizeof( ULONG ) * SID_MAX_SUB_AUTHORITIES
|
|
|
|
|
|
NTSTATUS
|
|
RtlFormatCurrentUserKeyPath(
|
|
OUT PUNICODE_STRING CurrentUserKeyPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the supplied buffer with a string representation
|
|
of the current user's SID.
|
|
|
|
Arguments:
|
|
|
|
CurrentUserKeyPath - Returns a string that represents the current
|
|
user's root key in the Registry. Caller must call
|
|
RtlFreeUnicodeString to free the buffer when done with it.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Returns STATUS_SUCCESS if the user string was
|
|
succesfully initialized.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE TokenHandle;
|
|
UCHAR TokenInformation[ SIZE_OF_TOKEN_INFORMATION ];
|
|
ULONG ReturnLength;
|
|
ULONG SidStringLength ;
|
|
UNICODE_STRING SidString ;
|
|
NTSTATUS Status;
|
|
|
|
#ifdef NTOS_KERNEL_RUNTIME
|
|
|
|
//
|
|
// Inside the kernel we can tell rapidly if we are impersonating.
|
|
//
|
|
Status = STATUS_NO_TOKEN;
|
|
if (PS_IS_THREAD_IMPERSONATING (PsGetCurrentThread ())) {
|
|
#else
|
|
{
|
|
#endif
|
|
|
|
Status = ZwOpenThreadTokenEx (NtCurrentThread(),
|
|
TOKEN_READ,
|
|
TRUE,
|
|
OBJ_KERNEL_HANDLE,
|
|
&TokenHandle);
|
|
|
|
if ( !NT_SUCCESS( Status ) && ( Status != STATUS_NO_TOKEN ) ) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
Status = ZwOpenProcessTokenEx (NtCurrentProcess(),
|
|
TOKEN_READ,
|
|
OBJ_KERNEL_HANDLE,
|
|
&TokenHandle);
|
|
if ( !NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
Status = ZwQueryInformationToken( TokenHandle,
|
|
TokenUser,
|
|
TokenInformation,
|
|
sizeof( TokenInformation ),
|
|
&ReturnLength
|
|
);
|
|
|
|
ZwClose( TokenHandle );
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
Status = RtlLengthSidAsUnicodeString(
|
|
((PTOKEN_USER)TokenInformation)->User.Sid,
|
|
&SidStringLength
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
return Status ;
|
|
}
|
|
|
|
CurrentUserKeyPath->Length = 0;
|
|
CurrentUserKeyPath->MaximumLength = (USHORT)(SidStringLength +
|
|
sizeof( L"\\REGISTRY\\USER\\" ) +
|
|
sizeof( UNICODE_NULL ));
|
|
CurrentUserKeyPath->Buffer = (RtlAllocateStringRoutine)( CurrentUserKeyPath->MaximumLength );
|
|
if (CurrentUserKeyPath->Buffer == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Copy "\REGISTRY\USER" to the current user string.
|
|
//
|
|
|
|
RtlAppendUnicodeToString( CurrentUserKeyPath, L"\\REGISTRY\\USER\\" );
|
|
|
|
SidString.MaximumLength = (USHORT)SidStringLength ;
|
|
SidString.Length = 0 ;
|
|
SidString.Buffer = CurrentUserKeyPath->Buffer +
|
|
(CurrentUserKeyPath->Length / sizeof(WCHAR) );
|
|
|
|
Status = RtlConvertSidToUnicodeString( &SidString,
|
|
((PTOKEN_USER)TokenInformation)->User.Sid,
|
|
FALSE
|
|
);
|
|
if ( !NT_SUCCESS( Status )) {
|
|
RtlFreeUnicodeString( CurrentUserKeyPath );
|
|
|
|
} else {
|
|
CurrentUserKeyPath->Length += SidString.Length ;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlOpenCurrentUser(
|
|
IN ULONG DesiredAccess,
|
|
OUT PHANDLE CurrentUserKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts to open the the HKEY_CURRENT_USER predefined handle.
|
|
|
|
Arguments:
|
|
|
|
DesiredAccess - Specifies the access to open the key for.
|
|
|
|
CurrentUserKey - Returns a handle to the key \REGISTRY\USER\*.
|
|
|
|
Return Value:
|
|
|
|
Returns ERROR_SUCCESS (0) for success; error-code for failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING CurrentUserKeyPath;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
NTSTATUS Status;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
//
|
|
// Format the registry path for the current user.
|
|
//
|
|
|
|
Status = RtlFormatCurrentUserKeyPath( &CurrentUserKeyPath );
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
InitializeObjectAttributes( &Obja,
|
|
&CurrentUserKeyPath,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE|OBJ_FORCE_ACCESS_CHECK,
|
|
NULL,
|
|
NULL
|
|
);
|
|
Status = ZwOpenKey( CurrentUserKey,
|
|
DesiredAccess,
|
|
&Obja
|
|
);
|
|
RtlFreeUnicodeString( &CurrentUserKeyPath );
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
//
|
|
// Opening \REGISTRY\USER\<SID> failed, try \REGISTRY\USER\.DEFAULT
|
|
//
|
|
RtlInitUnicodeString( &CurrentUserKeyPath, RtlpRegistryPaths[ RTL_REGISTRY_USER ] );
|
|
InitializeObjectAttributes( &Obja,
|
|
&CurrentUserKeyPath,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE|OBJ_FORCE_ACCESS_CHECK,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = ZwOpenKey( CurrentUserKey,
|
|
DesiredAccess,
|
|
&Obja
|
|
);
|
|
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlpGetTimeZoneInfoHandle(
|
|
IN BOOLEAN WriteAccess,
|
|
OUT PHANDLE Key
|
|
)
|
|
{
|
|
return RtlpGetRegistryHandle( RTL_REGISTRY_CONTROL, L"TimeZoneInformation", WriteAccess, Key );
|
|
}
|
|
|
|
|
|
|
|
extern const WCHAR szBias[];
|
|
extern const WCHAR szStandardName[];
|
|
extern const WCHAR szStandardBias[];
|
|
extern const WCHAR szStandardStart[];
|
|
extern const WCHAR szDaylightName[];
|
|
extern const WCHAR szDaylightBias[];
|
|
extern const WCHAR szDaylightStart[];
|
|
|
|
NTSTATUS
|
|
RtlQueryTimeZoneInformation(
|
|
OUT PRTL_TIME_ZONE_INFORMATION TimeZoneInformation
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Key;
|
|
UNICODE_STRING StandardName, DaylightName;
|
|
RTL_QUERY_REGISTRY_TABLE RegistryConfigurationTable[ 8 ];
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
Status = RtlpGetTimeZoneInfoHandle( FALSE, &Key );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
RtlZeroMemory( TimeZoneInformation, sizeof( *TimeZoneInformation ) );
|
|
RtlZeroMemory( RegistryConfigurationTable, sizeof( RegistryConfigurationTable ) );
|
|
|
|
RegistryConfigurationTable[ 0 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
RegistryConfigurationTable[ 0 ].Name = (PWSTR)szBias;
|
|
RegistryConfigurationTable[ 0 ].EntryContext = &TimeZoneInformation->Bias;
|
|
|
|
|
|
StandardName.Buffer = TimeZoneInformation->StandardName;
|
|
StandardName.Length = 0;
|
|
StandardName.MaximumLength = sizeof( TimeZoneInformation->StandardName );
|
|
RegistryConfigurationTable[ 1 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
RegistryConfigurationTable[ 1 ].Name = (PWSTR)szStandardName;
|
|
RegistryConfigurationTable[ 1 ].EntryContext = &StandardName;
|
|
|
|
RegistryConfigurationTable[ 2 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
RegistryConfigurationTable[ 2 ].Name = (PWSTR)szStandardBias;
|
|
RegistryConfigurationTable[ 2 ].EntryContext = &TimeZoneInformation->StandardBias;
|
|
|
|
RegistryConfigurationTable[ 3 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
RegistryConfigurationTable[ 3 ].Name = (PWSTR)szStandardStart;
|
|
RegistryConfigurationTable[ 3 ].EntryContext = &TimeZoneInformation->StandardStart;
|
|
*(PLONG)(RegistryConfigurationTable[ 3 ].EntryContext) = -(LONG)sizeof( TIME_FIELDS );
|
|
|
|
DaylightName.Buffer = TimeZoneInformation->DaylightName;
|
|
DaylightName.Length = 0;
|
|
DaylightName.MaximumLength = sizeof( TimeZoneInformation->DaylightName );
|
|
RegistryConfigurationTable[ 4 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
RegistryConfigurationTable[ 4 ].Name = (PWSTR)szDaylightName;
|
|
RegistryConfigurationTable[ 4 ].EntryContext = &DaylightName;
|
|
|
|
RegistryConfigurationTable[ 5 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
RegistryConfigurationTable[ 5 ].Name = (PWSTR)szDaylightBias;
|
|
RegistryConfigurationTable[ 5 ].EntryContext = &TimeZoneInformation->DaylightBias;
|
|
|
|
RegistryConfigurationTable[ 6 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
RegistryConfigurationTable[ 6 ].Name = (PWSTR)szDaylightStart;
|
|
RegistryConfigurationTable[ 6 ].EntryContext = &TimeZoneInformation->DaylightStart;
|
|
*(PLONG)(RegistryConfigurationTable[ 6 ].EntryContext) = -(LONG)sizeof( TIME_FIELDS );
|
|
|
|
Status = RtlQueryRegistryValues( RTL_REGISTRY_HANDLE,
|
|
(PWSTR)Key,
|
|
RegistryConfigurationTable,
|
|
NULL,
|
|
NULL
|
|
);
|
|
ZwClose( Key );
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlSetTimeZoneInformation(
|
|
IN PRTL_TIME_ZONE_INFORMATION TimeZoneInformation
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Key;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
Status = RtlpGetTimeZoneInfoHandle( TRUE, &Key );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
|
|
(PWSTR)Key,
|
|
szBias,
|
|
REG_DWORD,
|
|
&TimeZoneInformation->Bias,
|
|
sizeof( TimeZoneInformation->Bias )
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
|
|
(PWSTR)Key,
|
|
szStandardName,
|
|
REG_SZ,
|
|
TimeZoneInformation->StandardName,
|
|
(wcslen( TimeZoneInformation->StandardName ) + 1) * sizeof( WCHAR )
|
|
);
|
|
}
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
|
|
(PWSTR)Key,
|
|
szStandardBias,
|
|
REG_DWORD,
|
|
&TimeZoneInformation->StandardBias,
|
|
sizeof( TimeZoneInformation->StandardBias )
|
|
);
|
|
}
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
|
|
(PWSTR)Key,
|
|
szStandardStart,
|
|
REG_BINARY,
|
|
&TimeZoneInformation->StandardStart,
|
|
sizeof( TimeZoneInformation->StandardStart )
|
|
);
|
|
}
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
|
|
(PWSTR)Key,
|
|
szDaylightName,
|
|
REG_SZ,
|
|
TimeZoneInformation->DaylightName,
|
|
(wcslen( TimeZoneInformation->DaylightName ) + 1) * sizeof( WCHAR )
|
|
);
|
|
}
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
|
|
(PWSTR)Key,
|
|
szDaylightBias,
|
|
REG_DWORD,
|
|
&TimeZoneInformation->DaylightBias,
|
|
sizeof( TimeZoneInformation->DaylightBias )
|
|
);
|
|
}
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
|
|
(PWSTR)Key,
|
|
szDaylightStart,
|
|
REG_BINARY,
|
|
&TimeZoneInformation->DaylightStart,
|
|
sizeof( TimeZoneInformation->DaylightStart )
|
|
);
|
|
}
|
|
|
|
ZwClose( Key );
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlSetActiveTimeBias(
|
|
IN LONG ActiveBias
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Key;
|
|
RTL_QUERY_REGISTRY_TABLE RegistryConfigurationTable[ 2 ];
|
|
LONG CurrentActiveBias;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
Status = RtlpGetTimeZoneInfoHandle( TRUE, &Key );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
RtlZeroMemory( RegistryConfigurationTable, sizeof( RegistryConfigurationTable ) );
|
|
RegistryConfigurationTable[ 0 ].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
|
|
RegistryConfigurationTable[ 0 ].Name = L"ActiveTimeBias";
|
|
RegistryConfigurationTable[ 0 ].EntryContext = &CurrentActiveBias;
|
|
|
|
Status = RtlQueryRegistryValues( RTL_REGISTRY_HANDLE,
|
|
(PWSTR)Key,
|
|
RegistryConfigurationTable,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) || CurrentActiveBias != ActiveBias ) {
|
|
|
|
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
|
|
(PWSTR)Key,
|
|
L"ActiveTimeBias",
|
|
REG_DWORD,
|
|
&ActiveBias,
|
|
sizeof( ActiveBias )
|
|
);
|
|
}
|
|
|
|
ZwClose( Key );
|
|
return Status;
|
|
}
|
|
|
|
#if defined(ALLOC_DATA_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
|
|
#pragma const_seg()
|
|
#endif
|
|
|