Иногда в процессе обратной разработки какой-либо программы (в том числе драйвера) может потребоваться прервать ее исполнение в момент совершения некоторого действия с определенным ключом реестра, в такой ситуации можно воспользоваться недокументированной функциональностью точек остановки для ключей реестра.
Впервые точки остановки для ключей реестра появились в Windows XP, где была реализована возможность исполнения ядром инструкции int 3 при открытии ключа реестра с пометкой (отладочным флагом)
Рис. 1: Фрагмент функции CmpDoOpen
Данная функциональность появилась в ядре еще до выпуска каких-либо пакетов обновления, причем устанавливать отладочную («checked») версию ядра не требовалось. Вместе с тем API-функций для установки флага
Начиная с Windows Vista, список доступных отладочных флагов был расширен, появилась возможность установки этих флагов через API-функцию NtSetInformationKey, однако сама функциональность осталась только в отладочных версиях ядра (которые можно взять из Windows Driver Kit).
Табл. 1: Возможные значения отладочного флага
Для установки отладочного флага необходимо вызвать функцию NtSetInformationKey, передав ей в качестве первого аргумента дескриптор ключа реестра, для которого нужно установить отладочный флаг. В качестве второго аргумента — KeySetDebugInformation, а последние два аргумента должны описывать буфер, содержащий двойное слово (DWORD), в котором размещено значение отладочного флага (или комбинация из двух и более флагов).
Для активации обсуждаемых точек остановки необходимо выставить значение переменной ядра CmpRegDebugBreakEnabled в единицу.
В качестве примера попытаемся поймать момент записи значения в ключ реестра «
В результате происходит срабатывание точки остановки, виден следующий стек вызовов:
По стеку вызовов ясно видно, что запись инициирует компонент «mountmgr», в его коде можно обнаружить искомый вызов nt!RtlWriteRegistryValue:
Описанная функциональность может быть использована как для решения задач, связанных непосредственно с отладкой ядра и драйверов, так и для решения сторонних задач — например, поиска программ в исследуемом образе системы, которые используют заданный ключ реестра.
Впервые точки остановки для ключей реестра появились в 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 | 0x01 | Открытие ключа |
BREAK_ON_DELETE | 0x02 | Удаление ключа |
BREAK_ON_SECURITY_CHANGE | 0x04 | Изменение дескриптора безопасности |
BREAK_ON_CREATE_SUBKEY | 0x08 | Создание подключа |
BREAK_ON_DELETE_SUBKEY | 0x10 | Удаление подключа |
BREAK_ON_SET_VALUE | 0x20 | Установка значения |
BREAK_ON_DELETE_VALUE | 0x40 | Удаление значения |
BREAK_ON_KEY_VIRTUALIZE | 0x80 | Виртуализация ключа |
Для установки отладочного флага необходимо вызвать функцию 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]
Выводы
Описанная функциональность может быть использована как для решения задач, связанных непосредственно с отладкой ядра и драйверов, так и для решения сторонних задач — например, поиска программ в исследуемом образе системы, которые используют заданный ключ реестра.
Поделиться с друзьями
msatersam11
Что мешает procmon( Process Monitor ) использовать в таких случаях?
К слову, предлагается к скачиванию, даже на микрософтоском сайте.
msuhanov
Ничего не мешает. С другой стороны, функциональности этой программы не всегда может хватать.
qw1
Однако, так мы получим факт, что процесс с PID 0x12345 сделал операцию в реестре, но не получим доступ к коду, которым он это сделал. Может, код самоудаляется после выполнения. А тут встали на breakpoint и можно выйти в программу — поотлаживать, посмотреть, какие логические пути ведут к этому вызову API.