
A DTrace on Windows Reimplementation
Трассировщик Стива. Повторная реализация перехвата системных вызовов DTrace для Windows. Представьте это как совместимый с PatchGuard хук SSDT, но без хаков. Он не поддерживает SSSDT (win32k API), поскольку сами API системных вызовов DTrace не поддерживают эту дополнительную таблицу. Ядерные API Zw* можно трассировать в дополнение ко всем системным вызовам SSDT в пользовательском режиме.
Пожалуйста, дочитайте этот README до конца, если вы разрабатываете новые плагины для STrace!
DTrace в Windows поддерживает множество типов зондов (probe), включая syscall, fbt, etw, profile и, возможно, другие. Эта повторная реализация воспроизводит только типы зондов syscall и etw. Все остальные типы зондов считаются полностью выходящими за рамки этого проекта и никогда не будут поддерживаться. Не стесняйтесь форкать проект, если хотите добавить дополнительные типы зондов. Область ограничена только зондами syscall и etw из-за сложности остальных типов зондов и систем, которых они касаются.
Эта повторная реализация полностью отбрасывает скриптовый язык D (обратите внимание: не DLang — более популярный современный язык). Сложность виртуальной машины в ядре, как в оригинальной реализации DTrace, неприемлема для этого проекта. Вместо этого данная реализация напрямую предоставляет соответствующие C-колбэки, необходимые для подключения к интерфейсам ядра Windows DTrace. Чтобы обеспечить «горячую загрузку» скриптов, вместо виртуальной машины и скриптового окружения оригинального DTrace была использована система плагинов на основе DLL. Эта система плагинов принимает «обычную» DLL пользовательского режима без включённых проверок безопасности или иных внешних зависимостей и вручную отображает её в адресное пространство ядра. Экспорты plugin dll вызываются при срабатывании колбэков системных вызовов ядра. Ядерные API разрешаются через обычную таблицу импорта (IAT) DLL; DLL плагинов линкуются с ntoskrnl.lib, после чего драйвер разрешает эти API во время загрузки, позволяя вызывать любые системные API внутри DLL плагинов как обычно. Производительность этой системы плагинов отличная, поскольку между ENTRY и RETURN системного вызова напрямую выполняется нативный код, а не интерпретатор скриптов или JIT. Такая конструкция повышает производительность по сравнению с реализацией DTrace, предоставленной Microsoft.
Установка хуков очень проста: вы получаете процедуру регистрации/снятия хука по имени API с колбэком до (pre) и после (post) системного вызова. Колбэки предоставляют аргументы и возвращаемые значения, доступные только для чтения. Возвращаемые значения можно подменять в return-зондах, а аргументы — изменять в entry-зондах, однако сам системный вызов обычно нельзя заменить или «отменить» — этот API действует как наблюдатель. Однако существует упущение, аналогичное описанному в (https://github.com/everdox/InfinityHook и https://github.com/everdox/InfinityHook/raw/master/resources/perf.png), которое позволяет заменить указатель системного вызова в стеке. Благодаря этому упущению можно полностью заменить системный вызов указателем на процедуру, которую вы контролируете. Когда происходят события и срабатывает ваш колбэк хука, можно сделать всё, что угодно. Колбэки синхронны: если вы задержите выполнение в entry-колбэке, например, зациклившись в цикле while, вызов системного вызова будет задержан на это время. Выполнение системного вызова происходит сразу после возврата из entry-колбэка и непосредственно перед входом в return-колбэк. Эта система полностью совместима с PatchGuard, однако DSE должна быть отключена, поскольку Microsoft, к сожалению, считает такой тип расширения ядра частью ядра NT и поэтому проверяет, что корневой подписант — Windows. Это не работает с такими уловками, как включение пользовательских подписантов ядра: DSE действительно должна быть отключена во время загрузки ядра.
ValidationFlags=IMGP_LATEST_MS_ROOT_REQUIRED | IMGP_WINDOWS_ROOT_REQUIRED | IMGP_MS_SIGNATURE_REQUIRED
Scenario=ImgSigningScenarioWindows
Rust-драйвер отказывается от системы плагинов на основе DLL, используемой C++-драйвером, и вместо этого пытается использовать интерпретатор WebAssembly для размещения wasm-скриптов в ядре Windows. Это сделано для большего сходства с оригинальной реализацией DTrace, которая использует виртуальную машину, но с языком лучше, чем DLang. Это обеспечивает песочницу и другие приятные преимущества. POC завершён и работает, но, к сожалению, непригоден для использования из-за проблем с производительностью при интерпретации WASM с помощью WASMI. Чтобы этот альтернативный вариант был работоспособен, потребуется JIT-движок wasm, совместимый с ядром NT. Он включён в качестве забавной новинки и при некоторой доработке может найти применение для чего-то ещё.
Этот проект в основном разделён на свои C++ и Rust компоненты. C-драйвер полностью функционален, и его следует предпочитать. На высоком уровне проект имеет следующие компоненты:
Настройка Visual Studio с DDK
Соберите драйвер и CLI, переместите файлы в ту же папку, где находится скрипт, затем запустите powershell-скрипт из папки install от имени администратора:
./install_as_admin.ps1
Перезагрузитесь, затем выберите загрузочную запись STrace и нажмите F8 (не Enter!) на этом экране:

После этого должен появиться этот экран — выберите загрузку с отключённой DSE:

После успешной загрузки CLI можно использовать для загрузки и выгрузки DLL плагинов, чтобы начать трассировку. Предоставляются два примера плагинов. Если возникнут проблемы, внимательно изучите подробности ниже. В первый раз, возможно, потребуется перезагрузиться ещё раз и, вероятно, вручную настроить службу STrace на автозапуск at boot с помощью такого инструмента, как Process Hacker.
Оригинальная установка DTrace находится здесь: https://techcommunity.microsoft.com/t5/windows-kernel-internals/dtrace-on-windows-20h1-updates/ba-p/1127929. Оригинальный DTrace требует настройки Secure Boot и Virtualized Based Security; с STrace это не так, поскольку он не реализует типы зондов (FBT), для которых требуются эти функции.
Этот проект выполняет те же операции, что и установщик, но с помощью простого powershell-скрипта вместо MSI. Процесс прост: сначала скопируйте apiset dll в system32, чтобы активировалось расширение ядра. Затем установите запись драйвера, чтобы main драйвера выполнялся при запуске системы для связи с пользовательским режимом. ApiSet DLL цифрово подписаны, поэтому, как и требовалось, был использован оригинальный apiset от Microsoft. Этот файл предоставляется только в бинарном виде и не содержит никакой логики, кроме указания импорта расширения на реализующий драйвер: ext-ms-win-ntos-trace-l1-1-0 -> dtrace.sys. Подробнее об этом механизме см. https://www.geoffchappell.com/studies/windows/win32/apisetschema/index.htm (ApiSetSchemaExtensions).
STrace загружается очень рано во время инициализации ядра; включения Test Signing в базе данных конфигурации загрузки (BCD) недостаточно. Принудительная проверка подписи драйверов (DSE) должна быть отключена для успешной загрузки STrace, и это необходимо делать вручную при каждой загрузке. Чтобы упростить это, установочный скрипт создаёт простую запись в меню загрузки, которую пользователь может выбрать. Не существует флага BCD, позволяющего отключить DSE навсегда между перезагрузками; ядро прямо запрещает это.
Забыв отключить DSE при загрузке, вы гарантированно попадёте в меню автоматического восстановления (Automatic Repair) после перезапуска. Можно попробовать снова без вреда; если загрузка продолжит завершаться сбоем, драйвер STrace нужно будет вручную удалить из папки драйверов. Имейте в виду: сбои загрузки могут привести к отключению флага автозапуска службы STrace в Windows*; возможно, потребуется вернуть его на автозапуск, если в любой момент произойдёт сбой загрузки.
Для разработки собственных плагинов лучше всего использовать один из существующих плагинов в качестве базового проекта. Проекты Visual Studio настроены со множеством очень специфических параметров для создания автономных бинарных файлов без зависимостей. Нестандартных параметров слишком много, чтобы их перечислять, поэтому просто скопируйте один из проектов и измените код, добавив собственную логику (https://stackoverflow.com/questions/884255/visual-studio-copy-project). Если вы создали полезный плагин, пожалуйста, отправьте PR! Чем больше плагинов создано, тем полезнее эта система для всех!