手把手用 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 打破了这种印象。它的原理可以概括为三步:
- 反汇编定位:借助 Capstone 反汇编引擎,扫描进程内存中 glibc 的代码段,找出所有
syscall指令(源码见src/disasm_wrapper.c与src/patcher.c)。 - 热补丁替换:把
syscall指令连同附近可覆盖的字节,替换为一条jmp跳转指令,跳转到库自己准备的"中转站"。 - 参数转换回调:中转站负责保存寄存器现场、把 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 实现可能无法正常工作。
- 已知问题:
clone与rt_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),仅供参考