Отключение 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
Общая процедура довольно проста:
- используйте ptrace, чтобы дождаться возврата от execve(2)
- найти адрес вспомогательного вектора
- перезаписать запись 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
, если целевая программа действительно этого хочет, есть способ найти исходный символ и использовать его вместо него.