Поймать syscall.

Тема в разделе "WASM.RESEARCH", создана пользователем Ahimov, 21 ноя 2025.

  1. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    602
    Привет.

    Давно были известные трудности с прямым сервисным вызовом в протекторах(vmp). Если есть sysenter/syscall, это делает бесполезным юзер отладку(их нужно отдать в плагины, скрывающие дебаг). Думал как выцепить ядерный вызов посредством юзер, те без изменений ядра.

    На 86(legacy) работало на сервисном возврате, те kifast..ret -> определялся сервисный прототип через аргументы.

    На wow(compat) через слой wow.

    Как в 64(long) поймать syscall ?

    Покопался в архитектуре, обработка тут например:

    Control-flow Enforcement Technology Specification

    По исходам xp идей нет, как фаулт прокинуть или еще что :dash1:

    Сегментацию использовать не выйдет(исполняться в другом сегменте).

    WIC это слишком глубокий интернал похоже.
     

    Вложения:

  2. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.502
    Адрес:
    Россия, Нижний Новгород
    Не глубокий, но неудобный: ядро возвращает нам управление на выходе из сисколла, и у нас потеряны первые четыре аргумента, которые передаются в регистрах.
    Если нам эти аргументы не нужны - то неплохой вариант.

    Также можно посмотреть в сторону BTF (Branch Single-Step), который генерирует #DB на каждой передаче управления (jmp, call и т.д.) - он должен триггериться и на сисколлы.

    Или можно использовать DTrace - трассировщик на базе ETW, напоминающий линуксовый eBPF: ты можешь подписаться на сисколл и вклинить свой обработчик на языке D: https://learn.microsoft.com/ru-ru/windows-hardware/drivers/devtest/dtrace-etw
    У него недостаток - необходимость перевода системы в отладочный режим.
     
    Mikl___ и Ahimov нравится это.
  3. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.223
  4. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    602
    Для sysenter можно провернуть такое, кодовый селектор откатывается в дефаулт. Если вызов расположен в rx проекции, а исходная na/nx, тоесть 0:na ~ cs:rx, тогда после смены мода sysexit вкинет ловушку.

    Жаль не используется произвольная сегментация в 64. Шедулер 86 как не странно не фиксит сегменты, что не позволяет обнаружить планирование. На 64 такое происходит:

    Код (ASM):
    1. ; If the user segment selectors have been changed, then reload them with
    2. ; their cannonical values.
    3. ;
    4.         mov     ax, ds                  ; compute sum of segment selectors
    5.         mov     cx, es                  ;
    6.         add     ax, cx                  ;
    7.         mov     cx, gs                  ;
    8.         add     ax, cx                  ;
    9.         cmp     ax, ((KGDT64_R3_DATA or RPL_MASK) * 3) ; check if sum matches
    10.         je      short KiSC25            ; if e, sum matches expected value
    11.         mov     cx, KGDT64_R3_DATA or RPL_MASK ; reload user segment selectors
    12.         mov     ds, cx                  ;
    13.         mov     es, cx
    HoShiMin
    Железная трассировка не пригодна, слишком много путей ее детекта и нужно фиксить профайлеры(сервисы возвращающие время). Тогда уж визоры :yes4:
    Mikl___
    Описание сервисных интерфейсов, как добыть ID.
     
    Mikl___ нравится это.
  5. galenkane

    galenkane Active Member

    Публикаций:
    1
    Регистрация:
    13 янв 2017
    Сообщения:
    453
    наблюдать/перехватывать обработчик, на который указывает MSR IA32_LSTAR (KiSystemCall64/Shadow)
    --- Сообщение объединено, 23 ноя 2025 ---
    можно еще так проверить Идея: заставить каждую SYSCALL/SYSRET падать в #UD, а гипервизор эти #UD перехватывает и эмулирует инструкцию.
    Кратко:
    1. В VMX-VMCS:
      • включаешь загрузку/сохранение IA32_EFER на VM-entry/exit;
      • включаешь MSR bitmap для EFER (ловишь чтения/записи);
      • в Exception Bitmap отмечаешь #UD.
    2. Логика с EFER.SCE:
      • при VM-entry/на MSR-read гость всегда видит EFER.SCE = 1 → SYSCALL/SYSRET разрешены;
      • при MSR-write ты можешь маскировать/чистить SCE, но главное — в момент исполнения SYSCALL/SYSRET ты делаешь так, чтобы в реальном EFER SCE = 0, тогда CPU вместо перехода в ядро выдаёт #UD → VM-exit.
    3. В обработчике VM-exit по #UD:
      • по RIP читаешь opcode:
        • если 0F 05 → SYSCALL,
        • если 48 0F 07 → SYSRET,
      • не инжектишь #UD обратно в гостя, а:
        • для SYSCALL:
          • эмулируешь то, что делает инструкция по SDM: перенос RIP→RCX, RIP←LSTAR, RFLAGS→R11, маска RFLAGS по FMASK, загрузка CS/SS по STAR, CPL=0 и т.п.,
          • на этом шаге у тебя полный доступ к RAX (номер syscаll) и аргументам → здесь ты «ловишь syscall»;
        • для SYSRET:
          • эмулируешь обратный переход: RIP←RCX, RFLAGS←R11 & mask, восстановление CS/SS по STAR, CPL=3.
    4. Из-за KVA Shadowing (KVAS) / shadow CR3:
      • если CR3 → user-PCID, сначала временно подменяешь CR3 на kernel DTB (PsGetCurrentProcess()->DirectoryTableBase), чтобы корректно прочитать opcode из памяти, потом возвращаешь старый.
    Итог: каждую SYSCALL/SYSRET ты перехватываешь на уровне гипервизора, без патча ядра и без трогания LSTAR/KiSystemCall64Shadow, просто через EFER.SCE + #UD.
     
    Mikl___ нравится это.
  6. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    602
    В юзер.
     
  7. galenkane

    galenkane Active Member

    Публикаций:
    1
    Регистрация:
    13 янв 2017
    Сообщения:
    453
    Перехват системных вызовов в x64 Windows (User-Mode)

    Инлайн-хук (трамплин в ntdll.dll)

    • Суть: Модификация кода системной функции в ntdll.dll – замена начала функции на переход (jmp) к своему обработчику. Такой «сплайсинг» внедряет т. н. трамплин: выполнение перенаправляется в ваш код, где можно проверить/изменить параметры, после чего по необходимости вызывается оригинальный syscalls-стаб. Таким образом перехватываются вызовы Nt*/Zw* функций на границе user-kernel.
    • Уровень перехвата: Пользовательский (patch применяется в ntdll.dll, ниже – в ядро – из user-mode добраться нельзя). Hook ставится на stub-функции в ntdll (перед выполнением инструкции syscall). В kernel-mode изменения не вносятся.
    • Права администратора: Не требуются для внедрения хука в свой процесс (достаточно права на запись в память процесса). Однако для глобального перехвата во всех процессах нужна загрузка DLL-хука в чужие процессы (через AppInit_DLLs, внедрение кода, и пр.), что обычно требует привилегий администратора.
    • Совместимость с KVA/PG/VBS: На работу KVA Shadowing (изоляции адресов ядра) и PatchGuard данный метод не влияет – хук вносится только в память процесса, Kernel Patch Protection следит лишь за ядром. Однако при включенной виртуализации безопасности (VBS, HVCI) могут возникнуть ограничения: Hypervisor Code Integrity может блокировать изменение кода подписанных системных DLL, поэтому патчить ntdll.dll в защищённых процессах не получится.
    • Надёжность: Метод прост в реализации, но легко обнаружим: подменённые первые байты функции заметны при сравнении с эталонным образом на диске. Некоторые EDR/AV-механизмы периодически проверяют целостность ntdll.dll и могут восстанавливать свои хуки либо реагировать на их отсутствие. Кроме того, поскольку хук выполняется в юзер-пространстве, злоумышленник при желании может его обойти (например, напрямую вызывая системный вызов, минуя заражённую функцию или восстановив оригинальный код). Тем не менее, сам по себе инлайн-хук остаётся работоспособным на всех версиях Windows (с учётом редких изменений в реализации целевых функций).
    Перехват через таблицу импорта (IAT Hooking)

    • Суть: Изменение адреса целевой API-функции в Import Address Table загруженного модуля. При запуске процесса Windows заполняет таблицы импорта адресами нужных функций; если перезаписать указатель на функцию в этой таблице, приложение будет вызывать вместо оригинала ваш обработчик. Таким образом можно перехватывать вызовы WinAPI, которые завершаются системным вызовом (например, CreateProcessW → NtCreateProcess).
    • Уровень перехвата: Пользовательский, выше ntdll. Хуки ставятся на уровень Win32-API (kernel32/user32 и др.), до перехода в нативный Nt* вызов. Системные вызовы как таковые напрямую не модифицируются – перехват идёт на уровне вызова библиотечной функции, которая в конечном счёте выполнит syscall.
    • Права администратора: Для своего процесса – нет (таблица импорта находится в памяти процесса и доступна для записи после изменения защиты памяти). Для перехвата чужих процессов придётся внедрять DLL или код, править IAT в их адресном пространстве – это требует либо привилегии отладки, либо администратора.
    • Совместимость с KVA/PG/VBS: KVA Shadowing и PatchGuard не влияют, так как ядро не модифицируется. VBS (HVCI) в целом не мешает, поскольку изменение происходит в разделе данных (таблица импорта) процесса, а не в коде. (Однако защищённые процессы и режимы с полной целостностью кода могут вообще запрещать загрузку нелегитимных DLL и внедрение кода).
    • Надёжность: Метод менее заметен, чем патчинг кода: правка IAT меняет указатель в памяти процесса, что не всегда тривиально обнаружить без сравнительного анализа. Но и перехватывает он не гарантированно всё. Продвинутый целевой процесс может вызвать системный вызов минуя высокоуровневый API (т.е. обращаться прямо к Nt* в ntdll), тогда IAT-хук в user32/kernel32 будет обходиться. Кроме того, обновления Windows не влияют на сам механизм, но конкретные функции и их импорты могут меняться – разработчику хука нужно следить, чтобы перехватывать все нужные вызовы (например, часть функций Win32 API могла переехать из kernel32 в kernelbase.dll и т.п.). В целом метод широко используется и довольно устойчив к изменениям ОС.
    Перехват через исключения (VEH hooking: INT3 / Page Guard)

    • Суть: Установка специального исключения на адрес системного вызова, чтобы перехватывать управление без прямого патчинга. Реализуется двумя основными способами: (1) программная точка останова (opcode INT 3) в начале целевой функции; (2) использование Page Guard или запрета выполнения на странице с целевой функцией. В обоих случаях, когда код приложения обращается к перехватываемой функции, происходит исключение в процессе. Предварительно регистрируется Vectored Exception Handler (VEH) – глобальный обработчик исключений в процессе – который ловит эту ловушку и передаёт управление вашему коду. Далее обработчик может выполнить нужные действия, восстановить состояние и продолжить выполнение оригинальной функции. Такой подход позволяет перехватить syscall-стаб, не изменяя его постоянным образом (вы вмешиваетесь только на время исключения).
    • Уровень перехвата: Пользовательский (на уровне ntdll.dll). Метод применим как к самим системным stub-функциям (например, NtOpenFile), так и теоретически к любым API. В частности, возможно трассировать выполнение системного вызова по инструкциям, подставляя обработчик перед выполнением syscall. Ядро не модифицируется.
    • Права администратора: Для своего процесса – нет (достаточно возможности вызвать VirtualProtect и AddVectoredExceptionHandler). Для глобального применения в чужих процессах – требуется их запустить под контролем отладчика или внедрить VEH-хендлер в них (что опять же требует привилегий).
    • Совместимость с KVA/PG/VBS: Не зависит от KVA Shadowing и не нарушает PatchGuard (не трогаем ядро). Также обычно совместимо с VBS – мы не выполняем неподписанный код, а лишь используем штатный механизм исключений. (Однако, если процесс защищён от отладки/инъекций, внедрить VEH в него может быть невозможно.)
    • Надёжность: Этот способ более скрытный – сама функция не патчится, в памяти нет постоянных аномалий. Тем не менее метод накладывает нагрузку: каждый перехваченный вызов вызывает исключение (soft interrupt), что может заметно снижать производительность при большом числе событий. Враждебный процесс может заметить, что его функции работают медленнее или обрабатываются через исключения, хотя прямого детектирования сложно провести. VEH-хуки достаточно универсальны и переживают обновления ОС (опираются на механизмы, вряд ли изменяющиеся). Главный риск – сложность реализации: нужно аккуратно обрабатывать контекст потока при исключении, чтобы не нарушить работу программы.
    Аппаратные точки останова (Hardware Breakpoint Hook)

    • Суть: Использование аппаратных средств отладки CPU (регистры DR0-DR7) для перехвата выполнения. Процесс настраивается в режим отладки, и на адрес целевой функции устанавливается аппаратный breakpoint (на выполнение по адресу). Когда поток достигает этой инструкции, процессор генерирует исключение EXCEPTION_SINGLE_STEP, которое можно перехватить аналогично через VEH/SEH. Обработчик получит управление до исполнения оригинальной инструкции, что позволяет внедрить свою логику. После этого можно пропустить или эмулировать вызов, либо выполнить оригинал и затем продолжить процесс. Данный метод не требует изменения памяти целевого процесса вообще – ловушка устанавливается на уровне CPU.
    • Уровень перехвата: Пользовательский, аналогично – ловим выполнение инструкции в ntdll (или любом другом модуле) из user-mode. Kernel не трогаем. В 64-битной Windows таким способом можно отследить вызовы системных stub-функций (но учтите: каждый поток имеет собственные DR-регистры, breakpoint нужно либо устанавливать для каждого, либо использовать процесс в режиме debug для распространения на все потоки).
    • Права администратора: Для своего процесса – можно использовать SetThreadContext на свои потоки без повышенных прав. Но чаще сценарий – отладка чужого процесса, что требует SeDebugPrivilege (админ) для Attach и установки hardware breakpoints.
    • Совместимость с KVA/PG/VBS: С KVA Shadowing и PatchGuard проблем нет (мы не вмешиваемся в ядро). VBS/HVCI тоже напрямую не мешает – метод не модифицирует кода и не выполняет неподписанного кода. Однако Protected Process или приложения с анти-отладочными мерами могут просто не позволить установить debug-прерывание (система запретит отладку таких процессов).
    • Надёжность: Метод очень незаметный в памяти (нет патчей), но может быть выявлен по факту отладки: целевой процесс будет считаться запущенным под дебаггером. Многие вредоносные программы и защищённые приложения проверяют флаг BeingDebugged в PEB или наличие активного отладчика – если hardware-хук ставится через стандартные Debug API, это может быть обнаружено и вызовет изменение поведения программы. Кроме того, аппаратных брейкпойнтов ограниченное число (4), и они работают на уровне потока, что усложняет перехват во многих потоках одновременно. В остальном, при обычных процессах Windows этот способ стабилен и не зависит от обновлений ОС (функционал CPU/Debug Registers стандартен).
    Instrumentation Callback (процессный callback на syscall)

    • Суть: Undocumented-механизм Windows, позволяющий получить управление при возврате из ядра в user-mode после системного вызова. Для каждого процесса в структуре KPROCESS предусмотрено поле InstrumentationCallback – указатель на функцию в user-mode. Если его установить, то каждый раз после выполнения syscalls (а также некоторых исключений) ядро будет возвращаться не прямо в точку вызова, а сначала в указанный callback. Этот callback в контексте процесса получает управление перед исполнением инструкции ret в нативной функции, т. е. может анализировать/менять результат вызова, а затем продолжить нормальный поток выполнения. По сути, это официальный скрытый хук от Microsoft: он не изменяет код ntdll, а перехватывает системные вызовы через встроенный механизм планировщика.
    • Уровень перехвата: На границе user/kernel, ниже ntdll (сам stub не патчится, перехват реализован внутри ядра Windows). То есть, хук срабатывает даже если злонамеренный код напрямую вызывает инструкцию syscall – после выхода из ядра он всё равно попадёт в ваш callback. Сам callback исполняется в user-mode в адресном пространстве процесса.
    • Права администратора: Установка Instrumentation Callback доступна через нативный вызов NtSetInformationProcess (класс 0x28). Для своего процесса можно вызывать его без привилегий, а вот чтобы задать callback для чужого процесса, требуется флаг SeDebugPrivilege (отладка). Проще говоря, администратор может настроить этот перехват для любого процесса, а обычная программа – только для себя.
    • Совместимость с KVA/PG/VBS: Этот метод официально поддерживается ядром (хоть и недокументирован), поэтому он не конфликтует с KVA Shadowing и не нарушает PatchGuard – никаких запрещённых патчей в ядро не вносится. Hypervisor-based Code Integrity обычно не мешает установке callback’а, так как ваш код уже загружен как часть процесса (но если процесс защищённый, без соответствующих сертификатов установить callback извне не удастся). В общем случае Instrumentation Callback работает даже при включённом VBS.
    • Надёжность: Метод считается довольно скрытным и стабильным. В памяти процесса никаких изменений системных модулей нет, ловушка реализована силами самого Windows, что затрудняет её обнаружение простыми методами. Антивирусы и анти-чит системы редко проверяют наличие установленного Instrumentation Callback, т.к. он используется внутренними инструментами Microsoft и малоизвестен. Кроме того, hook срабатывает до возврата в исходный код, его сложно обойти злоумышленнику (разве что полностью избегать любых системных вызовов, что нереально). К минусам можно отнести недокументированность: реализация могла меняться (для Windows 7 и 10 отличаются номера InfoClass в NtSetInformationProcess). Тем не менее, начиная с Windows 10 механизм стабильно присутствует, и вероятность, что обновления Windows его полностью сломают, невелика. Detectability для продвинутого защитного ПО возможна (например, по аномальному значению поля InstrumentationCallback в EPROCESS), но подобная проверка встречается крайне редко.
     
    Mikl___ нравится это.
  8. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.502
    Адрес:
    Россия, Нижний Новгород
    galenkane, в чём ценность ответов от ИИ, особенно если они даже близко не по теме?
     
    M0rg0t и Ahimov нравится это.
  9. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    602
    galenkane

    Это все не в тему совсем. Заранее адреса шлюзов не известны. Но даже если их найти, патч не выйдет, тк есть проверки целостности - самого шлюза и проверяющего его кода.

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