
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), для которых требуются эти функции.