Содержание материала

NtEnumerateKey

 

Благодаря структуре реестра, мы не можем запросить список всех ключей

в какой-либо его части. Мы можем получить информацию только об одном ключе,

указанном его индексом. Используется функция NtEnumerateKey.

 

NTSTATUS NtEnumerateKey(

IN HANDLE KeyHandle,

IN ULONG Index,

IN KEY_INFORMATION_CLASS KeyInformationClass,

OUT PVOID KeyInformation,

IN ULONG KeyInformationLength,

OUT PULONG ResultLength

);

 

 KeyHandle - это дескриптор ключа, в котором мы хотим получить

информацию о подключе, указанном параметром Index. Тип полученной информации

определяется полем KeyInformationClass. Данные записываются в буфер

KeyInformation, длина которого указана в параметре KeyInformationLength.

Количество записанных байт возвращается в ResultLength.

Наиболее важным является понимание того, что если мы скроем ключ, то

индексы всех последующих ключей будут сдвинуты. И так как нам придется получать

информацию о ключе с большим индексом запрашивая ключ с меньшим индексом, мы

должны подсчитать количество записей до скрытой и вернуть правильное значение.

Давайте разберем пример. Допустим в какой-то части реестра есть ключи с

именами A, B, C, D, E и F. Индекс начинается с нуля, это означает, что ключ E

имеет индекс 4. Теперь, если мы хотим скрыть ключ B и приложение вызвало

NtEnumerateKey с Index равным 4, мы должны вернуть информацию о ключе F, так

как индекс сдвинут. Проблема в том, что нам неизвестно, что нужно произвести

сдвиг. А если мы не позаботимся о сдвиге, то вернем E вместо F, когда будет

запрашиваться ключ с индексом 4, или ничего не вернем для ключа с индексом 1,

хотя должны вернуть C. В обоих случаях ошибка. Вот почему мы должны подумать

о сдвиге.

Если мы будем вычислять сдвиг вызовом функции для каждого индекса

от 0 до Index, иногда нам придется ждать годами (на 1ГГц процессоре это может

занять до 10 секунд со стандартным реестром, который очень большой). Поэтому

мы должны придумать более совершенный метод.

Мы знаем, что ключи (исключая ссылки) отсортированы по алфавиту. Если

мы пренебрежем ссылками (которые мы не хотим скрывать), мы сможем вычислить

сдвиг следующим методом. Мы отсортируем по алфавиту список имен ключей, которые

необходимо скрыть (используя RtlCompareUnicodeString), затем, когда приложение

вызывает NtEnumerateKey, мы не будем перевызывать ее с неизмененными

аргументами, а определим имя записи указанной параметром Index.

 

NTSTATUS RtlCompareUnicodeString(

IN PUNICODE_STRING String1,

IN PUNICODE_STRING String2,

IN BOOLEAN CaseInSensitive

);

 

String1 и String2 - строки, которые необходимо сравнить,

CaseInSensitive - True, если мы хотим провести сравнение, игнорируя регистр.

Результат функции описывает отношение между String1 и String2:

 

result > 0: String1 > String2

result = 0: String1 = String2

result < 0: String1 < String2

 

 Теперь мы должны найти границу. Мы сравним имя, указанное параметром Index с

именами в нашем списке. Границей будет последнее меньшее имя из нашего списка.

Мы знаем, что сдвиг не превышает номер граничного элемента в нашем списке.

Но не все элементы списка являются действительными ключами в той части реестра,

где мы находимся. Поэтому мы должны определить элементы списка до границы,

которые являются частью реестра. Мы можем сделать это используя NtOpenKey.

 

NTSTATUS NtOpenKey(

OUT PHANDLE KeyHandle,

IN ACCESS_MASK DesiredAccess,

IN POBJECT_ATTRIBUTES ObjectAttributes

);

 

KeyHandle - это хэндл родительского ключа. Мы будем использовать

значение, переданное в NtEnumerateKey. DesiredAccess - права доступа,

используем значение KEY_ENUMERATE_SUB_KEYS. ObjectAttributes описывают

подключ, которых мы хотим открыть (включая его имя).

 

#define KEY_ENUMERATE_SUB_KEYS 8

 

Если NtOpenKey вернет 0 - ключ был открыт успешно - это значит, что

этот элемент списка существует. Открытый ключ следует закрыть, используя

NtClose.

 

NTSTATUS NtClose(

IN HANDLE Handle

);

  

При каждом вызове функции NtEnumerateKey мы должны вычислять сдвиг,

как количество ключей из нашего списка, которые существуют в данной части

реестра. Затем мы должны прибавить этот сдвиг к аргументу Index и, наконец,

вызвать оригинальную NtEnumerateKey.

Для получения имени ключа, указанного параметром Index, мы используем

KeyBasicInformation в качестве значения для KeyInformationClass.

 

#define KeyBasicInformation 0

 

NtEnumerateKey вернет в буфере KeyInformation структуру:

 

typedef struct _KEY_BASIC_INFORMATION {

LARGE_INTEGER LastWriteTime;

ULONG TitleIndex;

ULONG NameLength;

WCHAR Name[1];

} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION;

 

Единственное что нам нужно - это Name, и его длина - NameLength.

Если ключа для сдвинутого параметра Index не существует, мы

должны вернуть ошибку STATUS_EA_LIST_INCONSISTENT.

 

#define STATUS_EA_LIST_INCONSISTENT 0x80000014

 

Добавить комментарий

Не использовать не нормативную лексику.

Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.

ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!


Защитный код
Обновить