Перехват 64-битной функции ядра Linux: длина указателя на функцию в 32/64-битной? - PullRequest
3 голосов
/ 07 марта 2011

Я пытаюсь повторно реализовать старый перехват ядра как бегемота (описан в эта проблема Phrack ).

Код для замены 32-битного вызова функции выглядит так:

#define SYSMAPADDR 0x12345678
#define CODESIZE 7
static char acct_code[7] = "\xb8\x00\x00\x00\x00"/*movl $0, %eax*/
"\xff\xe0";/*jmp *%eax*/
*(long*)&acct_code[1] = (long)my_hijacking_function;
// here, use either set_pages_rw or trick CR0 to do this:
memcpy(SYSMAPADDR, acct_code, CODESIZE);

Но 64-битный адрес исходной функции - 0xffffffff12345678 (ядро расположено в нехватке памяти).

Так будет ли указатель новой функции (long) соответствовать только 4 \ x00 байт инструкции movl ?

Кстати, пожалуйста, свяжите это с Могу ли я заменить функцию ядра Linux на модуль? и Переопределить функциональность модулями ядра Linux , описанный выше хакерский метод более гибок (может перехватывать внешние функции => нет необходимости перекомпилировать ядро).

Ответы [ 3 ]

4 голосов
/ 19 апреля 2011

Невозможно выполнить прямой и безусловный переход к адресу со смещением более 2 ГБ на любом x86 (32 или 64-разрядном).

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

Я использую регистр % r11 вместо аккумулятора.Согласно AMD64 ABI Draft 0.99.5 , % r11 - это временный регистр, который не сохраняется при вызовах функций.

Первая инструкция, movq $addr, %r11,делает именно так, как выглядит: загружает указанный адрес в регистр.Вторая инструкция, jmp *%r11, вызывает безусловный косвенный переход к адресу, хранящемуся в % r11 .

В конце резервных инструкций должен быть добавлен еще один безусловный косвенный переход к исходной функции цели - к адресу сразу после перезаписанных инструкций.Затем, когда вы захотите вызвать оригинал, вы можете вызвать адрес пролога резервной функции, и поток программы продолжится как обычно.

Помните, что число байтов для резервного копирования, M, должно бытьсумма размера инструкций сохранения / перехода и остатка перезаписанной инструкции.Вы не хотите оставлять какие-либо частичные инструкции после выполнения этого вуду.

2 голосов
/ 16 апреля 2011

Примечание: я предполагаю, что это для x86_64.

Указатели функций являются 64-битными, а инструкция movl распространяется на ноль в 64-битные регистры, поэтому вам придется переписать машинный код. Возможно, вам нужна инструкция 48 B8 (imm64) (т. Е. movq ..., %rax), и я думаю, инструкцию перехода можно оставить в покое, но я об этом немного знаю. Возможно, вам следует добавить теги 'x86-64' и 'assembly' к вашему вопросу.

1 голос
/ 07 марта 2011

Вы можете использовать операцию JMP rel32 (0xE9) для выполнения 32-разрядного относительного перехода от текущего адреса. Это позволит вам перейти в любое место в пределах 2 ГБ от адреса источника в пяти байтах. У него также есть преимущество в том, что он не забивает% eax (это может или не может быть важным в вашем случае).

Тем не менее, я бы порекомендовал изучить kprobes API . Это выполняет всю тяжелую работу по исправлению во время выполнения для вас. Он также имеет дело с несколькими маркерами, применяемыми к одной и той же функции и другими подобными действиями, и переносим на несколько платформ. В частности, если использовался ваш подход к обезьяньей заплатке, он может конфликтовать с API маркеров, если он скомпилирован, что может привести к сбоям. Это также приведет к сбоям, если динамически исправляемый код будет расположен в первых нескольких байтах функции (префиксы LOCK и т. Д.).

Возможно, вы также захотите посмотреть, как ftrace работает - в зависимости от конфигурации ядра может быть несколько быстрее подключиться к ftrace.

...