Это не элегантно, так что меня заинтересуют лучшие идеи, но вот краткое изложение того, что мне удалось сделать в основном работой.
Это двухсторонний подход небольшой полезной нагрузки начальной загрузки, вставленной в отступ исходного эльфа, который затем выполняет mmap () произвольно большего двоичного двоичного объекта для выполнения реальной работы.
Часть I: полезная нагрузка начальной загрузки
По сути, я вставляю небольшое количество кода в некоторые отступы между разделом .ARM.exidx (который загружается в верхней части сегмента кода) и разделом .preinit_array. Этот код просто открывает еще один двоичный BLOB-объект и mmap (), которые доступны только для чтения и исполняются по жестко закодированному виртуальному адресу.
Для того, чтобы мой вставленный код загружался как часть основного исполняемого файла, мне пришлось изменить размер сегмента загрузки в файле elf, в данном случае это вторая структура phdr, которая начинается с 0x54. P_filesz в 0x64 (0x54 + 0x20) и p_memsz в 0x68 (0x54 + 0x24) были изменены.
Я также изменил начальный адрес e_entry в заголовке elf со смещением 0x18, чтобы он указывал на мой вставленный код. Мой вставленный код выполняет переход к старому начальному адресу, когда он завершает настройку (на самом деле он сначала переходит на второй этап настройки в большей полезной нагрузке, которая затем переходит к исходному).
Наконец, я изменил статически связанные заглушки системных вызовов для функций, которые я хотел перехватить, чтобы они указывали на мои замены по адресу загрузки большей полезной нагрузки, в которой находится mmap ().
Часть II: большая полезная нагрузка
Это реализует любые изменения, которые вносятся - в моем случае, замена системных вызовов функциями, которые регистрируют, когда выполняются определенные условия. Поскольку основной исполняемый файл статически связан, это должно быть так же - или, проще говоря, он не может использовать библиотеку C. Вместо этого он использует язык ассемблера для выдачи системных вызовов для базового ввода-вывода. Я понял, что, не будучи загруженным в качестве исполняемого файла, у меня нет постоянного хранилища локальных переменных, поэтому при запуске я запускаю mmap () анонимную страницу для хранения локальных переменных - в основном это fd файла, в который я регистрируюсь, и операции драйвера fd устройства который должен быть зарегистрирован.
Компиляция этой части немного не элегантна. Я компилирую в сборку с ключом -S для gcc, затем удаляю все ключевые слова раздела. Затем я передаю его обратно через gcc для сборки и генерации объекта. Я запускаю это через компоновщик, указав имя моей первой функции в качестве точки входа (-e) и используя настройку обычного сценария компоновщика, который удаляет начальное смещение 0x8000. Но есть еще некоторое смещение из-за заголовков, в данном случае 128 байтов. чтобы сохранить исправления, я объявляю содержимое связанного эльфа в двоичный двоичный объект, помещаю 128 байтов из / dev / zero и помещаю это в начало ....
Как я уже говорил ... это не элегантно, поэтому я открыт для лучших идей