【问题标题】:Intercept file opening event in Linux在 Linux 中拦截文件打开事件
【发布时间】:2017-07-23 14:42:15
【问题描述】:

假设我们有一个可能dlopen() 某个第三方库的进程。这个库可以对用户只有写权限的文件执行open("write_only_logfile", O_WRONLY)。如果这个库试图打开一个文件,我们需要能够得到通知,所以稍后我们可以dup() 返回描述符并重定向输出。 有一些限制会使拦截变得更加困难:

  • LD_PRELOAD 被禁止 - 无法钩住 open()
  • inotify(7) 没有帮助,因为用户对 "write_only_logfile" 没有读取权限,并且它归管理员所有
  • 我们无权访问库资源,因此无法修改它
  • "write_only_logfile" 在库中是硬编码的,所以我们不能传递另一个名称来执行重定向

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

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

附言在最坏的情况下,可以找到 JVM 库映像 (libart.so) 并手动修补 open() 的跳转槽。但听起来不太好。

【问题讨论】:

  • 与其他操作系统相比,Linux 使这相对困难。 Microsoft 的分层驱动程序架构使其相对容易。许多 Windows 驱动程序手册包含强制加密文件系统的驱动程序示例。我从未见过 Linux 中分层的好例子。在目前的设计下可能是不可能的。
  • 这可能是重复的:Intercepting file operations on Linux。在 40161522 问题中,他不能使用LD_PRELOAD,因为这对他来说不可靠。
  • @jww 不错的建议,但 LD_PRELOAD 被划掉不是因为它不能很好地工作,而是因为我的应用程序是作为半准备过程的分支开始的。我感兴趣的库已经加载了。请查看已编辑的问题。

标签: android linux unix posix inotify


【解决方案1】:

听起来你的处境很麻烦。下面简要提到的大多数解决方案都保证会干扰 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/ 的 readlink)并对其采取行动(dup2 ,正如你已经提到的)。

“write_only_logfile”在库中被硬编码

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 这是一个可靠的建议,但除非启用用户命名空间,否则使用挂载命名空间不需要您是 root 用户吗?而且由于用户命名空间是众所周知的安全隐患,我不认为 Android 会很快将它们提供给应用程序......
    【解决方案3】:

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

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

    【讨论】:

    • 我感觉反对LD_PRELOAD 是非技术性的。原则上它可能有效,但如果网站政策不允许使用它也没关系。
    • 哦,我的问题描述得很糟糕,因为我错过了这样的场景。我不能使用LD_PRELOAD 和动态链接器的任何技巧,因为没有机会在应该监控的库之前加载任何自定义代码。请查看已编辑的问题。
    • “没有机会在应该监控的库之前加载任何自定义代码”你的意思不太清楚。 “假设我们有一个进程可以 dlopen() 一些第三方库”。如果你能控制这个过程,你就做好了。如果不是,那么描述这种情况的方式很奇怪。整个程序是第三方的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-27
    • 2015-11-05
    • 2021-06-27
    • 2019-06-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多