SetWindowsHookExW — полная внутренняя реализация в ядре

Тема в разделе "WASM.RESEARCH", создана пользователем galenkane, 2 июн 2026 в 05:37.

  1. galenkane

    galenkane Active Member

    Публикаций:
    4
    Регистрация:
    13 янв 2017
    Сообщения:
    475
    Может кому интересно как работает SetWindowsHookExW ;)

    Цепочка вызовов (Call Chain)
    user32!SetWindowsHookExW(idHook, lpfn, hMod, dwThreadId)
    → syscall → win32k!stub_UserSetWindowsHookEx
    → win32k!NtUserSetWindowsHookEx (ApiSet dispatch через
    W32GetWin32kApiSetTable+0xDB8)
    → win32kfull!NtUserSetWindowsHookEx (реализация)
    → Feature_656357688 ?
    zzzSetWindowsHookEx_New() : zzzSetWindowsHookEx()
    NtUserSetWindowsHookEx (win32kfull!0x6674600)

    Аргументы (ARM64 ABI):
    - x0 = hMod (module handle)
    - x1 = lpfn (hook procedure)
    - x2 = dwThreadId
    - x3 = idHook (w24)
    - x4 = flags
    - x5 = additional flags (w21)
    Ключевые шаги:
    1. Валидация idHook — idHook + 1 <= 0xF (типы от -1 до 13)
    2. EnterLeaveCritShared — вход в критическую секцию win32k
    3. PtiFromThreadId — если dwThreadId != 0, находит THREADINFO; если поток не
    найден → ERROR_INVALID_PARAMETER (0x57)
    4. PEB validation — получает PEB через PsGetCurrentProcess → PsGetProcessPeb,
    ProbeForRead на 0x7D0 байт, проверяет PEB+0x10 (BaseDllName/Ldr) совпадает с hMod
    5. Module string validation — проверяет UNICODE_STRING из таблицы модулей: длина,
    alignment, bounds
    6. Feature gate — Feature_656357688__private_IsEnabledDeviceUsageNoInline:
    - Включён → zzzSetWindowsHookEx_New (новый путь)
    - Выключен → zzzSetWindowsHookEx (классический путь)
    7. EtwTraceAuditApiSetWindowsHookEx — ETW аудит
    8. THREADINFO flag — устанавливает бит 0x1000 в THREADINFO+0xE0 (отметка что поток
    имеет хуки)
    Хранение хуков
    Из анализа функций PhkFirstValid, PhkNextValid, xxxCallHook2:
    - HOOK структура (tagHOOK) — выделается через handle manager (HMPheFromObject)
    - Hook chain — связный список; обход через PhkNextValid(phk)
    - Хранятся в THREADINFO — поле THREADINFO+0x210 указывает на hook chain
    - Desktop-level — глобальные хуки (WH_KEYBOARD_LL, WH_MOUSE_LL) через
    W32GetUserSessionState+0xA588 (session hook table)
    - DLT_HOOK — Domain Lock Type для синхронизации (gDomainHookLock в win32kbase)
    - Locking — Win32HMThreadLock<tagHOOK> / Win32HMOptionalThreadLockAlways<tagHOOK>
    для thread-safe доступа
    Диспетчеризация хуков (xxxCallHook2)
    Это центральная функция dispatch — вызывается при каждом событии, которое может
    попасть в хук:

    1. Session state — получает через W32GetUserSessionState
    2. Hook type check — tagHOOK+0x30 содержит idHook (w23)
    3. UIPI (User Interface Privilege Isolation) — вызывает:
    - UIPrivilegeIsolation::Enforced() — включена ли изоляция
    - UIPrivilegeIsolation::CheckAccess() — может ли хук быть вызван
    - _IsProcessDwm() — проверяет DWM (особые привилегии)
    - IsRestricted() / IsImmersiveBroker() / IsImmersiveAppRestricted()
    - Если UIPI блокирует → EtwTraceUIPIHookError
    4. Hook iteration — проходит цепочку через PhkNextValid(), проверяя каждый хук
    5. Module validation — xxxLoadHmodIndex проверяет что DLL хука ещё загружена
    6. Callback invocation — xxxHkCallHook() — фактический вызов hook procedure
    7. Free if needed — FreeHook() если хук помечен для удаления
    Вызов hook callback (xxxHkCallHook → KeUserModeCallback)
    xxxHkCallHook в конечном итоге вызывает KeUserModeCallback для передачи управления
    в usermode. Для каждого типа хука есть свой wrapper:
    user32!SetWindowsHookExW(idHook, lpfn, hMod,
    dwThreadId)
    → syscall → win32k!stub_UserSetWindowsHookEx
    → win32k!NtUserSetWindowsHookEx (ApiSet
    dispatch через W32GetWin32kApiSetTable+0xDB8)
    → win32kfull!NtUserSetWindowsHookEx
    (реализация)
    → Feature_656357688 ?
    zzzSetWindowsHookEx_New() :
    zzzSetWindowsHookEx()
    NtUserSetWindowsHookEx (win32kfull!0x6674600)
    Аргументы (ARM64 ABI):
    - x0 = hMod (module handle)
    - x1 = lpfn (hook procedure)
    - x2 = dwThreadId
    - x3 = idHook (w24)
    - x4 = flags
    - x5 = additional flags (w21)
    Ключевые шаги:
    1. Валидация idHook — idHook + 1 <= 0xF (типы от -1
    до 13)
    2. EnterLeaveCritShared — вход в критическую секцию
    win32k
    3. PtiFromThreadId — если dwThreadId != 0, находит
    THREADINFO; если поток не найден →
    ERROR_INVALID_PARAMETER (0x57)
    4. PEB validation — получает PEB через
    PsGetCurrentProcess → PsGetProcessPeb, ProbeForRead
    на 0x7D0 байт, проверяет PEB+0x10
    (BaseDllName/Ldr) совпадает с hMod
    5. Module string validation — проверяет
    UNICODE_STRING из таблицы модулей: длина,
    alignment, bounds
    6. Feature gate — Feature_656357688__private_IsEnab
    ledDeviceUsageNoInline:
    - Включён → zzzSetWindowsHookEx_New (новый путь)
    - Выключен → zzzSetWindowsHookEx (классический
    путь)
    7. EtwTraceAuditApiSetWindowsHookEx — ETW аудит
    8. THREADINFO flag — устанавливает бит 0x1000 в
    THREADINFO+0xE0 (отметка что поток имеет хуки)
    Хранение хуков
    Из анализа функций PhkFirstValid, PhkNextValid,
    xxxCallHook2:
    - HOOK структура (tagHOOK) — выделается через
    handle manager (HMPheFromObject)
    - Hook chain — связный список; обход через
    PhkNextValid(phk)
    - Хранятся в THREADINFO — поле THREADINFO+0x210
    указывает на hook chain
    - Desktop-level — глобальные хуки (WH_KEYBOARD_LL,
    WH_MOUSE_LL) через W32GetUserSessionState+0xA588
    (session hook table)
    - DLT_HOOK — Domain Lock Type для синхронизации
    (gDomainHookLock в win32kbase)
    - Locking — Win32HMThreadLock<tagHOOK> /
    Win32HMOptionalThreadLockAlways<tagHOOK> для
    thread-safe доступа
    Диспетчеризация хуков (xxxCallHook2)
    Это центральная функция dispatch — вызывается при
    каждом событии, которое может попасть в хук:
    1. Session state — получает через
    W32GetUserSessionState
    2. Hook type check — tagHOOK+0x30 содержит idHook
    (w23)
    3. UIPI (User Interface Privilege Isolation)
    вызывает:
    - UIPrivilegeIsolation::Enforced() — включена ли
    изоляция
    - UIPrivilegeIsolation::CheckAccess() — может ли
    хук быть вызван
    - _IsProcessDwm() — проверяет DWM (особые
    привилегии)
    - IsRestricted() / IsImmersiveBroker() /
    IsImmersiveAppRestricted()
    - Если UIPI блокирует → EtwTraceUIPIHookError
    4. Hook iteration — проходит цепочку через
    PhkNextValid(), проверяя каждый хук
    5. Module validation — xxxLoadHmodIndex проверяет
    что DLL хука ещё загружена
    6. Callback invocation — xxxHkCallHook() —
    фактический вызов hook procedure
    7. Free if needed — FreeHook() если хук помечен для
    удаления
    Вызов hook callback (xxxHkCallHook
    - IsRestricted() / IsImmersiveBroker() / IsImmersiveAppRestricted()
    - Если UIPI блокирует → EtwTraceUIPIHookError
    4. Hook iteration — проходит цепочку через PhkNextValid(), проверяя каждый хук
    5. Module validation — xxxLoadHmodIndex проверяет что DLL хука ещё загружена
    6. Callback invocation — xxxHkCallHook() — фактический вызов hook procedure
    7. Free if needed — FreeHook() если хук помечен для удаления
    Вызов hook callback (xxxHkCallHook KeUserModeCallback)
    xxxHkCallHook в конечном итоге вызывает KeUserModeCallback для передачи управления
    в usermode. Для каждого типа хука есть свой wrapper:
    - fnHkINLPKBDLLHOOKSTRUCT — WH_KEYBOARD_LL
    - fnHkINLPMSLLHOOKSTRUCT — WH_MOUSE_LL
    - fnHkINLPMOUSEHOOKSTRUCTEX — WH_MOUSE
    - fnHkINLPDEBUGHOOKSTRUCT — WH_DEBUG
    - fnHkINLPCHARHOOKSTRUCT — keyboard char hooks
    Эти wrappers упаковывают данные хука в формат для usermode callback и вызывают
    NtUserCallNextHookEx для продолжения цепочки.
    Unhook
    - NtUserUnhookWindowsHookEx → zzzUnhookWindowsHookEx → UnlinkHook + FreeHook
    - DelayHookFree / FreeDelayedHooks — отложенное освобождение (если хук вызывается
    рекурсивно)
    - DeBoostHook / BoostHook — управление приоритетом потока при вызове хуков
    Устаревший путь
    NtUserSetWindowsHookAW → zzzSetWindowsHookAW / zzzSetWindowsHookAW_New — старый
    API SetWindowsHookA/W (без Ex), почти тот же механизм но без поддержки dwThreadId.


    ⏺ Вот что изменилось в новом пути (zzzSetWindowsHookEx_New):
    Ключевые отличия нового vs старого SetWindowsHookEx
    1. Тип возвращаемого значения

    СтарыйНовый
    СигнатураtagHOOK*
    zzzSetWindowsHookEx(...)
    HHOOK__*
    zzzSetWindowsHookEx_New(...)
    ВозвращаетВнутренний объект ядраПользовательский хэндл напрямую
    В NtUserSetWindowsHookEx:
    - Старый: ldr x19, [x0] — разыменовывает tagHOOK* чтобы достать HHOOK (handle
    хранится в первом поле структуры)
    - Новый: mov x19, x0 — использует возвращаемое значение как есть, без
    разыменования
    Это говорит о том, что новый путь инкапсулирует работу с handle manager внутри
    себя и не выставляет наружу сырой kernel object.
    2. Feature gate
    Включается через Feature_656357688 (Velocity feature flag). На текущем стенде
    featureState = 0x147 — судя по коду в NtUserSetWindowsHookEx, при cbz w0 (feature
    disabled) идёт по старому пути. Фича проверяется через
    IsEnabledDeviceUsageNoInline — значит зависит отEdition/Device configuration.

    3. Дополнительные проверки в новом пути
    Из дизассемблера _New версии видны проверки, которых нет на пути
    NtUserSetWindowsHookEx→zzzSetWindowsHookEx:
    - lpfn (hook procedure) == NULL → ERROR_INVALID_PARAMETER (0x593) — новый путь
    валидирует callback pointer раньше
    - Hook type flags validation — через таблицу по адресу string+0xA64 проверяет биты
    разрешённого типа хука (tbnz/tbz проверки битов 1, 5). Старый путь делегировал
    это в zzzSetWindowsHookEx
    - Global vs Thread hook logic — раздельные ошибки: 0x593 (нет callback), 0x594
    (глобальный хук без hMod), 0x595 (thread hook без прав)
    - WPP tracing — более гранулярная трассировка с разными event ID (0xB, 0xC, 0xD)
    4. SetWindowsHookAW (deprecated API)

    Тоже раздвоен:
    - Старый: zzzSetWindowsHookAW возвращает __int64 (__cdecl*)(void) — адрес hook
    function (deprecated поведение)
    - Новый: zzzSetWindowsHookAW_New возвращает HHOOK__* — корректный handle
    Старый SetWindowsHookA/W (без Ex) исторически возвращал адрес hook procedure
    вместо хэндла — это баг, который сохранялся для обратной совместимости. Новый путь
    это исправляет.
    Итог

    Новое — это рефакторинг/улучшение:
    1. Возвращает HHOOK напрямую вместо сырого kernel object
    2. Ранняя валидация параметров (больше не нужно ждать до внутреннего вызова)
    3. Более точные коды ошибок (разные для каждого случая)
    4. Исправлен deprecated SetWindowsHookAW (теперь тоже возвращает handle)
    5. Закрыто за Velocity feature flag для контролируемого rollout

    ⏺ Из всего ресерча вот что реально интересное:
    1. Win32K split на 4 модуля — это не просто dll, это архитектурное разделение:
    - win32k — тонкие стабы, только dispatch через ApiSet таблицу
    - win32kbase — базовая инфраструктура (handles, locks, UIPI)
    - win32kfull — вся реальная реализация (hooks, windows, menus)
    - win32kbase_rs — Rust код (вероятно новые security checks)
    То есть win32k по сути стал "ntdll для GUI" — пустой прокси.
    2. UIPI в dispatch хуков — xxxCallHook2 это не просто "вызови callback". Перед
    каждым вызовом:
    - UIPrivilegeIsolation::Enforced() → UIPrivilegeIsolation::CheckAccess()
    - _IsProcessDwm() — DWM имеет особые привилегии
    - IsRestricted(), IsImmersiveBroker(), IsImmersiveAppRestricted()
    То есть integrity level проверяется на каждом хуке в цепочке, а не один раз при
    установке. Процесс с низким IL не может перехватить хуки процесса с высоким IL,
    даже если хук global.
    3. Velocity feature flags прямо в ядре — Feature_656357688 — это не compile-time
    switch, это runtime feature flag. featureState = 0x147 — значит ядро может
    включать/выключать кодовые пути без пересборки. Это A/B тестирование на уровне
    kernel.
    4. Delayed free для хуков — DelayHookFree / FreeDelayedHooks. Проблема: hook
    callback может триггерить другое событие, которое пройдёт через тот же hook chain.
    Если удалить хук во время его выполнения — crash. Поэтому хуки помечаются для
    удаления и реально фришатся позже (FreeDelayedHooks). Это тот паттерн, который в
    kernel programming называется "deferred deletion with RCU-like semantics".
    5. Priority boosting — BoostHook / DeBoostHook. Когда хук вызывается, поток
    получает boost приоритета через SetPriorityFloor(tagTHREADINFO,
    tagThreadPriorityFloor). Это значит hook callbacks временно повышают приоритет
    потока, чтобы не быть вытесненными. Low-level hooks (keyboard/mouse) особенно
    критичны — там ещё есть gnllHooksTimeout — если LL hook зависнет больше чем на N
    мс, он пропускается.
    6. Per-session hook isolation — всё через W32GetUserSessionState, а не глобальные
    переменные. Offset 0xA588 в session state — таблица глобальных хуков. Это значит
    каждый RDP/session имеет свой hook table.
    7. Handle Manager для HOOK — tagHOOK создаётся через HM (Handle Manager) —
    HMPheFromObject, HMAssignmentLock. Не просто ExAllocatePool, а полноценный kernel
    object с reference counting. Поэтому HHOOK — это настоящий handle, который можно
    закрыть только через NtUserUnhookWindowsHookEx.
    8. ARM64 security — каждый вызов hook callback проходит через
    KscpCfgCheckUserCallTargetEs — это CET (Control-flow Enforcement Technology) для
    indirect calls в ядре. Даже kernel-mode code не может просто прыгнуть на
    произвольный адрес.
    Самое практичное для понимания — это UIPI. Если пишешь security tool на хуках,
    нужно понимать что хуки фильтруются по integrity level на этапе dispatch, а не
    установки. SetWindowsHookEx может "успешно" установить хук, но он просто не будет
    вызываться для процессов с более высоким IL.

    стенд:
    Хост: macOS на Apple Silicon (Darwin 25.5.0)
    Две VM Parallels Desktop ARM64:
    VMРольСтатус
    Windows 11 Pro (Debugger)Отладчик (WinDbg)running
    Windows 11 Pro (Target)Цель отладкиrunning
    ОС: Windows 11 Build 26100 (24H2), ARM64 (AArch64), 4 vCPU, build lab
    ge_release.240331-1435
    Подключение:
    - Serial port через сокеты Parallels → socat relay на macOS → WinDbg на Debugger
    VM
    - WinDbg MCP agent (windbg_agent.dll) загружен в WinDbg, MCP сервер на
    10.211.55.5:44444
    - Claude Code подключается к MCP серверу по HTTP
     
    Последнее редактирование модератором: 3 июн 2026 в 13:08
  2. Research

    Research Active Member

    Публикаций:
    1
    Регистрация:
    6 янв 2024
    Сообщения:
    553
    Интересно как работает CreateRemoteThread
     
    Последнее редактирование: 2 июн 2026 в 12:50
  3. galenkane

    galenkane Active Member

    Публикаций:
    4
    Регистрация:
    13 янв 2017
    Сообщения:
    475
    CreateRemoteThread — Исследование внутренних механизмов ядра Windows

    Введение
    Код (Text):
    1. kernel32!CreateRemoteThread → ntdll!NtCreateThreadEx → nt!NtCreateThreadEx → nt!PspCreateThread → nt!PspAllocateThread + nt!PspInsertThread → nt!KeStartThread
    В данном исследовании рассматривается полный путь создания удалённого потока через CreateRemoteThread от usermode до планировщика ядра. Исследование проведено на Windows 11 Build 26100 (ARM64) с помощью WinDbg kernel debugging.

    1. kernel32!CreateRemoteThread
    Функция CreateRemoteThread — это thin wrapper, который подготавливает параметры и вызывает NtCreateThreadEx:
    Код (Text):
    1. CreateRemoteThread(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId)
    2.   → NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, lpStartAddress, lpParameter, FALSE, 0, dwStackSize, NULL)

    2. nt!NtCreateThreadEx (0xFFFFF801`B4AE7480)
    Точка входа syscall. Функция выделяет локальный контекст CREATE_THREAD_CONTEXT (0x1F0 байт), заполняет его и вызывает PspCreateThread.
    Ключевой дизассемблерный поток:
    Код (Text):
    1. nt!NtCreateThreadEx:
    2.   PACIBSP                              ; ← ARM64 PAC security cookie
    3.   memset(context, 0, 0x1F0)            ; Zero CREATE_THREAD_CONTEXT
    4.   ; Если передан хэндл процесса — построить контекст создания
    5.   if (hProcess != NULL)
    6.     → PspBuildCreateProcessContext(hProcess, 1, context, ...)
    7.   ; Получить ссылку на объект процесса по хэндлу
    8.   ObpReferenceObjectByHandleWithTag(
    9.     hProcess,
    10.     PROCESS_THREAD_INJECTION,          ; ← Требуемый доступ: THREAD_INJECTION
    11.     'CrP',                             ; ← Tag: 0x72437350 (object tag)
    12.     &EPROCESS
    13.   )
    14.   ; Проверка: тот же процесс или межпроцессный вызов?
    15.   if (EPROCESS+0x168 bit 0 == 0)       ; Флаг ProtectedProcess не установлен
    16.     → cross-process path
    17.   ; Подготовка расширенного контекста (ARM64 specific)
    18.   RtlGetExtendedContextLength2(...)
    19.   RtlInitializeExtendedContext2(...)
    20.   ; Основной вызов — создаём поток
    21.   PspCreateThread(
    22.     &ThreadHandle,
    23.     AccessMask,
    24.     StartRoutine,
    25.     ProcessHandle,
    26.     context,
    27.     ...
    28.   )
    29.   PspDeleteCreateProcessContext(context)
    30.   AUTIBSP
    31.   ret
    Ключевые моменты:
    • Tag объекта: 'CrP' (0x72437350) — используется для отслеживания ссылок на процесс при создании потока
    • Требуемый доступ: PROCESS_THREAD_INJECTION — для межпроцессного создания потока
    • Проверка EPROCESS+0x168 bit 0: если установлен — это Protected Process, и путь меняется

    3. nt!PspCreateThread (0xFFFFF801`B4ADBD60)
    Центральная функция создания потока. Получает ссылку на EPROCESS, проверяет готовность процесса, выделяет защиту от rundown, и вызывает PspAllocateThread + PspInsertThread.
    Код (Text):
    1. nt!PspCreateThread:
    2.   PACIBSP
    3.   memset(local_ctx, 0, 0x190)
    4.   ; Получаем EPROCESS по хэндлу
    5.   ObpReferenceObjectByHandleWithTag(hProcess, ..., 'CrP', &EPROCESS)
    6.   EPROCESS+0x168 → x21 = Flags (bit 0 = ProtectedProcess)
    7.   ; Межпроцессная проверка
    8.   if (ProcessHandle != NULL && EPROCESS != PsGetCurrentProcess)
    9.     PspIsProcessReadyForRemoteThread(EPROCESS)
    10.   ; Парсинг контекста создания
    11.   if (CreateContext != NULL)
    12.     check EPROCESS+0x6BC flags (bit 0, bit 12)
    13.     Parse context flags (bits 0-6) → setup local flags
    14.     Various flag combinations (1, 2, 4, 0x80)
    15.   ; Захват Rundown Protection
    16.   ExAcquireRundownProtection(EPROCESS+0x1D8)
    17.   ObfReferenceObjectWithTag(EPROCESS)
    18.   ; Выделение и инициализация ETHREAD
    19.   PspAllocateThread(EPROCESS, PreviousMode, CrossProcessFlags, ...)
    20.   ; Вставка потока в процесс
    21.   PspInsertThread(EPROCESS, Thread, CreateContext, ...)
    22.   ; Освобождение
    23.   ExReleaseRundownProtection
    24.   KeLeaveCriticalRegionThread

    4. nt!PspIsProcessReadyForRemoteThread (0xFFFFF801`B44F06E8)
    Проверяет, готов ли целевой процесс принять удалённый поток.
    Код (Text):
    1. nt!PspIsProcessReadyForRemoteThread:
    2.   ; Проверка EPROCESS+0x6BC
    3.   ldr w8, [x0, #0x6BC]
    4.   if (w8 bit 0 || w8 bit 12) → return 1  ; Ready
    5.   ; Проверка EPROCESS+0x168
    6.   ldr w8, [x0, #0x168]
    7.   if (w8 bit 0) → return 1               ; Protected process — ready
    8.   return 0                                ; Not ready
    Значения флагов:
    • EPROCESS+0x6BC bit 0: Process is fully initialized
    • EPROCESS+0x6BC bit 12: Process allows cross-process thread creation
    • EPROCESS+0x168 bit 0: ProtectedProcess flag

    5. nt!PspAllocateThread (0xFFFFF801`B4AEA760)
    Самая объёмная функция — выделяет память под ETHREAD, инициализирует все поля, создаёт стек и TEB.
    5.1 Инициализация и валидация
    Код (Text):
    1. nt!PspAllocateThread:
    2.   PACIBSP
    3.   sub sp, sp, #0x1E0                    ; Большой локальный кадр
    4.   ; Сохранение параметров в контекст
    5.   x21 = EPROCESS
    6.   w20 = PreviousMode (Kernel/User)
    7.   x19 = StartAddress
    8.   ; Проверка EPROCESS+0x168 bit 0 — флаг ProtectedProcess
    9.   ldr x8, [x21, #0x168]
    10.   and x8, x8, #1
    11.   strb w8, [local_ctx]                  ; SameProcessFlag
    12.   ; Валидация PS_CREATE_INFO
    13.   if (CreateInfo != NULL)
    14.     ldr x10, [CreateInfo+8]             ; Flags
    15.     if (Flags bit 12 == 0)
    16.       x1 = CreateInfo+0x140             ; Attribute list
    17.     if (Flags bit 14 == 0)
    18.       ; Загрузка debug register context
    19.     ; Проверка совместимости процессора
    20.     ldrb w8, [ProcessorCtx+0x788]       ; Processor group
    21.     ldrh w9, [AttrList+8]
    22.     cmp w8, w9
    23.     bne → STATUS_NOT_SUPPORTED
    5.2 Выбор процессора
    Код (Text):
    1.   ; Выбор идеального процессора для потока
    2.   KeSelectInitialIdealProcessorForThread(EPROCESS)
    3.   uxth w0, w0                            ; Processor number
    4.   str w0, [CreateInfo+0x14]             ; Save to context
    5.   ; Установка флага processor assigned
    6.   ldr x8, [CreateInfo+8]
    7.   orr x8, x8, #0x4000
    8.   str x8, [CreateInfo+8]
    9.   ; Получение NUMA-узла
    10.   KeGetProcessorNodeNumberByIndex(...)
    11.   add w24, w8, #1                        ; Node+1
    5.3 Создание объекта ETHREAD
    Код (Text):
    1.   ; Вычисление размера ETHREAD с учётом групп процессоров
    2.   KeQueryMaximumGroupCount()
    3.   if (groups > 1)
    4.     ; Дополнительное выравнивание для multi-group
    5.   w5 = total ETHREAD size
    6.   ; Создание объекта через Object Manager
    7.   ObCreateObjectEx(
    8.     PreviousMode,
    9.     NULL,                                ; ObjectType (Thread)
    10.     ObjectAttributes,
    11.     PreviousMode,
    12.     NULL,
    13.     ETHREAD_SIZE,
    14.     0, 0,
    15.     &ETHREAD
    16.   )
    17.   ; Обнуление ETHREAD
    18.   _memset_spec_unaligned_zva(ETHREAD, 0, ETHREAD_SIZE)
    5.4 Инициализация полей ETHREAD
    Код (Text):
    1.   ; Энергопотребление
    2.   PoEnergyEstimationEnabled()
    3.   if (enabled)
    4.     ETHREAD+0x778 = EnergyEstimationBuffer
    5.   ; Подсчёт групп и размеров
    6.   KeQueryMaximumGroupCount()
    7.   ; Расчёт Offsets: GroupAffinity, IdealProcessor, ...
    8.   ; Инициализация списков ETHREAD
    9.   ETHREAD+0x490 → first list head
    10.   ETHREAD+0x498 → second list head
    11.   ETHREAD+0x248 → group affinity array
    12.   ETHREAD+0x260 → ideal processors array
    13.   ; Копирование контекста процесса
    14.   ETHREAD+0x4E8 = EPROCESS+0x1C0         ; Process reference
    15.   ETHREAD+0x4C0 = StartAddress
    16.   ETHREAD+0x540 = StartAddress
    17.   ; Инициализация синхронизации
    18.   KeInitializeSemaphore(ETHREAD+0x4F8, 0, 1)  ; LpcReplySemaphore
    19.   ; Списки: ETHREAD+0x4A8, 0x5D0, 0x5E0, 0x638, 0x520
    20.   ; Timer: ETHREAD+0xF8
    21.   ETHREAD+0x678 = -3                      ; Initial TID sentinel
    22.   ; Время создания
    23.   KeQuerySystemTimePrecise(ETHREAD+0x4A0)
    24.   ; Присвоение Thread ID
    25.   PsAssignThreadId(ETHREAD)
    5.5 Создание пользовательского стека
    Код (Text):
    1.   ; Для пользовательского потока (PreviousMode == User)
    2.   if (EPROCESS+0x300 != NULL)           ; WoW64 process
    3.     PspSetupUserStack(EPROCESS, NULL, ..., local_ctx+0x108)
    4.     → RtlpWow64CreateUserStack(...)
    5.   else
    6.     PspSetupUserStack(EPROCESS, StartCtx, ..., local_ctx+0x108)
    7.     → RtlCreateUserStack(...)
    8.   ; PspSetupUserStack:
    9.   ;   KiStackAttachProcess()              ; Привязка к адресному пространству
    10.   ;   RtlCreateUserStack(...)             ; Выделение стека в usermode
    11.   ;   ExGenRandom()                       ; Рандомизация стека (ASLR)
    12.   ;   KiUnstackDetachProcess()            ; Отключение от address space
    5.6 Создание TEB
    Код (Text):
    1.   ; Создание Thread Environment Block
    2.   MmCreateTeb(
    3.     EPROCESS,
    4.     NULL,                                ; Start context (NULL for remote)
    5.     ETHREAD+0x4E8,                       ; Process reference
    6.     local_ctx+0x10                       ; Thread context
    7.   )
    8.   ; TEB размещается в пользовательском адресном пространстве
    9.   ; Инициализация WoW64 (если процесс WoW64)
    10.   PspWow64InitThread(EPROCESS, ...)
    11.   ; ARM64 EC (Emulation Compatible) инициализация
    12.   PsGetProcessMachine()                  ; Проверка архитектуры
    13.   if (machine != IMAGE_FILE_MACHINE_ARM64 && !WoW64)
    14.     PspArm64EcInitThread(EPROCESS, ETHREAD, ...)
    5.7 Подготовка контекста запуска и вызов KeInitThread
    Код (Text):
    1.   ; Формирование контекста для KeInitThread
    2.   ; Normal user thread path:
    3.   local_ctx+0xC8 = {0, PspUserThreadStartup}   ; KernelRoutine, RundownRoutine
    4.   local_ctx+0xD8 = {StartAddress, TEB}          ; StartAddress, Teb
    5.   local_ctx+0xE8 = {CreateInfo, AffinityCtx}
    6.   local_ctx+0xF8 = EPROCESS
    7.   local_ctx+0x100 = {NodeNumber, 0}
    8.   ; System thread path (x25 == NULL):
    9.   local_ctx+0xC8 = {0, PspSystemThreadStartup}
    10.   local_ctx+0xD8 = {StartRoutine, NULL}
    11.   ; Вызов KeInitThread — финальная инициализация KTHREAD
    12.   KeInitThread(ETHREAD, local_ctx+0xC8)

    6. nt!KeInitThread (0xFFFFF801`B4CA7CA8)
    Инициализирует KTHREAD (встроенный в ETHREAD), устанавливает APC, создаёт стек ядра.
    Код (Text):
    1. nt!KeInitThread:
    2.   ETHREAD = x19
    3.   Context = x20
    4.   ; Инициализация списков ожидания
    5.   ETHREAD+0x8 → WaitListEntry (self-linked)
    6.   ETHREAD+0x328 → TimerWaitBlock (self-linked)
    7.   ; Инициализация mutex列表
    8.   for (i = 0; i < 4; i++)
    9.     ETHREAD+0x150+i*0x30 → Mutex links
    10.   ; Настройка планирования
    11.   KPROCESS = Context+0x30                 ; Process KPROCESS pointer
    12.   ETHREAD+0x70 = Thread flags (xor with KPROCESS scheduling)
    13.   ETHREAD+0x54 = Quantum
    14.   ; Wait list и APC list
    15.   ETHREAD+0x90 → WaitList (self-linked)
    16.   ETHREAD+0xA0 → QueueList (self-linked)
    17.   ETHREAD+0xB0 = KPROCESS
    18.   ETHREAD+0x240 = KPROCESS               ; Process reference
    19.   ; Thread state flags
    20.   ETHREAD+0x6C = flags (possibly OR 0x4000 for wr-fasting)
    21.   ; Инициализация APC (КРИТИЧЕСКИЙ ШАГ)
    22.   KeInitializeApc(
    23.     ETHREAD+0x2A8,                       ; ← APC structure at offset 0x2A8
    24.     ETHREAD,                             ; Thread object
    25.     0,                                    ; No rundown routine
    26.     KiSchedulerAccountingRoutine,         ; KernelRoutine
    27.     NULL,                                 ; RundownRoutine
    28.     PspUserThreadStartup,                 ; ← NormalRoutine (USER APC!)
    29.     0,                                    ; ApcModeIndex
    30.     NULL                                  ; NormalContext
    31.   )
    32.   ; Инициализация события завершения
    33.   KeInitializeEvent(ETHREAD+0x300, NotificationEvent, FALSE)
    34.   ; Инициализация таймера
    35.   KeInitializeTimer(ETHREAD+0xF8)
    36.   ; Подключение к timer списку ETHREAD+0x1C8
    37.   ; Хеширование timer ID
    38.   ; Создание стека ядра
    39.   MmCreateKernelStack(stackParams)
    40.   ETHREAD+0x38 = KernelStack
    41.   ETHREAD+0x28 = {StackBase, StackLimit}
    42.   ; Инициализация Anti-Boost
    43.   KeAbInitializeThreadState(ETHREAD)
    44.   ETHREAD+0x390 = 1
    45.   ETHREAD+0x410 = 1
    46.   ; Thread state = Initialized (6)
    47.   stlrb w8=6, [ETHREAD+0x0]              ; KTHREAD.State = Initialized
    48.   ; Получение идеального NUMA-узла
    49.   KiGetIdealNodeProcessByGroup(KPROCESS)
    50.   ret STATUS_SUCCESS
    Значение APC при offset 0x2A8:
    APC инициализируется с NormalRoutine = PspUserThreadStartup. Когда поток впервые начнёт выполнение, этот APC будет доставлен, и PspUserThreadStartup выполнится в контексте нового потока.

    7. nt!PspInsertThread (0xFFFFF801`B4AEEDE0)
    Вставляет созданный ETHREAD в список потоков процесса и запускает его через KeStartThread.
    7.1 Захват блокировок
    Код (Text):
    1. nt!PspInsertThread:
    2.   ; Acquire process Push Lock (exclusive)
    3.   ExAcquirePushLockExclusive(EPROCESS+0x1B8)
    4.   ; Acquire parent process resource (shared) если есть parentCreateInfo
    5.   if (ParentCreateInfo != NULL)
    6.     ExAcquireResourceSharedLite(EPROCESS+0x290+0x38, TRUE)
    7.2 Проверки состояния процесса
    Код (Text):
    1.   ; Проверка EPROCESS+0x1E4 флагов
    2.   ldr w8, [EPROCESS+0x1E4]
    3.   if (w8 bit 0x1A == 0)                  ; Process not exiting
    4.     if (w8 bit 3 == 0)                   ; Process not in delete
    5.       if (w8 bit 0x1E == 0)              ; Not process snapshot
    6.         → proceed to start thread
    7.   ; Проверка ProtectedProcess (EPROCESS+0x168 bit 0)
    8.   ldr x8, [EPROCESS+0x168]
    9.   if (w8 bit 0) → cross-process injection blocked
    10.   ; Если проверки не пройдены → STATUS_PROCESS_IS_TERMINATING
    7.3 Запуск потока
    Код (Text):
    1.   ; Вызов KeStartThread — поток начинает выполнение
    2.   KeStartThread(ETHREAD, SchedulingContext, ...)
    3.   ; Обновление счётчика потоков процесса
    4.   EPROCESS+0x370 += 1                    ; ThreadCount
    5.   if (ThreadCount > EPROCESS+0x698)      ; MaxThreads
    6.     → track high water mark
    7.   ; Освобождение блокировок
    8.   ExReleaseResourceLite(...)
    9.   ReleasePushLock(EPROCESS+0x1B8)
    10.   ReleaseRundownProtection(EPROCESS+0x1D8)

    8. nt!KeStartThread (0xFFFFF801`B4466BE8)
    Финальная стадия — поток добавляется в планировщик. Выполняется на DPC уровне.
    Код (Text):
    1. nt!KeStartThread:
    2.   ; Получить KPROCESS из ETHREAD
    3.   x20 = ETHREAD+0xB0                     ; KPROCESS pointer
    4.   ; Raise IRQL → DPC level
    5.   KfRaiseIrql()
    6.   w26 = old IRQL
    7.   ; Acquire process spinlock
    8.   ExAcquireSpinLockExclusiveAtDpcLevel(KPROCESS+0x48)
    9.   ; Настройка идеального процессора
    10.   KiAdjustProcessIdealProcessorSetsForThreadCreation(KPROCESS)
    11.   ; Копирование scheduling parameters из KPROCESS
    12.   ldr w8, [KPROCESS+0x90]               ; Processor affinity
    13.   ldr w9, [ETHREAD+0x70]                ; Thread flags
    14.   lsr w8, w8, #1                         ; Divide affinity
    15.   eor w8, w9, w8, lsl #3
    16.   and w8, w8, #8
    17.   eor w8, w8, w9
    18.   str w8, [ETHREAD+0x70]                 ; Update thread flags
    19.   ; Копирование processor group
    20.   ldrsb w8, [KPROCESS+0x98]             ; Ideal processor
    21.   strb w8, [ETHREAD+0x253]              ; Thread ideal processor
    22.   strb w8, [ETHREAD+0xBB]               ; Current ideal processor
    23.   mov w8, #0x20
    24.   strb w8, [ETHREAD+0x33B]              ; System thread ideal
    25.   ; Проверка affinity
    26.   if (AffinityOverride != NULL)
    27.     KeIsEmptyAffinityEx(override)
    28.     if (empty) → use process affinity
    29.     KeIsSubsetAffinityEx(override, process_affinity)
    30.     if (!subset)
    31.       KiExtendProcessAffinity(KPROCESS, override)
    32.   ; Определение целевого процессора
    33.   if (KPROCESS == current_process)
    34.     ldrh w24, [ETHREAD+0x268]           ; Ideal group
    35.   else
    36.     ldrh w24, [KPROCESS+0x190]          ; Process group
    37.   ; Распределение по группам
    38.   for each processor in group:
    39.     copy affinity bits to local array
    40.   ; Добавление в ready queue
    41.   if (KPROCESS has active threads)
    42.     KiUpdateSharedReadyQueueAffinityThread(...)
    43.   ; Копирование scheduling group
    44.   ETHREAD+0x60 = KPROCESS+0x138         ; Scheduling group
    45.   ; Обновление счётчика активных потоков
    46.   ldaddal w8=8, [KPROCESS+0x110]        ; ActiveThreadCount += 8
    47.   ; Освобождение spinlock
    48.   ExReleaseSpinLockExclusiveFromDpcLevel(KPROCESS+0x48)
    49.   KfLowerIrql(oldIrql)
    50.   ; ETW трассировка
    51.   EtwTraceThreadAffinity(ETHREAD, group, ...)
    52.   EtwTraceIdealProcessor(ETHREAD, ...)
    53.   ; Снятие блокировки
    54.   ldaddal w8=8, [KPROCESS+0x110]        ; Thread started
    55.   ret

    9. nt!PspUserThreadStartup (0xFFFFF801`B4AF12C0)
    Kernel APC routine — выполняется когда поток впервые получает процессорное время. Это точка входа нового потока в контексте ядра.
    Код (Text):
    1. nt!PspUserThreadStartup:
    2.   ; Сброс IRQL
    3.   KfLowerIrql(0)
    4.   ; Получение текущего ETHREAD
    5.   x19 = ETHREAD (current thread)
    6.   ; Отключение обмена primary token (security)
    7.   PspDisablePrimaryTokenExchange(ETHREAD)
    8.   ; Проверка: процесс завершается?
    9.   ldr w8, [ETHREAD+0x580]               ; Thread flags
    10.   if (w8 bit 1)                          ; Thread is terminating
    11.     → PspTerminateThreadByPointer(ETHREAD, STATUS_THREAD_IS_TERMINATING, TRUE)
    12.   ; Проверка EPROCESS+0x6BC (process ready)
    13.   ldr x8, [ETHREAD+0xB0]                ; KPROCESS
    14.   ldr w8, [KPROCESS+0x6BC]
    15.   if (w8 bit 0)                          ; Process fully initialized
    16.     → DbgkCreateMinimalThread(ETHREAD)   ; Debug support
    17.     msr TPIDR_EL0, x8                   ; Set TLS register
    18.   ; Обновление TEB scheduling properties
    19.   KeUpdateTebSchedulingPropertiesCurrentThread(ETHREAD)
    20.   ; Уведомление о создании потока (callbacks, ETW)
    21.   PspNotifyThreadCreation(ETHREAD)
    22.   ; Проверка ready flag
    23.   ldr w8, [ETHREAD+0x580]
    24.   if (w8 bit 0)                          ; Need to wait
    25.     → KeWaitForSingleObject(ETHREAD, ...) ; Wait for process initialization
    26.   ; Проверка EPROCESS+0x6BC
    27.   ldr w8, [EPROCESS+0x6BC]
    28.   if (w8 bit 0 == 0)
    29.     → PspInitializeThunkContext()        ; Настройка usermode контекста
    30.   ; Возврат — поток переходит в usermode
    31.   AUTIBSP
    32.   ret
    Что происходит в usermode:
    Когда PspUserThreadStartup завершается, поток переходит в usermode и начинает выполнение с:
    • ntdll!LdrInitializeThunk — точка входа в usermode
    • LdrpInitializeProcess (для первого потока) или LdrpInitializeThread (для последующих)
    • Инициализация CRT, TLS, загрузка DLL
    • Вызов ThreadStartRoutine (переданный через CreateRemoteThread)

    10. Полная диаграмма потока вызовов
    Код (Text):
    1.  
    2. kernel32!CreateRemoteThread
    3.   │
    4.   ▼
    5. ntdll!NtCreateThreadEx  (syscall)
    6.   │
    7.   ▼
    8. nt!NtCreateThreadEx  (0xFFFFF801`B4AE7480)
    9.   ├─ PspBuildCreateProcessContext()
    10.   ├─ ObpReferenceObjectByHandleWithTag('CrP')
    11.   ├─ RtlGetExtendedContextLength2()
    12.   ├─ RtlInitializeExtendedContext2()
    13.   │
    14.   ▼
    15. nt!PspCreateThread  (0xFFFFF801`B4ADBD60)
    16.   ├─ ObpReferenceObjectByHandleWithTag('CrP') → EPROCESS
    17.   ├─ PspIsProcessReadyForRemoteThread()
    18.   │   ├─ EPROCESS+0x6BC bit 0 || bit 12?
    19.   │   └─ EPROCESS+0x168 bit 0?
    20.   ├─ ExAcquireRundownProtection(EPROCESS+0x1D8)
    21.   │
    22.   ├─► nt!PspAllocateThread  (0xFFFFF801`B4AEA760)
    23.   │   ├─ KeSelectInitialIdealProcessorForThread()
    24.   │   ├─ KeGetProcessorNodeNumberByIndex()
    25.   │   ├─ ObCreateObjectEx() → ETHREAD
    26.   │   ├─ KeInitializeSemaphore(ETHREAD+0x4F8)
    27.   │   ├─ PsAssignThreadId()
    28.   │   ├─ PspSetupUserStack()
    29.   │   │   ├─ KiStackAttachProcess()
    30.   │   │   ├─ RtlCreateUserStack()
    31.   │   │   ├─ ExGenRandom() (ASLR)
    32.   │   │   └─ KiUnstackDetachProcess()
    33.   │   ├─ MmCreateTeb()
    34.   │   ├─ PspWow64InitThread() / PspArm64EcInitThread()
    35.   │   └─ KeInitThread()
    36.   │       ├─ KeInitializeApc(ETHREAD+0x2A8, NormalRoutine=PspUserThreadStartup)
    37.   │       ├─ KeInitializeEvent(ETHREAD+0x300)
    38.   │       ├─ KeInitializeTimer(ETHREAD+0xF8)
    39.   │       └─ MmCreateKernelStack()
    40.   │
    41.   └─► nt!PspInsertThread  (0xFFFFF801`B4AEEDE0)
    42.       ├─ ExAcquirePushLockExclusive(EPROCESS+0x1B8)
    43.       ├─ ExAcquireResourceSharedLite(EPROCESS+0x290+0x38)
    44.       ├─ Check EPROCESS+0x1E4 flags
    45.       ├─ Check EPROCESS+0x168 (ProtectedProcess)
    46.       │
    47.       └─► nt!KeStartThread  (0xFFFFF801`B4466BE8)
    48.           ├─ KfRaiseIrql(DPC)
    49.           ├─ ExAcquireSpinLockExclusiveAtDpcLevel(KPROCESS+0x48)
    50.           ├─ KiAdjustProcessIdealProcessorSetsForThreadCreation()
    51.           ├─ Setup processor affinity from KPROCESS
    52.           ├─ KiUpdateSharedReadyQueueAffinityThread()
    53.           ├─ ExReleaseSpinLockExclusiveFromDpcLevel()
    54.           ├─ KfLowerIrql()
    55.           └─ EtwTraceThreadAffinity()
    56.           └─ Release locks
    57.           └─ Release rundown protection
    58. ═════════════════════════════════════════
    59.        Поток получает процессор
    60. ═════════════════════════════════════════
    61.           │
    62.           ▼
    63. nt!PspUserThreadStartup  (Kernel APC)
    64.   ├─ KfLowerIrql(0)
    65.   ├─ PspDisablePrimaryTokenExchange()
    66.   ├─ DbgkCreateMinimalThread()
    67.   ├─ KeUpdateTebSchedulingPropertiesCurrentThread()
    68.   ├─ PspNotifyThreadCreation()
    69.   └─ PspInitializeThunkContext()
    70.           │
    71.           ▼ [Transition to User Mode]
    72.           │
    73. ntdll!LdrInitializeThunk
    74.   ├─ LdrpInitializeThread()
    75.   ├─ CRT initialization
    76.   ├─ DLL notifications
    77.   └─ UserThreadStart(lpStartAddress, lpParameter)
    78.  

    11. Структуры и смещения
    EPROCESS offsets:

    Код (Text):
    1.  
    2. +0x0B0  KPROCESS pointer (embedded)
    3. +0x168  Flags (bit 0 = ProtectedProcess / SameProcess)
    4. +0x1B8  Push Lock (thread list, exclusive for insert)
    5. +0x1C0  Process reference
    6. +0x1D8  Rundown Protection
    7. +0x1E0  Additional flags
    8. +0x1E4  Process state flags (bit 3 = deleting, bit 0x1A = exiting, bit 0x1E = snapshot)
    9. +0x290  Active process links
    10. +0x300  WoW64 process structure
    11. +0x370  ThreadCount
    12. +0x580  Thread creation flags
    13. +0x698  MaxThreads (high water mark)
    14. +0x6BC  Process ready flags (bit 0 = initialized, bit 12 = allows remote threads)
    15. +0x700  Process extension
    16. +0x7AC  Process machine type
    17. +0x810  Process flags (bit 6 = stack randomization)
    18.  
    ETHREAD offsets:
    Код (Text):
    1.  
    2. +0x000  KTHREAD.State (6 = Initialized, 1 = Ready, 2 = Running)
    3. +0x008  WaitListEntry
    4. +0x028  KernelStack {Base, Limit}
    5. +0x038  KernelStack pointer
    6. +0x054  Quantum
    7. +0x060  Scheduling group
    8. +0x06C  Thread flags (bit 0xA = wr-fasting, bit 0x14 = created suspended)
    9. +0x070  Cross-process flags
    10. +0x088  Thread flags from process
    11. +0x090  WaitList
    12. +0x0A0  QueueList
    13. +0x0B0  KPROCESS pointer
    14. +0x0BB  Current ideal processor
    15. +0x0E8  Win32Thread / TEB pointer
    16. +0x0F8  Timer
    17. +0x150  Mutex links (4 entries)
    18. +0x17E  IdealNode count
    19. +0x1C8  Timer list entry
    20. +0x240  KPROCESS (second reference)
    21. +0x248  GroupAffinity array pointer
    22. +0x253  Ideal processor
    23. +0x260  IdealProcessor array pointer
    24. +0x268  Ideal group
    25. +0x290  ActiveProcessLinks
    26. +0x2A8  KAPC structure (initial user APC)
    27. +0x300  Event (thread termination)
    28. +0x328  TimerWaitBlock
    29. +0x33B  System ideal processor
    30. +0x390  Anti-Boost flag 1
    31. +0x410  Anti-Boost flag 2
    32. +0x424  Unknown field (0x20)
    33. +0x490  List head 1
    34. +0x498  List head 2
    35. +0x4A0  CreateTime
    36. +0x4A8  LpcReplyChain
    37. +0x4C0  StartAddress / Win32StartAddress
    38. +0x4D0  List head
    39. +0x4D8  List head
    40. +0x4E8  Process reference (from EPROCESS+0x1C0)
    41. +0x4F8  LpcReplySemaphore
    42. +0x520  IrpList
    43. +0x540  StartAddress (copy)
    44. +0x568  Unknown pointer
    45. +0x570  Push lock (thread level)
    46. +0x578  Unknown (0xF)
    47. +0x580  Thread flags (bit 1 = terminating, bit 0 = wait for init)
    48. +0x584  Additional flags (bit 0x200 = CFG)
    49. +0x5D0  Callback list
    50. +0x5E0  Callback list
    51. +0x5F0  Unknown
    52. +0x5F8  Unknown
    53. +0x608  Stack pointer from CreateInfo
    54. +0x638  List head
    55. +0x658  WoW64 context pointer
    56. +0x660  WoW64 context pointer 2
    57. +0x668  Energy estimation buffer pointer
    58. +0x670  GroupAffinity extended
    59. +0x678  Thread ID sentinel (-3)
    60. +0x698  High water mark
    61. +0x6A8  Unknown list
    62. +0x6B0  Unknown list
    63. +0x700  Thread extension
    64. +0x707  Priority (0xFF)
    65. +0x758  Unknown pointer
    66. +0x770  Fast reference (process)
    67.  
    KPROCESS offsets:
    Код (Text):
    1.  
    2. +0x000  Flags
    3. +0x048  Process lock (spinlock)
    4. +0x04C  Affinity
    5. +0x058  GroupAffinity array
    6. +0x090  Scheduling affinity
    7. +0x098  Ideal processor
    8. +0x0B0  KPROCESS pointer (self)
    9. +0x110  ActiveThreadCount
    10. +0x118  ThreadListHead
    11. +0x138  Scheduling group
    12. +0x158  Some flags
    13. +0x188  Additional scheduling
    14. +0x190  Default processor group
    15. +0x2D8  Affinity context
    16.  

    12. Механизмы безопасности
    12.1 Защита от инъекции в Protected Processes

    Код (Text):
    1.  
    2. NtCreateThreadEx:
    3.   → ObpReferenceObjectByHandleWithTag(PROCESS_THREAD_INJECTION)
    4.     ; Требуется право THREAD_INJECTION для целевого процесса
    5. PspCreateThread:
    6.   → PspIsProcessReadyForRemoteThread()
    7.     ; Проверяет EPROCESS+0x6BC и EPROCESS+0x168
    8.     ; Protected processes (bit 0 at +0x168) могут отклонять инъекцию
    9. PspInsertThread:
    10.   → Проверка EPROCESS+0x168 bit 0 (ProtectedProcess)
    11.     ; Если целевой процесс Protected — инъекция блокируется
    12.   → Проверка EPROCESS+0x1E4 флагов
    13.     ; Bit 3: process deleting
    14.     ; Bit 0x1A: process exiting
    15.     ; Bit 0x1E: process snapshot
    12.2 Rundown Protection
    Код (Text):
    1.  
    2. PspCreateThread:
    3.   ExAcquireRundownProtection(EPROCESS+0x1D8)
    4.   ; Защищает от уничтожения процесса во время создания потока
    5.   ...
    6.   ExReleaseRundownProtection(EPROCESS+0x1D8)
    12.3 Push Lock для списка потоков
    Код (Text):
    1.  
    2. PspInsertThread:
    3.   ExAcquirePushLockExclusive(EPROCESS+0x1B8)
    4.   ; Гарантирует атомарность добавления потока в список
    5.   ...
    6.   ReleasePushLock(EPROCESS+0x1B8)
    12.4 PAC (Pointer Authentication Codes) на ARM64
    Все функции начинаются с PACIBSP и заканчиваются AUTIBSP — это ARM64 Pointer Authentication, защищающая return address от перезаписи.

    13. Практические выводы
    1. CreateRemoteThread требует PROCESS_THREAD_INJECTION на целевом процессе
    2. Внутри ядра используется tag 'CrP' (0x72437350) для отслеживания объектных ссылок
    3. Перед созданием потока ядро проверяет PspIsProcessReadyForRemoteThread — процесс может быть "не готов"
    4. Защита Rundown Protection предотвращает разрушение процесса во время создания потока
    5. Push Lock на EPROCESS+0x1B8 обеспечивает целостность списка потоков
    6. Для пользовательского потока ядро создаёт стек (RtlCreateUserStack), TEB (MmCreateTeb), APC (KeInitializeApc)
    7. Начальная доставка APC через PspUserThreadStartup — первый код, выполняющийся в новом потоке
    8. Финальный переход в usermode: PspUserThreadStartup → PspInitializeThunkContext → ntdll!LdrInitializeThunk → ThreadStartRoutine
    9. KeStartThread работает на DPC level с spinlock на KPROCESS+0x48 для настройки affinity
    10. ASLR для стека обеспечивается через ExGenRandom в PspSetupUserStack
    11. Защита от инъекции в PPL (Protected Process Light): проверка EPROCESS+0x168 bit 0 и EPROCESS+0x1E4 флагов

    Исследование выполнено на Windows 11 Build 26100 ARM64 через WinDbg kernel debugging. Все адреса и смещения актуальны для данной сборки.
     
    Mikl___ нравится это.