手把手用 syscall_intercept 打造系统调用日志记录器:0 依赖实现 strace 核心功能 手把手用 syscall_intercept 打造系统调用日志记录器0 依赖实现 strace 核心功能【免费下载链接】syscall_interceptThe system call intercepting library项目地址: https://gitcode.com/gh_mirrors/sy/syscall_interceptsyscall_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_TYPERelease make sudo make install编译依赖很简单C99 编译器、cmake以及运行时依赖的 Capstone 反汇编引擎。第二步编写你的拦截回调核心 API 只有一个函数指针intercept_hook_point接口定义在include/libsyscall_intercept_hook_point.hint (*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_PRELOADexample.so ls在库的构造函数__attribute__((constructor))里完成回调注册后该进程发起的所有 libc 系统调用都会先经过你的代码。手把手实现系统调用日志记录器参考官方示例项目自带一个可直接运行的完整示例examples/syscall_logger.c输出格式和 strace 高度一致堪称现成的系统调用日志记录器。它的实现思路值得逐段拆解用一张表看懂回调参数参数含义syscall_number系统调用编号如SYS_openarg0~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-123INTERCEPT_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),仅供参考