我怎么能拦截linux sys调用?

Vha*_*run 14 c linux hook redirect system-calls

除了LD_PRELOAD技巧,以及用你提供的系统调用取代某个系统调用的Linux内核模块之外,是否有可能拦截一个系统调用(例如打开),以便在它到达实际打开之前首先通过你的函数?

DJ *_*lis 18

为什么不能/不想使用LD_PRELOAD技巧

这里的示例代码:

/*
 * File: soft_atimes.c
 * Author: D.J. Capelis
 *
 * Compile:
 * gcc -fPIC -c -o soft_atimes.o soft_atimes.c
 * gcc -shared -o soft_atimes.so soft_atimes.o -ldl
 *
 * Use:
 * LD_PRELOAD="./soft_atimes.so" command
 *
 * Copyright 2007 Regents of the University of California
 */

#define _GNU_SOURCE
#include <dlfcn.h>
#define _FCNTL_H
#include <sys/types.h>
#include <bits/fcntl.h>
#include <stddef.h>

extern int errorno;

int __thread (*_open)(const char * pathname, int flags, ...) = NULL;
int __thread (*_open64)(const char * pathname, int flags, ...) = NULL;

int open(const char * pathname, int flags, mode_t mode)
{
    if (NULL == _open) {
        _open = (int (*)(const char * pathname, int flags, ...)) dlsym(RTLD_NEXT, "open");
    }
    if(flags & O_CREAT)
        return _open(pathname, flags | O_NOATIME, mode);
    else
        return _open(pathname, flags | O_NOATIME, 0);
}

int open64(const char * pathname, int flags, mode_t mode)
{
    if (NULL == _open64) {
        _open64 = (int (*)(const char * pathname, int flags, ...)) dlsym(RTLD_NEXT, "open64");
    }
    if(flags & O_CREAT)
        return _open64(pathname, flags | O_NOATIME, mode);
    else
        return _open64(pathname, flags | O_NOATIME, 0);
}
Run Code Online (Sandbox Code Playgroud)

根据我的理解......它几乎是LD_PRELOAD技巧或内核模块.除非你想在一个模拟器下运行它,它可能会陷入你的函数或者在实际的二进制文件上重写代码来捕获你的函数,所以没有很多中间结构.

假设你不能修改程序而不能(或者不想)修改内核,LD_PRELOAD方法是最好的方法,假设你的应用程序是相当标准的,并且实际上并不是那个恶意试图过去的方法你的拦截.(在这种情况下,您将需要其他技术之一.)

  • 程序承认LD_PRELOAD完全是可选的.并非每个程序都与libc链接. (6认同)
  • @ acib708我的意思是程序可以在不使用libc的情况下进行系统调用.然后正在加载的库实际上并不重要,因为没有调用它的符号.相反,设置寄存器和创建中断的一小段组件可以进行调用. (4认同)
  • 一定喜欢这样,问题是“除了 X...”,最重要的答案是“使用 X”。 (3认同)

Tim*_*mmm 13

首先让我们消除一些其他人给出的非答案:

  • 使用LD_PRELOAD。是的,你LD_PRELOAD在问题中说了“此外......”,但显然这对某些人来说还不够。这不是一个好的选择,因为它仅在程序使用 libc 时才有效,但情况不一定如此。
  • 使用系统水龙头。是的,你在问题中说了“除了……Linux内核模块”,但显然这对某些人来说还不够。这不是一个好的选择,因为您必须加载自定义内核模块,这是一个很大的痛苦,并且还需要 root。
  • 瓦尔格林德。这确实有一定的作用,但它是模拟 CPU 的,所以它非常慢而且非常复杂。如果您只是为了一次性调试而这样做,那很好。如果你正在做一些值得生产的事情,这并不是一个真正的选择。
  • 各种系统调用审计。我不认为记录系统调用算作“拦截”它们。我们显然想要修改系统调用参数/返回值或通过其他代码重定向程序。

