Недокументированные возможности Windows: точки остановки для ключей реестра

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

Впервые точки остановки для ключей реестра появились в Windows XP, где была реализована возможность исполнения ядром инструкции int 3 при открытии ключа реестра с пометкой (отладочным флагом) BREAK_ON_OPEN или при создании подключа в составе такого ключа.

9c018f6aebf4474fa58a17b6e3dd6307.png
Рис. 1: Фрагмент функции CmpDoOpen

Данная функциональность появилась в ядре еще до выпуска каких-либо пакетов обновления, причем устанавливать отладочную («checked») версию ядра не требовалось. Вместе с тем API-функций для установки флага BREAK_ON_OPEN не было, а потому этот флаг можно было выставить у ключа только через редактирование файла с кустом реестра в HEX-редакторе.

Начиная с Windows Vista, список доступных отладочных флагов был расширен, появилась возможность установки этих флагов через API-функцию NtSetInformationKey, однако сама функциональность осталась только в отладочных версиях ядра (которые можно взять из Windows Driver Kit).

Табл. 1: Возможные значения отладочного флага

Флаг Значение Примечание
BREAK_ON_OPEN 0×01 Открытие ключа
BREAK_ON_DELETE 0×02 Удаление ключа
BREAK_ON_SECURITY_CHANGE 0×04 Изменение дескриптора безопасности
BREAK_ON_CREATE_SUBKEY 0×08 Создание подключа
BREAK_ON_DELETE_SUBKEY 0×10 Удаление подключа
BREAK_ON_SET_VALUE 0×20 Установка значения
BREAK_ON_DELETE_VALUE 0×40 Удаление значения
BREAK_ON_KEY_VIRTUALIZE 0×80 Виртуализация ключа

Для установки отладочного флага необходимо вызвать функцию NtSetInformationKey, передав ей в качестве первого аргумента дескриптор ключа реестра, для которого нужно установить отладочный флаг. В качестве второго аргумента — KeySetDebugInformation, а последние два аргумента должны описывать буфер, содержащий двойное слово (DWORD), в котором размещено значение отладочного флага (или комбинация из двух и более флагов).

Для активации обсуждаемых точек остановки необходимо выставить значение переменной ядра CmpRegDebugBreakEnabled в единицу.

Пример


В качестве примера попытаемся поймать момент записи значения в ключ реестра »HKEY_LOCAL_MACHINE\SYSTEM\MountedDevices», где сохраняются сведения о смонтированных томах, и определить компонент Windows, который записывает туда данные. Для этого отметим указанный ключ отладочным флагом BREAK_ON_SET_VALUE, включим точки остановки, изменив значение переменной CmpRegDebugBreakEnabled на единицу, и отформатируем тестовый диск, который затем смонтируем.

В результате происходит срабатывание точки остановки, виден следующий стек вызовов:

 # Child-SP          RetAddr           Call Site
00 ffffd000`79b851a0 fffff803`7ccf88fa nt!CmSetValueKey+0x158
01 ffffd000`79b852b0 fffff803`7c69eac3 nt!NtSetValueKey+0x73e
02 ffffd000`79b85470 fffff803`7c697e40 nt!KiSystemServiceCopyEnd+0x13
03 ffffd000`79b85678 fffff803`7d08f11e nt!KiServiceLinkage
04 ffffd000`79b85680 fffff801`46d6fcaa nt!RtlWriteRegistryValue+0x9e
05 ffffd000`79b856f0 fffff801`46d6b58c mountmgr+0xecaa
06 ffffd000`79b85820 fffff803`7cd9cd78 mountmgr+0xa58c
07 ffffd000`79b85850 fffff803`7cd9bdd5 nt!PnpNotifyDriverCallback+0x1b8
08 ffffd000`79b85900 fffff803`7cdd6755 nt!PnpNotifyDeviceClassChange+0x2f9
09 ffffd000`79b859d0 fffff803`7c66dcb7 nt!PnpDeviceEventWorker+0x4c1
0a ffffd000`79b85b50 fffff803`7c5e7071 nt!ExpWorkerThread+0x177
0b ffffd000`79b85be0 fffff803`7c699836 nt!PspSystemThreadStartup+0x23d
0c ffffd000`79b85c60 00000000`00000000 nt!KiStartSystemThread+0x16

По стеку вызовов ясно видно, что запись инициирует компонент «mountmgr», в его коде можно обнаружить искомый вызов nt! RtlWriteRegistryValue:
fffff801`46d6fca4 ff15fe44ffff    call    qword ptr [mountmgr+0x31a8 (fffff801`46d641a8)]  // вызов nt!RtlWriteRegistryValue
fffff801`46d6fcaa 488d55b7        lea     rdx,[rbp-49h]

Выводы


Описанная функциональность может быть использована как для решения задач, связанных непосредственно с отладкой ядра и драйверов, так и для решения сторонник задач — например, поиска программ в исследуемом образе системы, которые используют заданный ключ реестра.

Комментарии (0)

© Habrahabr.ru