news 2026/8/16 18:57:29

手把手用 syscall_intercept 打造系统调用日志记录器:0 依赖实现 strace 核心功能

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手用 syscall_intercept 打造系统调用日志记录器:0 依赖实现 strace 核心功能

手把手用 syscall_intercept 打造系统调用日志记录器:0 依赖实现 strace 核心功能

【免费下载链接】syscall_interceptThe system call intercepting library项目地址: https://gitcode.com/gh_mirrors/sy/syscall_intercept

syscall_intercept 是一款强大的系统调用拦截库,让你无需内核模块、无需 root 权限,纯用户态就能监控和拦截 Linux 程序的每一次系统调用(syscall)。本文手把手教你基于它的极简回调 API 打造一个系统调用日志记录器,轻松复刻 strace 的核心功能,全程只需几十行 C 代码。无论是排查程序行为、分析恶意软件,还是学习 Linux 系统编程,这套方案都值得收藏 🚀

系统调用日志记录器是什么?先理解系统调用

在 Linux 中,程序读写文件、创建进程、收发网络数据等一切"特权操作",都要通过系统调用(syscall)交给内核完成。strace 的核心功能,就是把进程发起的每一次系统调用、传入的参数和内核返回的结果,逐条打印出来,例如:

open("/etc/passwd", O_RDONLY) = 3

传统上 strace 依赖内核的 ptrace 跟踪机制,而 syscall_intercept 走的是完全不同的路线:直接在进程内存中热补丁 libc,把每次系统调用"劫持"到你的代码里。这意味着拦截发生在用户态,逻辑完全由你掌控。

syscall_intercept 凭什么"0 依赖"?核心原理

很多人听到"拦截系统调用"就以为要写内核模块,syscall_intercept 打破了这种印象。它的原理可以概括为三步:

  1. 反汇编定位:借助 Capstone 反汇编引擎,扫描进程内存中 glibc 的代码段,找出所有syscall指令(源码见src/disasm_wrapper.csrc/patcher.c)。
  2. 热补丁替换:把syscall指令连同附近可覆盖的字节,替换为一条jmp跳转指令,跳转到库自己准备的"中转站"。
  3. 参数转换回调:中转站负责保存寄存器现场、把 x86_64 系统调用约定的参数翻译成 C 函数参数,然后调用你注册的回调函数(汇编细节见src/intercept_template.S)。

对你来说,这些底层魔法完全透明——你只需要提供一个 C 函数,其余交给库处理。

快速上手:3 步运行系统调用拦截示例

第一步:获取并编译 syscall_intercept

先获取源码并编译安装:

git clone https://gitcode.com/gh_mirrors/sy/syscall_intercept cd syscall_intercept cmake . -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release make sudo make install

编译依赖很简单:C99 编译器、cmake,以及运行时依赖的 Capstone 反汇编引擎。

第二步:编写你的拦截回调

核心 API 只有一个函数指针intercept_hook_point,接口定义在include/libsyscall_intercept_hook_point.h

int (*intercept_hook_point)(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result);

返回值的约定只有两条,非常好记:

回调返回值含义
0你接管该系统调用,结果通过*result返回给调用方
非 0放行,继续执行原始系统调用,结果由内核产生

把回调函数挂到intercept_hook_point上,编译成动态库,就完成了全部"拦截注册"工作。

第三步:用 LD_PRELOAD 注入运行

Linux 的动态链接器提供了LD_PRELOAD机制,可以在程序启动时强制加载你的共享库:

cc example.c -lsyscall_intercept -fpic -shared -o example.so LD_PRELOAD=example.so ls

在库的构造函数(__attribute__((constructor)))里完成回调注册后,该进程发起的所有 libc 系统调用都会先经过你的代码。

手把手实现系统调用日志记录器:参考官方示例

项目自带一个可直接运行的完整示例examples/syscall_logger.c,输出格式和 strace 高度一致,堪称现成的系统调用日志记录器。它的实现思路值得逐段拆解:

用一张表看懂回调参数

