在 Linux 中拦截文件打开事件

Intercept file opening event in Linux

假设我们有一个进程可能dlopen()一些第三方库。该库可能会对用户只有写访问权限的某些文件执行 open("write_only_logfile", O_WRONLY)。我们需要能够在该库尝试打开文件时收到通知,因此稍后我们可能 dup() 返回描述符并重定向输出。 很少有限制使拦截变得更难:

我想知道 Linux 是否有有效的方法来帮助解决这种情况。 特别是考虑到进程可能经常 open() 杂项文件这一事实。

P.S。为了避免混淆并更好地理解 - 它是一个常规的 Android 应用程序,加载了 JVM。如果应用程序挂起(所谓的 ANR)- 系统会向它发送 SIGQUIT。通过 open()s /data/anr/traces.txt 的专用线程接收信号并将 JVM 状态写入其中。这些数据对于调试非常有用。但出于安全原因,应用程序无法直接读取该文件(所有应用程序都写入它,因此可能有些敏感)。无论如何,我相信拦截我的进程写入的内容是绝对公平的。

P.S.S.在最坏的情况下,可能会找到 JVM 库映像 (libart.so) 并为 open() 手动修补跳槽。不过不好听

如果这只是关于将写入(和读取)重定向到单个文件,您可以 运行 挂载命名空间中的应用程序具有适合该特定文件的绑定挂载。以这种方式设置可能需要一个小的 SUID 二进制文件。

一个更通用的解决方案可以快速接近联合文件系统,而且要做到正确非常困难。即使是内核联合文件系统 overlayfs,也无法提供完整的 POSIX 语义。

您需要 LD_PRELOAD 来挂接一个应用程序。要挂钩第三方库,只需在库之前正常加载挂钩(或将其放在可执行文件中)。

假设库从 libc 调用 open 而不是直接调用相应的系统调用,并且它以正常方式链接,您的代码中只有一个名为 open 的函数。让它从 libc(RTLD_NEXT 或其他)调用 open。第三方库(当然还有所有其他库)会将其 open 符号解析为您的函数。

听起来你遇到麻烦了。下面简要提到的大多数解决方案肯定会干扰 SELinux,所以请不要相信我的话。

使用 strace 调试自己的进程以拦截 open 是正常 Linux 上的常用解决方案之一。我不确定它是否适用于 Android;从某些新版本开始,它肯定可能成为不可调试应用程序的禁区(如果尚未被禁止)。

seccomp-bpf 是另一种可能性。可能不适用于旧的 Android 版本,但由于 Android O seccomp 将成为 Android 安全设置的保证部分。在仅警告模式下拦截 open,并在发生有趣的事情时(通过调试或信号)将控制权交还给自己。

如果 /data/anr/traces.txt 按需打开,您应该能够通过使用 inotify 或轮询观察 /proc/self/fd/ 的内容来观察。您可以通过设置打开线程的 io niceness 来减少比赛的影响……

以上所有只是部分解决方案,您可能仍然需要解码实际发生的 open 系统调用(strace 源代码可能对 strace/seccomp 解决方案有帮助,阅读链接 /proc/self/fd/) 并对其采取行动(dup2,正如您已经提到的那样)。

"write_only_logfile" is hardcoded inside the library

是否可以修改library/executable数据段的内存? Afaik mprotectPROTECT_EXEC 特别受到严格限制,但至少肯定允许 mmap(以支持 JIT 编译器等)。可能会编写一些东西来就地编辑字符串常量(只要这样做是可能的并且被允许,我自己对此不确定)。