1554 lines
52 KiB
C
1554 lines
52 KiB
C
/*++
|
|
|
|
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 PWSTR 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 ULONG KeyValueInfoLength,
|
|
IN PVOID Context,
|
|
IN PVOID Environment 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,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,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 PWSTR RtlpRegistryPaths[ RTL_REGISTRY_MAXIMUM ];
|
|
|
|
NTSTATUS
|
|
RtlpGetRegistryHandle(
|
|
IN ULONG RelativeTo,
|
|
IN PWSTR 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;
|
|
}
|
|
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&KeyPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
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
|
|
);
|
|
}
|
|
#if DBG
|
|
if (!NT_SUCCESS( Status ) && !OptionalPath) {
|
|
DbgPrint( "RTL: %wZ key not found - Status == %x \n", &KeyPath, Status );
|
|
}
|
|
#endif // DBG
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
DestinationString = (PUNICODE_STRING)Destination;
|
|
if (DestinationString->Buffer == NULL) {
|
|
DestinationString->Buffer = RtlAllocateStringRoutine( ValueLength );
|
|
DestinationString->MaximumLength = (USHORT)ValueLength;
|
|
}
|
|
else
|
|
if (ValueLength > DestinationString->MaximumLength) {
|
|
return( STATUS_BUFFER_TOO_SMALL );
|
|
}
|
|
|
|
RtlMoveMemory( DestinationString->Buffer, ValueData, ValueLength );
|
|
DestinationString->Length = (USHORT)(ValueLength - sizeof( UNICODE_NULL ));
|
|
}
|
|
else
|
|
if (ValueLength <= sizeof( ULONG )) {
|
|
RtlMoveMemory( 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 );
|
|
}
|
|
else {
|
|
RtlMoveMemory( DestinationLength, ValueData, ValueLength );
|
|
}
|
|
}
|
|
else
|
|
if (*DestinationLength < (ValueLength + sizeof( ValueLength ) + sizeof( ValueType ))
|
|
) {
|
|
return( STATUS_BUFFER_TOO_SMALL );
|
|
}
|
|
else {
|
|
*DestinationLength++ = ValueLength;
|
|
*DestinationLength++ = ValueType;
|
|
RtlMoveMemory( DestinationLength, ValueData, ValueLength );
|
|
}
|
|
}
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
NTSTATUS
|
|
RtlpCallQueryRegistryRoutine(
|
|
IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
|
|
IN PKEY_VALUE_FULL_INFORMATION KeyValueInformation,
|
|
IN ULONG KeyValueInfoLength,
|
|
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.
|
|
|
|
Arguments:
|
|
|
|
QueryTable - specifies the current query table entry.
|
|
|
|
KeyValueInformation - points to a buffer that contains the information
|
|
about the current registry value.
|
|
|
|
KeyValueInfoLength - specifies the maximum length of the buffer pointed
|
|
to by the KeyValueInformaiton parameter. 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.
|
|
|
|
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;
|
|
ULONG FreeMemSize;
|
|
|
|
//
|
|
// Initially assume the entire KeyValueInformation buffer is unused.
|
|
//
|
|
|
|
FreeMem = (PCHAR)KeyValueInformation;
|
|
FreeMemSize = KeyValueInfoLength;
|
|
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_SUCCESS );
|
|
}
|
|
else {
|
|
UNICODE_STRING KeyValueName;
|
|
|
|
if (QueryTable->Name) {
|
|
RtlInitUnicodeString( &KeyValueName, QueryTable->Name );
|
|
}
|
|
else
|
|
if (KeyValueInformation->Type != REG_NONE) {
|
|
KeyValueName.Buffer = KeyValueInformation->Name;
|
|
KeyValueName.Length = (USHORT)KeyValueInformation->NameLength;
|
|
KeyValueName.MaximumLength = KeyValueName.Length;
|
|
}
|
|
else {
|
|
RtlInitUnicodeString( &KeyValueName, L"*** Unknown ***" );
|
|
}
|
|
return( STATUS_OBJECT_NAME_NOT_FOUND );
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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 = (PCHAR)s - (PCHAR)ValueData;
|
|
}
|
|
else
|
|
if (ValueType == REG_MULTI_SZ) {
|
|
while (*s != UNICODE_NULL) {
|
|
while (*s++ != UNICODE_NULL) {
|
|
}
|
|
}
|
|
|
|
ValueLength = ((PCHAR)s - (PCHAR)ValueData) + sizeof( UNICODE_NULL );
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT)) {
|
|
//
|
|
// There is a registry value. Calculate a pointer to the
|
|
// free memory at the end of the value information buffer,
|
|
// and its size.
|
|
//
|
|
|
|
FreeMem += KeyValueInformation->DataOffset +
|
|
KeyValueInformation->DataLength;
|
|
FreeMem = (PCHAR)(((ULONG)FreeMem + 7) & ~7);
|
|
FreeMemSize -= (FreeMem - (PCHAR)KeyValueInformation);
|
|
|
|
//
|
|
// See if there is room in the free memory area for a null
|
|
// terminated copy of the value name string. If not return
|
|
// and error.
|
|
//
|
|
|
|
if (FreeMemSize <= KeyValueInformation->NameLength) {
|
|
return( STATUS_NO_MEMORY );
|
|
}
|
|
|
|
//
|
|
// There is room, so copy the string, and null terminate it.
|
|
//
|
|
|
|
ValueName = (PWSTR)FreeMem;
|
|
RtlMoveMemory( 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 += KeyValueInformation->NameLength + sizeof( UNICODE_NULL );
|
|
FreeMem = (PCHAR)(((ULONG)FreeMem + 7) & ~7);
|
|
FreeMemSize -= (FreeMem - (PCHAR)KeyValueInformation);
|
|
}
|
|
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) - 2;
|
|
s = (PWSTR)ValueData;
|
|
while (s < ValueEnd) {
|
|
while (*s++ != UNICODE_NULL) {
|
|
}
|
|
|
|
ValueLength = (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
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
#if DBG
|
|
DbgPrint( "RTL: QueryRoutine( %ws ) failed - Status == %lx\n", ValueName, Status );
|
|
#endif // DBG
|
|
break;
|
|
}
|
|
|
|
ValueData = (PVOID)s;
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
else
|
|
if (ValueType == REG_EXPAND_SZ) {
|
|
//
|
|
// 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;
|
|
|
|
Source.Buffer = (PWSTR)ValueData;
|
|
Source.MaximumLength = (USHORT)ValueLength;
|
|
Source.Length = (USHORT)(Source.MaximumLength - sizeof(WCHAR));
|
|
Destination.Buffer = (PWSTR)FreeMem;
|
|
Destination.Length = 0;
|
|
Destination.MaximumLength = (USHORT)FreeMemSize;
|
|
|
|
Status = RtlExpandEnvironmentStrings_U( Environment,
|
|
&Source,
|
|
&Destination,
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
#if DBG
|
|
DbgPrint( "RTL: Expand variables for %wZ failed - Status == %lx\n", &Source, Status );
|
|
#endif // DBG
|
|
return( Status );
|
|
}
|
|
|
|
ValueData = Destination.Buffer;
|
|
ValueLength = Destination.Length + sizeof( UNICODE_NULL );
|
|
ValueType = REG_SZ;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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
|
|
);
|
|
|
|
}
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS( Status )) {
|
|
DbgPrint( "RTL: QueryRoutine( %ws ) failed - Status == %lx\n", ValueName, Status );
|
|
}
|
|
#endif
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlQueryRegistryValues(
|
|
IN ULONG RelativeTo,
|
|
IN PWSTR 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.
|
|
|
|
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;
|
|
ULONG KeyValueInfoLength;
|
|
ULONG ValueIndex;
|
|
ULONG ResultLength;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
Status = RtlpGetRegistryHandle( RelativeTo, Path, FALSE, &Key );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
KeyValueInformation = NULL;
|
|
KeyValueInfoLength = 0x10000;
|
|
Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
|
|
(PVOID *)&KeyValueInformation,
|
|
0,
|
|
&KeyValueInfoLength,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
|
|
ZwClose( Key );
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
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 );
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&KeyPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
Key,
|
|
NULL
|
|
);
|
|
Status = ZwOpenKey( &Key1,
|
|
MAXIMUM_ALLOWED,
|
|
&ObjectAttributes
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
if (QueryTable->QueryRoutine != NULL) {
|
|
goto enumvalues;
|
|
}
|
|
}
|
|
#if DBG
|
|
else
|
|
if (!(QueryTable->Flags & RTL_REGISTRY_OPTIONAL)) {
|
|
DbgPrint( "RTL: %wZ sub key not found.\n", &KeyPath );
|
|
}
|
|
#endif // DBG
|
|
}
|
|
}
|
|
else
|
|
if (QueryTable->Name != NULL) {
|
|
RtlInitUnicodeString( &KeyValueName, QueryTable->Name );
|
|
retryqueryvalue:
|
|
Status = ZwQueryValueKey( Key1,
|
|
&KeyValueName,
|
|
KeyValueFullInformation,
|
|
KeyValueInformation,
|
|
KeyValueInfoLength,
|
|
&ResultLength
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
KeyValueInformation->Type = REG_NONE;
|
|
KeyValueInformation->DataLength = 0;
|
|
Status = RtlpCallQueryRegistryRoutine( QueryTable,
|
|
KeyValueInformation,
|
|
KeyValueInfoLength,
|
|
Context,
|
|
Environment
|
|
);
|
|
}
|
|
|
|
else if (Status == STATUS_BUFFER_OVERFLOW) {
|
|
PVOID NewBuffer = NULL;
|
|
|
|
//
|
|
// Try to allocate a larger buffer as this is one humongous
|
|
// value.
|
|
//
|
|
Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
|
|
&NewBuffer,
|
|
0,
|
|
&ResultLength,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE );
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
ZwFreeVirtualMemory( NtCurrentProcess(),
|
|
(PVOID *)&KeyValueInformation,
|
|
&KeyValueInfoLength,
|
|
MEM_RELEASE );
|
|
KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)NewBuffer;
|
|
KeyValueInfoLength = ResultLength;
|
|
goto retryqueryvalue;
|
|
}
|
|
}
|
|
else {
|
|
if ( KeyValueInformation->Type == REG_MULTI_SZ ) {
|
|
try {
|
|
RtlZeroMemory((PUCHAR)KeyValueInformation+ResultLength,2);
|
|
KeyValueInformation->DataLength += 2;
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
;
|
|
}
|
|
}
|
|
Status = RtlpCallQueryRegistryRoutine( QueryTable,
|
|
KeyValueInformation,
|
|
KeyValueInfoLength,
|
|
Context,
|
|
Environment
|
|
);
|
|
//
|
|
// 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:
|
|
for (ValueIndex = 0; TRUE; ValueIndex++) {
|
|
Status = ZwEnumerateValueKey( Key1,
|
|
ValueIndex,
|
|
KeyValueFullInformation,
|
|
KeyValueInformation,
|
|
KeyValueInfoLength,
|
|
&ResultLength
|
|
);
|
|
if (Status == STATUS_NO_MORE_ENTRIES) {
|
|
if (ValueIndex != 0 || !(QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED)) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
#if DBG
|
|
DbgPrint( "RTL: No values found under %wZ key.\n", &KeyPath );
|
|
#endif // DBG
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else
|
|
if (!NT_SUCCESS( Status )) {
|
|
break;
|
|
}
|
|
|
|
Status = RtlpCallQueryRegistryRoutine( QueryTable,
|
|
KeyValueInformation,
|
|
KeyValueInfoLength,
|
|
Context,
|
|
Environment
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If requested, delete the value key after it has been successfully queried.
|
|
//
|
|
|
|
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 );
|
|
}
|
|
ZwFreeVirtualMemory( NtCurrentProcess(),
|
|
(PVOID *)&KeyValueInformation,
|
|
&KeyValueInfoLength,
|
|
MEM_RELEASE
|
|
);
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlWriteRegistryValue(
|
|
IN ULONG RelativeTo,
|
|
IN PWSTR Path,
|
|
IN PWSTR 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;
|
|
}
|
|
|
|
ZwClose( Key );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlDeleteRegistryValue(
|
|
IN ULONG RelativeTo,
|
|
IN PWSTR Path,
|
|
IN PWSTR 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 );
|
|
|
|
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) {
|
|
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;
|
|
}
|
|
else {
|
|
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) {
|
|
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 = L'\0';
|
|
}
|
|
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 )
|
|
{
|
|
return( NtGlobalFlag );
|
|
}
|
|
|
|
|
|
//
|
|
// 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.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING UserString;
|
|
HANDLE TokenHandle;
|
|
UCHAR TokenInformation[ SIZE_OF_TOKEN_INFORMATION ];
|
|
ULONG ReturnLength;
|
|
NTSTATUS Status;
|
|
|
|
Status = ZwOpenThreadToken( NtCurrentThread(),
|
|
TOKEN_READ,
|
|
TRUE,
|
|
&TokenHandle
|
|
);
|
|
|
|
if( !NT_SUCCESS( Status ) && ( Status != STATUS_NO_TOKEN ) ) {
|
|
return( Status );
|
|
}
|
|
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
|
|
Status = ZwOpenProcessToken( NtCurrentProcess(),
|
|
TOKEN_READ,
|
|
&TokenHandle
|
|
);
|
|
if( !NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
Status = ZwQueryInformationToken( TokenHandle,
|
|
TokenUser,
|
|
TokenInformation,
|
|
sizeof( TokenInformation ),
|
|
&ReturnLength
|
|
);
|
|
if( !NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
ZwClose( TokenHandle );
|
|
Status = RtlConvertSidToUnicodeString( &UserString,
|
|
((PTOKEN_USER)TokenInformation)->User.Sid,
|
|
TRUE
|
|
);
|
|
if( !NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
CurrentUserKeyPath->Length = 0;
|
|
CurrentUserKeyPath->MaximumLength = UserString.Length +
|
|
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\\" );
|
|
|
|
//
|
|
// Append the user's <SID> to the current user string.
|
|
//
|
|
|
|
Status = RtlAppendUnicodeStringToString( CurrentUserKeyPath,
|
|
&UserString
|
|
);
|
|
RtlFreeUnicodeString( &UserString );
|
|
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 )) {
|
|
goto trydefault;
|
|
}
|
|
|
|
InitializeObjectAttributes( &Obja,
|
|
&CurrentUserKeyPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
Status = ZwOpenKey( CurrentUserKey,
|
|
DesiredAccess,
|
|
&Obja
|
|
);
|
|
RtlFreeUnicodeString( &CurrentUserKeyPath );
|
|
|
|
if( !NT_SUCCESS( Status )) {
|
|
trydefault:
|
|
//
|
|
// Opening \REGISTRY\USER\<SID> failed, try \REGISTRY\USER\.DEFAULT
|
|
//
|
|
RtlInitUnicodeString( &CurrentUserKeyPath, RtlpRegistryPaths[ RTL_REGISTRY_USER ] );
|
|
InitializeObjectAttributes( &Obja,
|
|
&CurrentUserKeyPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
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 WCHAR szBias[];
|
|
extern WCHAR szStandardName[];
|
|
extern WCHAR szStandardBias[];
|
|
extern WCHAR szStandardStart[];
|
|
extern WCHAR szDaylightName[];
|
|
extern WCHAR szDaylightBias[];
|
|
extern 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 = 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 = szStandardName;
|
|
RegistryConfigurationTable[ 1 ].EntryContext = &StandardName;
|
|
|
|
RegistryConfigurationTable[ 2 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
RegistryConfigurationTable[ 2 ].Name = szStandardBias;
|
|
RegistryConfigurationTable[ 2 ].EntryContext = &TimeZoneInformation->StandardBias;
|
|
|
|
RegistryConfigurationTable[ 3 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
RegistryConfigurationTable[ 3 ].Name = 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 = szDaylightName;
|
|
RegistryConfigurationTable[ 4 ].EntryContext = &DaylightName;
|
|
|
|
RegistryConfigurationTable[ 5 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
RegistryConfigurationTable[ 5 ].Name = szDaylightBias;
|
|
RegistryConfigurationTable[ 5 ].EntryContext = &TimeZoneInformation->DaylightBias;
|
|
|
|
RegistryConfigurationTable[ 6 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
RegistryConfigurationTable[ 6 ].Name = 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 ) {
|
|
;
|
|
}
|
|
else {
|
|
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
|
|
(PWSTR)Key,
|
|
L"ActiveTimeBias",
|
|
REG_DWORD,
|
|
&ActiveBias,
|
|
sizeof( ActiveBias )
|
|
);
|
|
}
|
|
ZwClose( Key );
|
|
return Status;
|
|
}
|