我怎样才能拦截Linux系统调用?

2024-10-30 08:36:00
admin
原创
91
摘要:问题描述:除了 LD_PRELOAD 技巧和用您提供的系统调用替换某个系统调用的 Linux 内核模块之外,是否有可能拦截系统调用(例如打开),以便它在到达实际打开之前先经过您的函数?解决方案 1:首先让我们消除其他人给出的一些非答案:使用LD_PRELOAD。是的,你在问题中说的是“此外LD_PRELOAD...

问题描述:

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


解决方案 1:

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

  • 使用LD_PRELOAD。是的,你在问题中说的是“此外LD_PRELOAD……”,但显然这对某些人来说还不够。这不是一个好的选择,因为它仅在程序使用 libc 时才有效,但情况并非如此。

  • 使用 Systemtap。是的,你在问题中说“除了……Linux 内核模块”,但显然这对某些人来说还不够。这不是一个好选择,因为你必须加载自定义内核模块,这非常麻烦,而且需要 root 权限。

  • Valgrind。这确实有用,但它是通过模拟 CPU 来工作的,所以它真的很慢而且很复杂。如果你只是为了一次性调试而这样做,那没问题。如果你正在做一些值得投入生产的事情,那不是一个真正的选择。

  • 各种系统调用审计。我认为记录系统调用不算作“拦截”它们。我们显然想要修改系统调用参数/返回值或通过其他代码重定向程序。

但是这里还没有提到其他可能性。请注意,我对这些东西都很陌生,还没有尝试过,所以有些事情我可能错了。

重写代码

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

探针

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

跟踪

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

赛康普

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

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

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

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

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

  • eBPF 可以修改系统调用的返回值或参数吗?

  • 仅拦截使用 PTRACE_SINGLESTEP 的系统调用

  • 这是拦截系统调用的好方法吗?

  • 无需修改内核即可以最小开销拦截系统调用

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

解决方案 2:

为什么您不能/不想使用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);
}

据我所知...这几乎就是 LD_PRELOAD 技巧或内核模块。除非您想在可以捕获到您的函数的模拟器下运行它,或者在实际二进制文件上进行代码重写以捕获到您的函数,否则没有太多的折衷方案。

假设您无法修改程序,也无法(或不想)修改内核,LD_PRELOAD 方法就是最好的方法,前提是您的应用程序相当标准,并且实际上不是恶意试图绕过您的拦截的应用程序。(在这种情况下,您将需要其他技术之一。)

解决方案 3:

Valgrind可用于拦截任何函数调用。如果您需要在成品中拦截系统调用,那么这将毫无用处。但是,如果您尝试在开发过程中进行拦截,那么它将非常有用。我经常使用这种技术来拦截哈希函数,以便我可以控制返回的哈希以进行测试。

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

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

解决方案 4:

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

Systemtap 可以根据其通配符匹配功能在必要时拦截大量系统调用。Systemtap 不是 C,而是一种独立的语言。在基本模式下,systemtap 应该可以阻止您做蠢事,但它也可以在“专家模式”下运行,如果需要,可以返回到允许开发人员使用 C。

它不需要您修补内核(或者至少不应该),并且一旦模块被编译,您就可以从测试/开发箱中复制它并将其插入(通过 insmod)生产系统。

我还没有找到一个 Linux 应用程序能够找到办法解决/避免被 systemtap 捕获。

解决方案 5:

我没有使用 LKM 轻松完成此操作的语法,但这篇文章很好地概述了您需要做什么: http://www.linuxjournal.com/article/4378

您也可以只修补 sys_open 函数。从 linux-2.6.26 开始,它从 file/open.c 的第 1084 行开始。

您可能还会发现,如果您无法使用 inotify、systemtap 或 SELinux 为您完成所有这些日志记录,而无需构建新系统。

解决方案 6:

如果您只想查看打开的内容,则需要查看 ptrace() 函数或命令行 strace 实用程序的源代码。如果您确实想拦截该调用,也许让它执行其他操作,我认为您列出的选项 - LD_PRELOAD 或内核模块 - 是您唯一的选择。

解决方案 7:

如果您只是为了调试目的而这样做,请查看 strace,它内置于 ptrace(2) 系统调用之上,允许您在系统调用完成时挂接代码。请参阅手册页的 PTRACE_SYSCALL 部分。

解决方案 8:

如果你真的需要一个解决方案,你可能会对实现这一点的 DR rootkit 感兴趣,http://www.immunityinc.com/downloads/linux_rootkit_source.tbz2关于它的文章在这里http://www.theregister.co.uk/2008/09/04/linux_rootkit_released/

解决方案 9:

听起来你需要审计。

Auditd 允许全局跟踪所有系统调用或文件访问,并记录日志。您可以为您感兴趣的特定事件设置键。

解决方案 10:

使用 SystemTap 可能是一个选择。

对于 Ubuntu,请按照https://wiki.ubuntu.com/Kernel/Systemtap中的指示进行安装。

然后只需执行以下操作即可监听所有openat系统调用:

# stap -e 'probe syscall.openat { printf("%s(%s)
", name, argstr) }'
openat(AT_FDCWD, "/dev/fb0", O_RDWR)
openat(AT_FDCWD, "/sys/devices/virtual/tty/tty0/active", O_RDONLY)
openat(AT_FDCWD, "/sys/devices/virtual/tty/tty0/active", O_RDONLY)
openat(AT_FDCWD, "/dev/tty1", O_RDONLY)
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   681  
  在项目管理领域,集成产品开发(IPD)流程以其高效、协同的特点,被众多企业视为提升产品竞争力的关键。IPD流程强调跨部门、跨职能的紧密合作,以确保产品从概念到市场各个环节的无缝衔接。然而,实现这一目标并非易事,它需要企业深刻理解并掌握IPD流程中的跨部门协作艺术。本文将深入探讨IPD流程中跨部门协作的三个关键点,旨在为...
IPD项目管理咨询   9  
  掌握IPD流程图:提升团队协作的关键路径在当今快速变化的商业环境中,团队协作的效率与效果直接关系到项目的成功与否。集成产品开发(Integrated Product Development,简称IPD)作为一种先进的研发管理理念,通过跨部门、跨领域的协同工作,能够显著提升产品开发的速度与质量。而IPD流程图,则是这一理...
IPD流程阶段   9  
  IPD流程概述:理解其核心价值与实施背景集成产品开发(Integrated Product Development,简称IPD)是一种先进的产品开发管理理念,它强调跨部门协作、市场导向和快速响应变化的能力。IPD流程不仅关注产品本身的技术创新,更注重将市场、研发、生产、销售等各个环节紧密集成,以实现产品从概念到市场的高...
华为IPD是什么   7  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程以其跨部门协作、高效决策和快速响应市场变化的特点,被众多企业视为提升竞争力的关键。然而,实践IPD流程并非易事,项目管理中的种种错误往往阻碍了其效果的充分发挥。本文旨在深入探讨如何在实施IPD流程时避免这些常见错误,...
IPD框架   7  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用