Разбираем самую «невидимую» технику среди всех известных инъекций.
Пожалуй, самый непредсказуемый трюк, на который способен современный малварь, — появиться в памяти процесса без единого файла на диске и без участия стандартного загрузчика Windows. Именно так ведёт себя Reflective DLL Injection. С момента появления в 2008 году приём стал неотъемлемой частью арсенала красных команд: библиотека «оживает» прямо в ОЗУ, а антивирусы и EDR нередко узнают об этом слишком поздно. В статье разберём историю, архитектуру, популярные инструменты и, главное, практические методы защиты.
Чтобы оценить изящество Reflective DLL Injection, нужно вспомнить, как Windows обычно работает с динамическими библиотеками. В штатном сценарии приложению достаточно вызвать LoadLibrary, после чего ОС ищет DLL на диске, мапит её в адресное пространство процесса, фиксирует таблицу импортов, делает релокации и вызывает DllMain
. Процедура прозрачна, но оставляет длинный цифровой «след» — файл на диске, записи в системных журналах, предсказуемые вызовы API.
Рефлективная инъекция стирает эти отпечатки. В библиотеку вшивается функции-загрузчик ReflectiveLoader
, который берёт на себя обязанности операционной системы. Вместо того чтобы читать DLL с диска, он получает байтовый массив прямо в память, создает собственную копию PE-образа, самостоятельно релокирует его, резолвит импорты и вызывает точку входа. На диске — пусто, в журналах — тишина, а вредонос уже внутри.
Сердце техники — именно ReflectiveLoader
. Его логика напоминает «мини-Windows» внутри DLL:
VirtualAllocEx
(или аналога) подготавливается область EXECUTE_READWRITE для нового образа.ReflectiveLoader
переносит заголовки и разделы PE-файла в выделенную область с сохранением выравнивания.DllMain(..., DLL_PROCESS_ATTACH, ...)
, передавая бразды правления полезной нагрузке.Особенность в том, что всё происходит внутри одного процесса и одной страницы памяти: операционной системе остаётся лишь покорно наблюдать, как библиотека «рождает» саму себя.
Публично техника была представлена исследователем Stephen Fewer из Harmony Security осенью 2008 года на конференции Black Hat Europe. Его работа «Reflective DLL Injection» быстро стала классикой: Фьюэр не только описал концепцию, но и выложил пример кода, с которого позже выросли модули Metasploit и Cobalt Strike. Если вам нужен первоисточник, настоятельно советуем прочитать оригинальное исследование — половина современных реализаций до сих пор опирается на те же идеи.
С 2008 года Reflective DLL Injection шагнула далеко вперёд. Основные векторы развития:
.text
вычищается и заменяется рефлективным загрузчиком.Сегодня Reflective DLL Injection встроена в большинство популярных инструментов:
Доступность таких фреймворков — палка о двух концах. С одной стороны, злоумышленникам не нужно изобретать велосипед, с другой — защитники могут воспроизвести атаку в лаборатории и заранее настроить детекторы.
Полностью невидимых техник не бывает. Вот сигналы, на которые стоит обращать внимание:
InLoadOrderModuleList
и InMemoryOrderModuleList
.Современные EDR (Defender for Endpoint, CrowdStrike, Huntress) отслеживают подобные аномалии поведенческими правилами, но ручная проверка всё ещё полезна — особенно в инцидент-респонсе, когда важна скорость.
Ниже — набор рекомендаций, который поможет удержать инфрас-труктуру с тёмной стороны силы:
VirtualAlloc
→ WriteProcessMemory
→ CreateRemoteThread
. Если буфер превысил, скажем, 1 МБ и сразу исполняется — высокий риск атаки.DWORD WINAPI ReflectiveLoader(LPVOID lpParameter)
{
BYTE *base = (BYTE *)lpParameter;
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)base;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(base + dos->e_lfanew);
LPVOID mem = VirtualAlloc(NULL,
nt->OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (!mem) return 0;
// Копируем заголовки
memcpy(mem, base, nt->OptionalHeader.SizeOfHeaders);
// Копируем секции
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt);
for (int i = 0; i < nt->FileHeader.NumberOfSections; ++i, ++sec)
memcpy((BYTE *)mem + sec->VirtualAddress,
base + sec->PointerToRawData,
sec->SizeOfRawData);
// Импорты и релокации (опущены для краткости)
// Вызываем DllMain
DLLMAIN entry = (DLLMAIN)((BYTE *)mem +
nt->OptionalHeader.AddressOfEntryPoint);
return entry((HINSTANCE)mem, DLL_PROCESS_ATTACH, NULL);
}
Даже такой упрощённый загрузчик обходил «классические» антивирусы эпохи до EDR. Теперь он полезен скорее как учебное пособие, но принципы остались неизменными.
Эксперты прогнозируют несколько трендов:
Reflective DLL Injection — удачный пример того, как изящная идея превращается в де-факто стандарт атаки. Но и защита не стоит на месте: грамотный мониторинг памяти, изоляция процессов и постоянные упражнения SOC делают технику заметной ещё до того, как вредонос выполнит первый зловредный вызов. Берите этот материал как основу: моделируйте угрозы, обучайте команду, совершенствуйте систему логов. Пусть отражения останутся лишь незаметными бликами — и никогда не превращаются в реальный инцидент.
Первое — находим постоянно, второе — ждем вас