2017-07-23 66 views
2

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

  • LD_PRELOAD是被禁止的 - 没办法挂钩open()
  • inotify(7)没有帮助,因为用户对"write_only_logfile"没有读取权限,它是由管理员拥有
  • 我们无法访问库源,因此无法修改它
  • "write_only_logfile"在库内部硬编码,所以我们无法通过其他名称来执行重定向

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

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

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

+0

与其他操作系统相比,Linux使这一点相对困难。微软的分层驱动架构使其相对容易。许多Windows驱动程序书籍都包含强制性加密文件系统的驱动程序示例。我从来没有见过在Linux中分层的好例子。在目前的设计下可能是不可能的。 – jww

+0

这可能是重复的:[在Linux上拦截文件操作](https://stackoverflow.com/q/40161522/608639)。在40161522问题中,他不能使用'LD_PRELOAD',因为它对他不可靠。 – jww

+0

@jww很好的建议,但'LD_PRELOAD'被划掉了,不是因为它不能正常工作,而是因为我的应用程序是作为半准备过程的分支开始的。我感兴趣的图书馆已经加载。请参阅编辑的问题。 – Sergio

回答

1

听起来像你是在麻烦的情况。下面简要提到的大多数解决方案都保证会干扰SELinux,所以不要拿我的话来说。

调试你自己的进程strace拦截open是普通Linux上常用的解决方案之一。我不确定它是否适用于Android;它肯定会成为非可调试应用程序开始的一些新版本(如果它尚未被禁止的话)的禁用。

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

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

以上所有仅仅是部分解决方案,您仍然可能需要解码发生的实际的open系统调用(strace源代码可能对此有帮助strace/seccomp解决方案,readlink为/proc/self/fd/),并根据它进行操作(dup2,如您所述)。

“write_only_logfile”是图书馆

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

1

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

一个更通用的解决方案可以快速接近联合文件系统,并且正确地实现它非常困难。即使内核联合文件系统overlayfs也没有设法提供完整的POSIX语义。

+0

这是一个可靠的建议,但是不使用mount命名空间要求你是root用户,除非启用了用户命名空间?而且由于用户命名空间是众所周知的安全隐患,因此Android不会很快为他们提供应用程序...... – user1643723

0

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

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

+1

我有一种感觉,对'LD_PRELOAD'的反对是非技术性的。它原则上可行,但如果网站政策不允许使用,则无关紧要。 –

+0

哦,我错过了我的问题,因为我错过了这样的场景。我不能使用'LD_PRELOAD'和动态链接器的任何技巧,因为没有机会加载任何早于应该监视的库的自定义代码。请参阅编辑的问题。 – Sergio

+0

“没有机会加载比应该被监控的库更早的任何自定义代码”这不太清楚你的意思。 “假设我们有一个可能会(或)某个第三方库的进程”。如果你可以控制这个过程,你就可以设置。如果不是,这是描述情况的一种奇怪的方式。整个计划是第三方。 –