Три коротких истории о реестре Windows

в 14:12, , рубрики: c++, windows, Блог компании «Лаборатория Касперского», разработка под windows, реестр windows, системное программирование

Добрый день, уважаемые читатели.

Реестр — это одна из самых заметных и значительных систем Windows. Вряд ли найдется человек, который не слышал о нем. Занимаясь программированием под Windows уже около 20 лет, я думал, что знаю о нем все. Но время от времени появляется что-то новое, что показывает мне, как я был неправ. Поэтому сегодня я хочу рассказать вам о необычных способах работы с реестром, которые я встречал, исследуя руткиты, и которые удивили меня.

Три коротких истории о реестре Windows - 1

История первая. Имена значений и ключей реестра

Все мы знаем, что в Windows существуют некоторые правила именования объектов, будь то файлы, каталоги или ключи реестра. Имена файлов не могут содержать символ “”. Имена не могут быть пустыми. У имен есть некоторые ограничения по длине и т. д.

Невольно мы распространяем эти ограничения на все системы Windows и соблюдаем их при работе с реестром. И тут заключается наша ошибка. В реестре ограничений при создании имен на удивление мало. Например, в имени значений можно использовать символ “”

Три коротких истории о реестре Windows - 2

Удивлены? Нет? Тогда что вы скажете, если я покажу вам, что в имени значения можно использовать символ “”? Да-да, именно нулевой символ. Тот самый, который традиционно используется для указания конца строки.

Для этого нам понадобится функция NtSetValueKey, экспортируемая из ntdll.dll

HKEY hKey = 0;
RegOpenKeyA(
       HKEY_LOCAL_MACHINE, 
       "Software\Microsoft\Windows\CurrentVersion\Run", 
       &hKey);

UNICODE_STRING uName;
uName.Buffer = L"TestZero";
uName.MaxLen = uName.Length = 9 * sizeof(wchar_t);	

NTSTATUS status = 0;

status = NtSetValueKey(
             hKey, 
             &uName, 
             0, 
             REG_SZ, 
             (void*)lpData, 
             DataSize); 

RegCloseKey(hKey);

Для выполнения функции NtSetValueKey вам понадобятся права администратора. В результате у вас в реестре появится значение с именем TestZero.

Некоторые разработчики Microsoft тоже будут удивлены, т. к. стандартный редактор реестра не может отобразить такое необычное значение реестра.

Три коротких истории о реестре Windows - 3

История вторая

Вторая история, которую я вам сегодня расскажу, произошла в 2013 году.

Вначале небольшое отступление. В «Лаборатории Касперского» я являюсь членом команды, которая, помимо всего прочего, создает Kaspersky Rescue Disk. Для лечения Windows из-под Linux нам необходимо самостоятельно разбирать файлы реестра. И для проверки правильности работы этого механизма мы используем множество тестов. Среди них есть один достаточно простой:

  • Под Windows записываем в реестре тестовые значения.
  • Копируем файл куста реестра в тестовый каталог.
  • Запускаем программу, выполняющую удаление тестовых значений.
  • Загружаем модифицированный куст в реестр для проверки правильности удаления.

И вот в один прекрасный день мы обновили на тестовом стенде Windows до версии 8.1, и тест перестал удалять проверочные значения. Как же так, удивился я. Скопировал файл с кустом реестра к себе на рабочий компьютер — нет значений! Моя первая мысль: нужно добавить в тест Flush измененных ключей. Добавил вызов RegFlushKey, перезапустил тест — нет значений!

Неужели RegFlushKey не работает, задумался я. Но, как оказалось, прав я был лишь частично.

Фокус оказался в том, что в Windows 8.1 компания Microsoft изменила механизм сохранения изменений в реестр. Раньше все изменения реестра накапливались в памяти, а потом, при закрытии ключа, при выполнении RegFlushKey или по истечении некоторого времени система сохраняла изменения в файл куста реестра. В Windows 8.1 изменения вместо файла куста реестра сохраняются в одноименные файлы с расширениями .LOG, .LOG1 и .LOG2, а мой код эти файлы в те времена игнорировал.

В этих файлах изменения накапливаются около часа. И лишь после этого Windows начинает задачу интеграции изменений в основной файл. Эта задача называется Reconciliation, и она запускается либо раз в 40 минут, либо при завершении работы Windows. Вызов функции RegFlushKey к запуску задачи Reconciliation не приводит. Для принудительного запуска задачи интеграции изменений нужно вызвать ZwSetSystemInformation с недокументированным аргументом SystemRegistryReconciliationInformation.

ZwSetSystemInformation(
    0x9b, //SystemRegistryReconciliationInformation
    NULL, 
    0);

Для выполнения функции ZwSetSystemInformation вам понадобятся права администратора. А архитектура исполняемого файла должна совпадать с архитектурой системы. Вызвать эту функцию из 32-битной программы в 64-битной Windows не получится.

История третья

Некоторое время назад мы обнаружили руткит, который прописывал в реестре запуск своего драйвера. Наши продукты удаляли соответствующие ключи реестра, но после перезагрузки ключи оказывались на своих местах. Похоже, он ставит свои callback-функции на изменения реестра и после наших изменений восстанавливает свои ключи, подумал я. Но оказалось, что нет. Точнее — да. Callback-функции руткит ставил, но к задаче восстановления ключей они не имели никакого отношения. Все было сделано проще и изящнее.

Драйвер руткита при запуске переименовывал файл куста реестра SYSTEM в HARDWARE. Создавал свой файл SYSTEM и периодически сохранял в него с помощью функции RegSaveKey ветку HKLM/System. При сохранении он восстанавливал свои ключи. При перезагрузке Windows система загружала файл SYSTEM и запускала драйвер руткита. Красиво? Красиво.

Wanted

P.S. У нас тут ищут разработчика-исследователя в команду, которая пилит анти-спамовый движок, а еще нужен инженер по тестированию.

Автор: OldMaster

Источник

* - обязательные к заполнению поля