Недокументированные возможности Windows: точки остановки для ключей реестра
Впервые точки остановки для ключей реестра появились в Windows XP, где была реализована возможность исполнения ядром инструкции int 3 при открытии ключа реестра с пометкой (отладочным флагом) BREAK_ON_OPEN
или при создании подключа в составе такого ключа.
Рис. 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]
Выводы
Описанная функциональность может быть использована как для решения задач, связанных непосредственно с отладкой ядра и драйверов, так и для решения сторонник задач — например, поиска программ в исследуемом образе системы, которые используют заданный ключ реестра.