Ответ 1
1. Кажется, начало security_bprm_check()
не восстанавливается полностью до вызова функции. Oops происходит в security_bprm_check+0x6
, т.е. Сразу после прыжка, который вы разместили там, поэтому, похоже, какая-то часть прыжка все еще там в тот момент. Я не могу сейчас сказать, почему это может произойти.
Взгляните на реализацию Kernel Probes (KProbes) на x86, это может дать вам несколько советов. Подробнее см. Также описание KProbes. KProbes необходимо исправлять и восстанавливать почти произвольные фрагменты кода ядра безопасным способом выполнения своей работы.
2.Накопите другой подход, который вы упомянули, относительно копирования функции. Ниже приведена небольшая взлома, и разработчики ядра будут недовольны разработчиками, но если нет другого способа, это может помочь.
Вы можете выделить память для копирования функций из той же области, где выделена память для кода модулей ядра. Эта область должна быть выполнена по умолчанию. Опять же, KProbes используют этот трюк для выделения своих буферов для обхода.
Память выделяется функцией module_alloc()
и освобождается module_free()
. Эти функции, конечно, не экспортируются, но вы можете найти их адреса так же, как и для security_file_mmap()
и т.д. Просто любопытство, вы используете kallsyms_on_each_symbol()
, правильно?
Если вы выделяете память таким образом, это также может помочь избежать другой не столь очевидной проблемы. На x86-64 области адресов памяти, доступные для kmalloc и для кода модулей, расположены довольно далеко друг от друга (см. Documentation/x86/x86_64/mm.txt), вне досягаемости любого относительного скачка. Если память сопоставляется с областью адресов модулей, вы можете использовать почти относительные прыжки и вызовы для вызова скопированных функций. Аналогичную проблему с RIP-относительной адресацией также можно избежать.
РЕДАКТИРОВАТЬ: Обратите внимание, что на x86, если вы скопируете часть кода в другую область памяти и хотите запустить ее, некоторые изменения в этом коде могут быть необходимы. По крайней мере, вам необходимо исправить относительные вызовы и переходы, которые передают управление вне скопированного кода (например, вызовы другой функции и т.д.), А также инструкции с относительной адресацией RIP.
Кроме того, в коде могут быть другие структуры, которые необходимо скопировать. Например, компилятор мог оптимизировать некоторые или даже все операторы switch
для перехода через таблицу. То есть адреса блоков кода для каждого case
хранятся в таблице в памяти, а переменная switch - это индекс в эту таблицу. Таким образом, вместо многих сравнений ваш модуль будет выполнять что-то вроде jmp <table_start>(%reg, N)
(N - размер указателя, в байтах). То есть, просто переход к адресу, который находится в соответствующем элементе таблицы. Поскольку такие таблицы создаются для кода перед его копированием, может потребоваться исправление, в противном случае такие переходы вернут выполнение исходного кода, а не скопированного.