Получение аргументов функции с использованием kprobes

Я поместил kprobe в функцию, и теперь мне нужно получить значения ее аргументов в функции предварительной обработки kprobe.

Вот моя функция:

void foobar(int arg, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8)
{
    printk("foobar called\n");
}

Помещаем в него kprobe и вызываем функцию:

...
kp.addr = (kprobe_opcode_t *) foobar;
register_kprobe(&kp);

foobar(0xdead1, 0xdead2, 0xdead3, 0xdead4, 0xdead5, 0xdead6, 0xdead7, 0xdead8);

И, наконец, функция prehandler (взято отсюда):

static int inst_generic_make_request(struct kprobe *p, struct pt_regs *regs)
{
  printk(KERN_INFO "eax: %08lx   ebx: %08lx   ecx: %08lx   edx: %08lx\n",
    regs->ax, regs->bx, regs->cx, regs->dx);
    printk(KERN_INFO "esi: %08lx   edi: %08lx   ebp: %08lx   esp: %08lx\n",
      regs->si, regs->di, regs->bp, regs->sp);
    regs++;
    //...
}

Вывод из функции prehandler выглядит следующим образом (я увеличил regs указатель 3 раза)

May 10 22:58:07 kernel: [  402.640994] eax: 000dead1   ebx: f7d80086   ecx: 000dead3   edx: 000dead2
May 10 22:58:07 kernel: [  402.640996] esi: 00000000   edi: b77c8040   ebp: 00000000   esp: f7d8006c

May 10 22:58:07 kernel: [  402.641006] eax: f7d8032c   ebx: 000dead5   ecx: 000dead6   edx: 000dead7
May 10 22:58:07 kernel: [  402.641007] esi: 000dead8   edi: f7d800e0   ebp: f7d80330   esp: 08049674

May 10 22:58:07 kernel: [  402.641014] eax: 00000080   ebx: 0992b018   ecx: 0000108e   edx: 0992b008
May 10 22:58:07 kernel: [  402.641015] esi: 08049674   edi: b77c8040   ebp: bfe23fb8   esp: bfe23f50

Теперь я вижу аргументы foobar функция в различных регистрах (но где 0xdead4?) Разве они не должны быть в стеке? Как я могу получить доступ к стеку из функции prehandler? Или как я могу получить аргументы любой функции, не зная их типов и количества? Я знаю, что это может быть непростой задачей (и даже невозможно получить все значения), но достаточно только приблизительных значений. Я вычисляю соотношение между аргументами двух функций, и мне действительно не нужны точные значения. Это помогло бы, если бы у меня был ассемблерный код функции вызывающего, где аргументы помещаются в стек)?

2 ответа

Решение

Есть как минимум два подхода.

Подход 1: Jprobes

Возможно, самый простой: если Jprobes подходит для вашей задачи, вы можете попробовать. Они являются близкими родственниками kprobes (см. Их подробное описание и ссылки на примеры в документации к ядру).

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

Подход 2: регистры и стек

Другим подходом может быть немного расширить то, что вы уже делаете, и извлечь аргументы из регистров и стека. Из выходного журнала в вашем вопросе я предполагаю, что вы работаете в 32-битной системе x86.

x86, 32 бит

Насколько я видел, есть два наиболее распространенных соглашения о передаче аргументов в ядре Linux на x86 (подробности доступны в руководстве Агнера Фога). Обратите внимание, что системные вызовы следуют другим соглашениям (подробности см. В руководстве), но я предполагаю, что вы заинтересованы в анализе "обычной" функции, а не в системном вызове.

Конвенция 1

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

Как у вас есть сохраненное значение esp, вы можете найти то, на что он указывает. *(esp+4) должен быть первым аргументом, *(esp+8) - второй и т. д., если используется это соглашение.

Конвенция 2

Кажется, он используется для большинства функций ядра, включая ту, которую вы упомянули в вопросе.

Ядро скомпилировано с -mregparm=3 и, следовательно, первые 3 аргумента передаются в eax, edx а также ecx в этом порядке остальные идут в стек. *(esp+4) должен быть 4-м аргументом, *(esp+8) - пятый и так далее.

x86, 64 бит

Кажется, на x86-64 все немного проще. Большинство функций ядра (включая функции с переменным списком аргументов) получают первые 6 аргументов в rdi, rsi, rdx, rcx, r8, r9 в этом порядке остальные идут в стек. *(esp+8) должен быть седьмым аргументом, *(esp+16) - восьмое и так далее.

РЕДАКТИРОВАТЬ:

Обратите внимание, что на x86-32 значение esp не сохраняется в pt_regs для ловушек режима ядра (включая программные точки останова, на которые опирается KProbes). ; предоставляет kernel_stack_pointer() функция для получения правильного значения esp работает как на x86-32, так и на x86-64. Смотрите описание kernel_stack_pointer() в этом заголовочном файле для деталей.

К тому же, regs_get_kernel_stack_nth() (также определено в этом заголовке) предоставляет удобный способ получить содержимое стека в обработчике.

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

1) Создайте ваше ядро ​​или модуль с отладочной информацией (опция -g), для примера давайте предположим, что я создал модуль с именем 'test.ko' с отладочной информацией.

2) Используйте команду readelf для декодирования отладочной информации. Как это:

   $readelf debug-dump=info test.ko > log.info

Здесь я перенаправил вывод readelf в файл log.info.

3) Теперь откройте log.info и найдите функцию, для которой вы хотите узнать аргументы функции, в нашем случае, скажем, "foobar()". Там будет запись Dwarf с TAG DW_TAG_subprogram для функции foobar(). После этой TAG вы найдете несколько других записей карликов с именами аргументов функции. В этой записи вы найдете расположение этих аргументов функции при вызове функции. Например, он говорит, что первый аргумент 'arg' будет в регистре ebx, второй аргумент будет в esp+8, а третий аргумент будет в регистре ecx и так далее.

4) После того, как вы получите эту информацию, в вашем обработчике kprobe выведите все регистры. А также распечатывает данные стека, это вы можете распечатать, как вы знаете регистр esp в prehandler.

5) На основе информации, которую вы получите на 3-м шаге, найдите значения аргументов.

Другие вопросы по тегам