然而,这里还没有提到其他可能性。请注意,我对所有这些东西都很陌生,还没有尝试过任何东西,所以我可能对某些事情是错误的。

重写代码

理论上,您可以使用某种自定义加载程序来重写系统调用指令以跳转到自定义处理程序。但我认为实施起来绝对是一场噩梦。

k探针

kprobes是某种内核检测系统。它们只有对任何内容的只读访问权限,因此您不能使用它们来拦截系统调用,而只能记录它们。

跟踪

ptrace是 GDB 等调试器用来进行调试的 API。有一个PTRACE_SYSCALL选项可以在系统调用之前/之后暂停执行。从那里您可以像 GDB 一样做几乎任何您喜欢的事情。这是一篇有关如何使用 ptrace 修改系统调用参数的文章。然而它显然有很高的开销。

塞康普

Seccomp是一个旨在允许您过滤系统调用的系统。您无法修改参数,但可以阻止它们或返回自定义错误。Seccomp 过滤器是 BPF 程序。如果您不熟悉,它们基本上是用户可以在内核空间虚拟机中运行的任意程序。这避免了用户/内核上下文切换,这使得它们比 ptrace 更快。

虽然您无法直接从 BPF 程序修改参数,但您可以返回SECCOMP_RET_TRACE,这将触发ptraceing 父级中断。因此,它基本上是相同的,PTRACE_SYSCALL只是您需要在内核空间中运行一个程序来决定是否要根据其参数实际拦截系统调用。因此,如果您只想拦截一些系统调用(例如open()使用特定路径),它应该会更快。

我认为这可能是最好的选择。这是与上述同一作者撰写的一篇关于它的文章请注意,他们使用经典的 BPF 而不是 eBPF,但我想您也可以使用 eBPF。

编辑:实际上你只能使用经典的BPF,而不能使用eBPF。LWN有一篇关于它的文章

以下是一些相关问题。第一篇绝对值得一读。

这里还有一篇关于通过 ptrace 操作系统调用的好文章。


小智 7

Valgrind可用于拦截任何函数调用.如果您需要拦截成品中的系统调用,那么这将是没有用的.但是,如果您在开发期间尝试拦截,那么它可能非常有用.我经常使用这种技术来拦截散列函数,以便我可以控制返回的散列以进行测试.

如果您不知道,Valgrind主要用于查找内存泄漏和其他与内存相关的错误.但底层技术基本上是一个x86模拟器.它模拟你的程序并拦截对malloc/free等的调用.好的是,你不需要重新编译就可以使用它.

Valgrind有一个功能,他们称之为功能包装,用于控制功能的拦截.有关详细信息,请参见Valgrind手册的第3.2节.您可以为任何您喜欢的功能设置功能包装.拦截调用后,将调用您提供的替代函数.


小智 5

有些应用程序可以欺骗strace/ptrace不运行,所以我唯一真正的选择就是使用systemtap

如果需要由于其通配符匹配,Systemtap可以拦截一堆系统调用.Systemtap不是C,而是一种单独的语言.在基本模式下,systemtap应该阻止你做愚蠢的事情,但它也可以在"专家模式"下运行,如果需要,可以回退到允许开发人员使用C.

它不需要你修补你的内核(或者至少不应该),并且一旦编译了一个模块,你就可以从测试/开发盒中复制它并在生产系统上插入它(通过insmod).

我还没有找到一个已经找到解决方法的linux应用程序/避免被systemtap捕获.


szt*_*pet 1

如果您确实需要一个解决方案,您可能会对实现此目的的 DR rootkit 感兴趣,http://www.immunityinc.com/downloads/linux_rootkit_source.tbz2有关它的文章位于http://www.theregister.co。英国/2008/09/04/linux_rootkit_released/

  • 当存在其他更传统的替代方法时,为什么要建议一种晦涩的方法呢?LD_PRELOAD 是最常见的。 (4认同)