
whatfiles v2.0
记录任何Linux进程访问的文件
whatfiles
Whatfiles 是一个 Linux 实用工具,用于记录另一个程序在你的系统上读取/写入/创建/删除哪些文件。它还会追踪目标进程创建的所有新进程和线程,并记录每次操作是否成功。
理由:
长期以来,我一直对缺乏一个简单的实用工具来查看进程从 main() 到退出期间接触了哪些文件感到沮丧。无论你是不信任某个软件供应商,还是担心恶意软件,能够了解一个程序或安装程序对你的系统做了什么都很重要。lsof 只能观察某一时刻的状态,而 strace 体积庞大且有些复杂。
示例输出:
mode: exec, file: /usr/bin/cp, syscall: execve(), PID: 17004, process: sh, result: 0
mode: read, file: /tmp/demo/copy.txt, syscall: openat(), PID: 17004, process: /usr/bin/cp, result: -1 (No such file or directory)
mode: read, file: /etc/hostname, syscall: openat(), PID: 17004, process: /usr/bin/cp, result: 3
mode: write+create, file: /tmp/demo/copy.txt, syscall: openat(), PID: 17004, process: /usr/bin/cp, result: 4
mode: chmod, file: /tmp/demo/copy.txt, syscall: fchmodat(), PID: 17005, process: /usr/bin/chmod, result: 0
mode: rename, file: /tmp/demo/copy.txt, to: /tmp/demo/renamed.txt, syscall: renameat2(), PID: 17006, process: /usr/bin/mv, result: 0
mode: read, file: /tmp/demo/missing.txt, syscall: openat(), PID: 17007, process: /usr/bin/cat, result: -1 (No such file or directory)
mode: delete, file: /tmp/demo/renamed.txt, syscall: unlinkat(), PID: 17008, process: /usr/bin/rm, result: 0
每一行说明了对文件做了什么、文件本身、是哪个系统调用执行的、哪个进程和线程,以及内核返回了什么。路径始终是绝对路径:相对路径会相对于进程的工作目录解析,或者相对于它传递给 *at() 系统调用的目录解析。result 是系统调用的返回值,因此可以区分失败的访问和成功的访问。
除了打开、创建和删除之外,whatfiles 还会报告程序的 rename、link、symlink、mkdir、rmdir、truncate、chmod、chown 和 exec。SYSCALLS.md 涵盖了目前尚未报告的内容以及为什么每一项新增都值得做。
使用:
-
基本用法,启动
ls并将输出写入当前目录下的日志文件:$ whatfiles ls -lah ~/Documents -
使用
-o指定输出文件位置:$ whatfiles -o MyLogFile cd .. -
包含调试输出,打印到 stdout 而不是日志文件:
$ whatfiles -d -s apt install zoom -
附加到当前正在运行的进程(需要 root 权限):
$ sudo whatfiles -p 1234 -
如果 whatfiles 本身被杀死,则终止被追踪的程序,而不是让它继续在未被追踪的情况下运行:
$ whatfiles -k ./installer.sh
随时按 Ctrl-C:whatfiles 会从它正在追踪的所有内容上分离,让那些进程继续运行,并完成日志的写入。
分发
可直接使用的二进制文件在 releases 页面上!也有人好心地将它添加到了 Arch 仓库,并且 letompouce 还搭建了一个 GitLab 流水线。
编译(需要 gcc 和 make):
$ cd whatfiles
$ make
$ sudo make install
支持 x86、x86_64、ARM32 和 ARM64 架构。make install 遵循 PREFIX 和 DESTDIR。
需要 Linux 3.4 或更新版本。在 Linux 5.3 及更新版本上,whatfiles 会直接向内核询问每次系统调用停止的信息,这正是让 64 位机器上的 32 位系统调用能够正确解码的原因;在较旧的内核上,它会回退到读取寄存器。
Android
使用 NDK 交叉编译,然后将二进制文件推送到设备:
$ make android NDK=~/Android/Sdk/ndk/<version>
$ adb push bin/whatfiles-android /data/local/tmp/whatfiles
$ adb shell chmod 755 /data/local/tmp/whatfiles
$ adb shell /data/local/tmp/whatfiles -o /data/local/tmp/ls.log ls /sdcard
ANDROID_ABI 选择 arm64(默认值),或 arm32、x86_64 或 x86。ANDROID_API 设置最低 API 级别,默认为 21。
在设备上有几点不同:
- 将二进制文件放在
/data/local/tmp中。/sdcard挂载时没有执行权限。 adb shell中的工作目录不可写,因此请使用-o传入/data/local/tmp下的路径,或使用-s写入 stdout。- 在 whatfiles 下运行命令会以普通 shell 用户身份工作,附加到该用户启动的进程也是如此。附加到其他任何东西(例如一个应用)需要 root,因此在 userdebug 构建上需要
adb root。在 Android 14 模拟器上,这在 SELinux 强制执行的情况下也能工作;生产设备的策略可能仍会拒绝。 make test-android NDK=~/Android/Sdk/ndk/<version>会为连接的设备构建 whatfiles 和测试程序,在那里运行检查,并删除它推送的内容。- 从 arm64 构建追踪的 32 位应用会使用 32 位系统调用号和参数寄存器来读取,而不是被当作 64 位应用。该路径尚未在真实硬件上验证:此处用于测试的模拟器没有 32 位 ABI。
make test 会构建 tests/ 中的程序并在 whatfiles 下运行它们以检查其行为,包括信号传递、线程和子进程覆盖以及中断处理。
将来可能会被问到的问题:
-
这不就是
strace -fe trace=creat,open,openat,unlink,unlinkat ./program的重新实现吗?是的。不过它旨在更简单、更用户友好。
-
有 Mac 和 Windows 版本吗?
没有。在 Mac 上追踪系统调用需要
task_for_pid(),而这需要代码签名,我无法让它工作,而且无论如何我也没兴趣每年向 Apple 支付 100 美元来编写自由软件。Mac 上的dtruss可用于跟踪单个进程及其子进程,不过-t标志似乎只接受单个系统调用进行过滤。fs_usage做类似的事情,但我不确定它是否跟踪子进程/线程。Windows 的 Process Monitor 相当不错。
限制:
-
将工作交接给自身副本的程序。 尤其是浏览器:如果已经有一个实例在运行,你启动的那个实例会把你的请求传递给它然后退出,因此 whatfiles 没有可追踪的东西并停止,而你所请求的窗口来自未被追踪的副本。请改为追踪一个单独的实例,例如使用
whatfiles firefox --no-remote --profile ~/ff-trace-profile以及一个尚不存在的配置文件目录,或者先退出正在运行的副本。 -
不是安全边界。 一个不想被监视的程序可以察觉到自己正在被追踪,而且
io_uring执行文件操作时不会经过 whatfiles 所监视的系统调用。请将日志视为对程序所做事情的描述,而不是对它可能做过的一切的证明。 -
速度。 每次系统调用都会让被追踪的进程停止两次,因此系统调用密集的程序运行速度会比平时慢几倍。这与
strace在跟踪所有系统调用时付出的代价相同。 -
附加需要权限。
-p通常需要 root,或者放宽的/proc/sys/kernel/yama/ptrace_scope。被拒绝的附加会让目标进程正常继续运行。 -
如果 whatfiles 被直接杀死(使用
SIGKILL),它正在追踪的程序会继续在未被追踪的情况下运行,除非它是用-k启动的。
计划中的功能:
- 目前没有,欢迎提出请求和 PR。
感谢你的关注,也请顺便看看 Cloaker、Nestur 和 Flying Carpet!