Отключение vsyscalls в Linux

Я работаю над программным обеспечением, которое отслеживает системные вызовы других процессов с помощью ptrace(2). К сожалению, большинство современных операционных систем реализуют какие-то быстрые системные вызовы в пользовательском режиме, которые в Linux называются vsyscalls.

Есть ли способ отключить использование vsyscalls/vDSO для одного процесса или, если это невозможно, для всей операционной системы?

5 ответов

Решение

Пытаться echo 0 > /proc/sys/kernel/vsyscall64

Если вы пытаетесь отслеживать звонки gettimeofday, и они не отображаются, какой источник времени использует система (pmtimer, acpi, tsc, hpet и т. Д.). Интересно, вы бы меня унизили, пытаясь заставить свой таймер сделать что-то старше, например, pmtimer. Возможно, одна из многих специфических оптимизаций таймера gtod позволяет избежать вызовов ptrace, даже если vsyscall установлен на ноль.

Есть ли способ отключить использование vsyscalls/vDSO для одного процесса или, если это невозможно, для всей операционной системы?

Оказывается, есть способ эффективно отключить связывание vDSO для одного процесса, не отключая его для всей системы, используя ptrace!

Все, что вам нужно сделать, это остановить отслеживаемый процесс до его возвращения из execve и удалите AT_SYSINFO_EHDR вход из вспомогательного вектора (который идет непосредственно после переменных среды вдоль области памяти, указанной в rsp). PTRACE_EVENT_EXEC это хорошее место для этого.

AT_SYSINFO_EHDR это то, что ядро ​​использует, чтобы сообщить системному компоновщику, где vDSO отображается в адресном пространстве процесса. Если этой записи нет, ld кажется, действует так, как если бы система не отображала vDSO.

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

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

Для более новых систем echo 0 > /proc/sys/kernel/vsyscall64 может не сработать. В Ubuntu 16.04 vDSO можно отключить для всей системы, добавив параметр ядра vdso=0 в /etc/default/grub по параметру: GRUB_CMDLINE_LINUX_DEFAULT,

ВАЖНО: параметр GRUB_CMDLINE_LINUX_DEFAULT могут быть перезаписаны другими файлами конфигурации в /etc/default/grub.d/..., поэтому дважды проверьте, когда добавить свою пользовательскую конфигурацию.

Подобрав на подходе Тендеры McChiken в, я сделал создать оболочку, отключающий vDSO для произвольного двоичного файла, не затрагивая остальную часть системы: https://github.com/danteu/novdso

Общая процедура довольно проста:

  1. используйте ptrace, чтобы дождаться возврата от execve(2)
  2. найти адрес вспомогательного вектора
  3. перезаписать запись AT_SYSINFO_EHDR с помощью AT_IGNORE, указав приложению игнорировать следующее значение

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

Простая общая библиотека для переопределенияgettimeofdayиtimeфункции, например, могут выглядеть так:

vdso_override.c:

      #include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/syscall.h>

int gettimeofday(struct timeval *restrict tv, struct timezone *restrict tz)
{
    return syscall(__NR_gettimeofday, (long)tv, (long)tz, 0, 0, 0, 0);
}

time_t time(time_t *tloc)
{
    return syscall(__NR_time, (long)tloc, 0, 0, 0, 0, 0);
}

Это использует оболочку libc для выполнения необработанного системного вызова (см. syscall(2)), поэтому vDSO обходит. Таким образом вам придется перезаписать все системные вызовы, которые vDSO экспортирует в вашу архитектуру (перечислены в vdso(7)).

Компилировать с

      gcc -fpic -shared -o vdso_override.so vdso_override.c

Затем запустите любую программу, в которой вы хотите отключить вызовы VDSO, следующим образом:

      LD_PRELOAD=./vdso_override.so <some program>

Это, конечно, работает только в том случае, если программа, которую вы запускаете, не пытается активно обойти это. Хотя вы можете переопределить символ, используяLD_PRELOAD, если целевая программа действительно этого хочет, есть способ найти исходный символ и использовать его вместо него.

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