在 Linux 中拦截文件打开事件
Intercept file opening event in Linux
假设我们有一个进程可能dlopen()
一些第三方库。该库可能会对用户只有写访问权限的某些文件执行 open("write_only_logfile", O_WRONLY)
。我们需要能够在该库尝试打开文件时收到通知,因此稍后我们可能 dup()
返回描述符并重定向输出。
很少有限制使拦截变得更难:
LD_PRELOAD
被禁止 - 无法挂钩 open()
inotify(7)
没有帮助,因为用户对 "write_only_logfile"
没有读取权限,并且它归管理员所有
- 我们无法访问库资源,因此无法修改它
"write_only_logfile"
是硬编码在库中的,所以我们不能通过另一个名字来执行重定向
我想知道 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 mprotect
和 PROTECT_EXEC
特别受到严格限制,但至少肯定允许 mmap(以支持 JIT 编译器等)。可能会编写一些东西来就地编辑字符串常量(只要这样做是可能的并且被允许,我自己对此不确定)。
假设我们有一个进程可能dlopen()
一些第三方库。该库可能会对用户只有写访问权限的某些文件执行 open("write_only_logfile", O_WRONLY)
。我们需要能够在该库尝试打开文件时收到通知,因此稍后我们可能 dup()
返回描述符并重定向输出。
很少有限制使拦截变得更难:
LD_PRELOAD
被禁止 - 无法挂钩open()
inotify(7)
没有帮助,因为用户对"write_only_logfile"
没有读取权限,并且它归管理员所有- 我们无法访问库资源,因此无法修改它
"write_only_logfile"
是硬编码在库中的,所以我们不能通过另一个名字来执行重定向
我想知道 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 mprotect
和 PROTECT_EXEC
特别受到严格限制,但至少肯定允许 mmap(以支持 JIT 编译器等)。可能会编写一些东西来就地编辑字符串常量(只要这样做是可能的并且被允许,我自己对此不确定)。