Как внедрить код в ядро Linux без исходников и отладочных символов

03 Aug, 2026
1,452
🔱 357
👥 28

Представьте ситуацию: у вас на руках готовый бинарник ядра Linux для мобильного устройства или элокальной платы на ARM64. Исходников нет, отладочную информацию вырезали при сборке, а вам нужно перехватить системный вызов или добавить собственную логику прямо на уровне ядра.

Обычный путь в таких случаях долгий и унылый. Приходится разбирать бинарник в IDA Pro или Ghidra, вручную искать смещения функций и писать хрупкие патчи. Разработчик под ником bmax121 решил упростить этот процесс и выложил проект KernelPatch.

Зачем нужен KernelPatch

KernelPatch — это фреймворк для патчинга и хукинга ядра Linux, когда из исходных данных есть только урезанный (stripped) образ ядра вроде vmlinux или boot.img.

Когда ядро собирается без отладочной информации, традиционные инструменты отладки вроде kprobes отказываются работать без дополнительных костылей. KernelPatch решает эту проблему самостоятельно. Он восстанавливает таблицу символов прямо из бинарника ядра и даёт удобный API для модификации исполняемого кода.

Проект пригодится реверс-инжинирам, исследователям безопасности Android и разработчикам низкоуровневого ПО для встраиваемых систем.

Что умеет фреймворк

Разработчик заложил в проект несколько важных возможностей для работы с ядром:

  • Поиск символов без исходников. Инструмент парсит внутренние структуры kallsyms внутри готового образа. Ему не нужны внешние файлы System.map или исходный код. Похожий подход используется в утилите vmlinux-to-elf, откуда автор и взял часть идей.
  • Два режима внедрения кода. Можно сделать статический патч образа (например, перезаписать boot.img на диске) либо загрузить код динамически во время работы системы.
  • Перехват функций через inline hook. Для архитектуры ARM64 перехват вызовов сделан на базе наработок android-inline-hook. Фреймворк корректно обрабатывает инструкцию инструкции с относительной адресацией (b, bl, adrp), чтобы не сломать исполнение оригинального кода.
  • Хуки системных вызовов. Подмена адресов в таблице sys_call_table выполняется в пару команд.
  • Встроенный модуль SU. Для Android-устройств в проекте есть собственная реализация суперпользователя.

Как это устроено внутри

Чтобы загружать пользовательские модули (KPM — Kernel Patch Modules) на лету, фреймворку нужна память внутри ядра. Для этого в KernelPatch встроен компактный аллокатор TLSF. Он помогает выделять исполняемые страницы памяти (ROX) прямо в адресном пространстве ядра.

При проведении inline-хука фреймворк считывает первые инструкции целевой функции, формирует из них так называемый трамплин (trampoline) и записывает переход на ваш обработчик. Когда ваш код отработает, управление возвращается к оригинальной функции через трамплин.

+-------------------+      +---------------------+      +-------------------+
|  Целевая функция  | ---> |  Ваш обработчик KPM | ---> | Трамплин (Orig)   |
|  (инструкция JMP) |      |  (пользовательский) |      |  (продолжение)    |
+-------------------+      +---------------------+      +-------------------+

Системные требования и ограничения

У проекта есть четкие границы применимости, о которых важно знать заранее:

  • Поддерживается только архитектура ARM64. Версий для x86_64 или RISC-V пока нет.
  • Диапазон версий ядра — от Linux 3.18 до 6.6.
  • Главное условие — ядро должно быть собрано с флагом CONFIG_KALLSYMS=y.

Если производитель прошивки полностью выпилил CONFIG_KALLSYMS на этапе сборки, KernelPatch не сможет автоматически распарсить адреса функций. К счастью, в большинстве прошивок для Android и ARM-устройств этот флаг оставлен включенным.

Пример практического использования

Допустим, вам нужно отследить все запуск процессов через вызов sys_execve на тестовом устройстве с Android, но вы не хотите пересобирать ядро из исходников вендора.

Раньше пришлось бы собирать полноценный модуль ядра (LKM), решать проблемы с проверкой цифровой подписи и отсутствием загрузчика модулей в ядре.

С KernelPatch процесс выглядит иначе:

  1. Вы пишете небольшой C-код с логикой перехвата.
  2. Компилируете его в формат KPM.
  3. Скармливаете утилите KernelPatch ваш boot.img и скомпилированный KPM-модуль.
  4. Получаете модифицированный boot.img, прошиваете его через fastboot.

После загрузки устройства каждый вызов execve сначала проходит через ваш обработчик.

README у проекта довольно лаконичный, а всю подробную документацию придется изучать по исходникам и файлам в папке doc/. Тем не менее, проект активно развивается и уже набрал более 1400 звезд на GitHub.

Кстати, если вам нужен не сам фреймворк для экспериментов, а готовый root для Android на его основе, автор рекомендует обратить внимание на его соседний проект — APatch. Это более высокоуровневая альтернатива Magisk и KernelSU, использующая KernelPatch под капотом.

Для исследователей безопасности и разработчиков под ARM64 KernelPatch — отличная находка, сэкономящая не один десяток часов при анализе закрытых ядер.

🍪 Мы используем файлы cookie и сервис аналитики Яндекс.Метрика, чтобы сайт работал лучше. Продолжая пользоваться devtrends.ru, вы соглашаетесь с обработкой данных согласно Политике конфиденциальности.