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

Впервые точки остановки для ключей реестра появились в 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]

Выводы


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

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


  1. msatersam11
    07.02.2017 13:11

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


    Что мешает procmon( Process Monitor ) использовать в таких случаях?
    К слову, предлагается к скачиванию, даже на микрософтоском сайте.


    1. msuhanov
      07.02.2017 13:35
      +1

      Что мешает procmon( Process Monitor ) использовать в таких случаях?


      Ничего не мешает. С другой стороны, функциональности этой программы не всегда может хватать.


    1. qw1
      07.02.2017 21:42
      +1

      Что мешает procmon( Process Monitor ) использовать в таких случаях?
      В современных Windows даже procmon с его драйвером ядра — оверкилл, можно просто подписаться на провайдера событий «Microsoft-Windows-Kernel-Registry» (похожий пример)

      Однако, так мы получим факт, что процесс с PID 0x12345 сделал операцию в реестре, но не получим доступ к коду, которым он это сделал. Может, код самоудаляется после выполнения. А тут встали на breakpoint и можно выйти в программу — поотлаживать, посмотреть, какие логические пути ведут к этому вызову API.


  1. maxdm
    07.02.2017 17:33

    Класс, а для объектов ядра подобного нет?