在Linux中禁用vsyscall

7eumitmz  于 2023-01-01  发布在  Linux
关注(0)|答案(5)|浏览(167)

我正在开发一个软件,它使用ptrace(2)监视其他进程的系统调用。不幸的是,大多数现代操作系统都实现了某种快速的用户模式系统调用,在Linux中称为 vsyscalls
是否有办法对单个进程禁用vsyscalls/vDSO,如果无法禁用,则对整个操作系统禁用?

unguejic

unguejic1#

尝试echo 0 > /proc/sys/kernel/vsyscall64
如果你尝试在gettimeofday调用时使用ptrace,但是它们没有出现,那么系统使用的是什么时间源(pmtimer,acpi,tsc,hpet,等等),我想知道你是否可以试着强迫你的计时器使用像pmtimer这样的老时间,这可能是gtod计时器特定优化中的一个导致了你的ptrace调用被避免,即使vsyscall设置为零。

vs3odd8k

vs3odd8k2#

是否有办法对单个进程禁用vsyscalls/vDSO,如果无法禁用,则对整个操作系统禁用?
事实证明,一种有效地禁用单个进程的链接vDSO的方法,而不必使用ptrace在系统范围内禁用它!
您所要做的就是在跟踪的进程从execve返回之前停止它,并从辅助向量中删除AT_SYSINFO_EHDR条目(它直接位于rsp中指向的内存区域的环境变量之后)。PTRACE_EVENT_EXEC是一个很好的位置。
AT_SYSINFO_EHDR是内核用来告诉系统链接器vDSO在进程地址空间中的Map位置的内容。如果此条目不存在,ld的行为就好像系统尚未MapvDSO一样。
请注意,这并没有从进程内存中取消vDSO的Map,它只是在链接其他共享库时忽略它。如果作者真的想与它交互,恶意程序仍然可以与它交互。
我知道这个答复有点迟,但我希望这个消息能使一些可怜的人免于头痛

7fyelxc5

7fyelxc53#

对于较新的系统echo 0 > /proc/sys/kernel/vsyscall64可能不起作用。在Ubuntu 16.04中,可以通过在/etc/default/grub中的以下参数下添加内核参数vdso=0来在系统范围内禁用vDSO:GRUB_CMDLINE_LINUX_DEFAULT.
重要提示:参数GRUB_CMDLINE_LINUX_DEFAULT可能会被/etc/default/grub.d/...中的其他配置文件覆盖,因此请仔细检查何时添加自定义配置。

zkure5ic

zkure5ic4#

Tenders McChiken's approach上,我创建了一个 Package 器,它可以对任意二进制文件禁用vDSO,而不会影响系统的其余部分:https://github.com/danteu/novdso
一般程序相当简单:
1.使用ptrace等待从execve(2)返回
1.查找辅助向量的地址
1.用AT_IGNORE覆盖AT_SYSINFO_EHDR条目,告诉应用程序忽略以下值

wd2eg0qa

wd2eg0qa5#

我知道这是一个比较老的问题,但是没有人提到过第三种有用的方法,可以在每个进程的基础上禁用vDSO。您可以用自己的函数覆盖libc函数,该函数使用LD_PRELOAD执行实际的系统调用。
例如,用于覆盖gettimeofdaytime函数的简单共享库可能如下所示:

vdso_覆盖.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 Package 器发出一个原始系统调用(请参见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覆盖一个符号,但如果目标程序真的想这样做,有一种方法可以找到原始符号并使用它。

相关问题