/*++ 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 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. // #define MAX_USTRING ( sizeof(WCHAR) * (MAXUSHORT/sizeof(WCHAR)) ) // // 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 PUNICODE_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; 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\ 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; }