参数含义
syscall_number系统调用编号(如SYS_open
arg0~arg5系统调用的 6 个参数,来自寄存器
result输出参数,用于填写你"伪造"的返回值

示例里把 6 个参数打包成数组,通过syscall_desc.h中的描述表(examples/syscall_desc.c)把编号翻译成名字,再把参数按类型格式化输出。

输出 strace 风格日志:格式化参数与错误码

真正的难点在于"好看地打印参数":

  • 字符串参数:从指针读取内容并做转义,超长截断为...
  • 文件描述符与路径AT_FDCWD直接打印成名字,open标志位翻译成O_RDONLY | O_CREAT这样的可读形式;
  • 返回值与错误码:负数结果自动附带(Permission denied)之类的英文错误描述,误差不超过-0x1000时按错误码解析。

打印工具函数全部手写(见examples/syscall_logger.c中的print_*系列),刻意不调用 libc——因为此时 libc 已被热补丁,再调用会陷入死循环。

高并发安全:无锁环形缓冲区

真实场景下多线程同时发起系统调用,日志写入必须线程安全。示例的做法非常精巧:用 C11 原子操作(__atomic_*)实现一个无锁缓冲区,多个线程各自"认领"一段写入区间;缓冲区接近写满时,再通过syscall_no_intercept绕过拦截直接write落盘,避免递归拦截自己。

💡 注意syscall_no_intercept()这个 API:在回调内部需要发起新的系统调用时,绝不能使用 glibc 的syscall(),否则会再次触发拦截形成无限递归。这个"逃生通道"定义在同一个头文件里。

内置日志功能:一行环境变量开启系统调用监控

如果你不想写任何代码,syscall_intercept 还内置了一套日志能力,通过环境变量即可开启(实现在src/intercept_log.c):

环境变量作用
INTERCEPT_LOG设置后,把每个被拦截的系统调用记录到指定文件;路径以-结尾时自动追加进程 PID(如intercept.log-123
INTERCEPT_LOG_TRUNC设为0时,日志文件不截断(默认截断)
INTERCEPT_HOOK_CMDLINE_FILTER只对指定程序名生效,例如只监控名为ls的进程

配合INTERCEPT_HOOK_CMDLINE_FILTER还有一个实用调试技巧:用它限定目标程序后,再用 gdb 调试就不会误拦截调试器自身的系统调用。

进阶玩法:不只是记录,还能拦截改写

日志记录只是"观察",syscall_intercept 更强大的地方在于"改写"。项目里的另一个示例examples/fork_ban.c演示了如何彻底禁止程序创建子进程——在回调里拦截SYS_fork,直接返回-EPERM,进程感知到的就是内核拒绝了调用。

这种"无中生有"的能力可以衍生出很多玩法:限制文件访问、伪造系统调用结果、实现沙箱、给旧程序打"行为补丁"……本质上你的回调函数就是应用与内核之间的一道可编程闸门 🎯

使用限制与避坑指南

  • 平台范围:仅支持 Linux + x86_64,且只对 glibc 做了完整验证;其他 libc 实现可能无法正常工作。
  • 已知问题clonert_sigreturn两个系统调用存在已知缺陷,涉及多线程与信号处理的场景要特别小心(详见README.md的 Limitations 一节)。
  • 拦截范围:只拦截 libc 发起的系统调用,代码中内联汇编直接发出的 syscall 不会被捕获。
  • 热补丁冲突:依赖"进程内只有本库在热补丁 libc"这一假设,不要与其他同类工具混用。
  • 初始化时机:库依赖 libc 已加载进内存,这正是它被设计为配合LD_PRELOAD使用的原因。

总结

syscall_intercept 用"热补丁 libc + 极简回调 API"的组合,把系统调用拦截这个听起来很底层的课题,变成了新手也能上手的几十行 C 代码。参考examples/syscall_logger.c,你可以轻松打造一个属于自己的系统调用日志记录器;进一步发挥,还能实现 strace 之外的拦截、改写、沙箱等高级功能。配合内置的INTERCEPT_LOG环境变量和命令行过滤,无论是日常调试还是系统监控,它都是一件趁手的好工具 ✨

【免费下载链接】syscall_interceptThe system call intercepting library项目地址: https://gitcode.com/gh_mirrors/sy/syscall_intercept

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/16 18:57:23

Node.js内存溢出全攻略:从紧急处理到根治优化

1. 问题本质:为什么你的Node.js应用会“内存溢出”?“FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory”。这行红字对于任何一个Node.js开发者来说,都像是一个熟悉的噩梦。它意味着你的应用在尝试分配更…

作者头像 李华
网站建设 2026/8/16 18:51:09

EasyPR-Java的局限与扩展:为什么只支持蓝黄车牌?如何打破限制

EasyPR-Java的局限与扩展:为什么只支持蓝黄车牌?如何打破限制 【免费下载链接】EasyPR-Java 车牌识别软件EasyPR的Java版本 项目地址: https://gitcode.com/gh_mirrors/ea/EasyPR-Java EasyPR-Java 是开源车牌识别系统 EasyPR 的 Java 版本&#…

作者头像 李华
网站建设 2026/8/16 18:50:25

OpenClaw部署指南:从环境配置到生产实践

1. OpenClaw部署环境准备与基础配置 作为一个长期从事AI工具部署的技术人员,我最近在本地环境部署OpenClaw时遇到了不少坑。OpenClaw作为一款新兴的AI工具链管理平台,其部署过程比想象中要复杂得多。首先需要明确的是,OpenClaw对运行环境有严…

作者头像 李华
网站建设 2026/8/16 18:44:30

7 Scenes与NRGBD数据集评测:FastVGGT跨场景性能表现

7 Scenes与NRGBD数据集评测:FastVGGT跨场景性能表现 【免费下载链接】FastVGGT [ICLR 2026] FastVGGT: Fast Visual Geometry Transformer 项目地址: https://gitcode.com/gh_mirrors/fa/FastVGGT FastVGGT 是一款用于三维重建的视觉几何 Transformer 加速框…

作者头像 李华
网站建设 2026/8/16 18:43:50

从TCP协议到连接池:拆解一次网络连接的真实成本与性能优化

1. 这篇文章真正要解决的问题 如果你是一名后端开发者,一定对“连接池”这个概念不陌生。无论是数据库连接池、HTTP连接池还是Redis连接池,我们都在项目中配置过。但你是否真的想过,为什么我们需要连接池?一次简单的TCP连接&#…

作者头像 李华
网站建设 2026/8/16 18:43:46

MPTCPv1调度器实现与性能优化指南

1. MPTCPv1调度器概述 多路径TCP(MPTCP)作为传统TCP协议的扩展,允许单个TCP连接同时使用多条网络路径传输数据。我在内核4.15版本上实现MPTCPv1调度器时,发现其核心价值在于:当设备同时连接Wi-Fi和蜂窝网络时&#xff…

作者头像 李华