在Linux中如何检测文件访问?

10

我有很多流程和数据处理应用程序,偶尔需要进行监视,这意味着我需要知道它们读取哪些文件。这主要是为了帮助打包测试用例,但在调试时也可能很有用。

有没有一种方法可以以产生此类列表的方式运行可执行文件?

我的想法有两个:

  1. 有一个命令可以调用并调用我的应用程序。类似于GDB。我调用GDB,给它一个可执行文件的路径和一些参数,然后GDB为我调用它。也许有类似的命令可以告诉我如何使用系统资源。
  2. 也许更有趣(但不必要的)解决方案。
    1. 创建名为libc.so的库,其中实现fopen(和其他一些函数)
    2. 将LD_LIBRARY_PATH更改为指向新库
    3. 在编辑器中复制真正的libc.so并重命名fopen(例如nepof)
    4. 我的库加载副本并根据需要调用重命名的函数以提供fopen功能。
    5. 调用应用程序,然后调用我的代理fopen。

选择#1肯定是首选,但欢迎对如何更轻松地完成#2的评论。


请参见https://dev59.com/MnA85IYBdhLWcg3wCe9Z。 - Ian Vaughan
3个回答

13

其中一个选项是使用strace:

strace -o logfile -eopen yourapp

这将记录所有文件打开事件,但会带来可能相当显著的性能损失。然而,它的优点是易于使用。

另一个选择是使用LD_PRELOAD。 这对应于您的选项#2。 基本思路是像这样进行操作:

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>

int open(const char *fn, int flags) {
    static int (*real_open)(const char *fn, int flags);

    if (!real_open) {
        real_open = dlsym(RTLD_NEXT, "open");
    }

    fprintf(stderr, "opened file '%s'\n", fn);
    return real_open(fn, flags);
}

然后使用以下命令进行构建:

gcc -fPIC -shared -ldl -o preload-example.so preload-example.c

然后使用以下命令运行程序:

$ LD_PRELOAD=$PWD/preload-example.so cat /dev/null
opened file '/dev/null'

这种方法的开销要小得多。

但是请注意,还有其他打开文件的入口 - 例如,fopen()、openat()或其中许多传统兼容性入口之一:

00000000000747d0 g    DF .text      000000000000071c  GLIBC_2.2.5 _IO_file_fopen
0000000000068850 g    DF .text      000000000000000a  GLIBC_2.2.5 fopen
000000000006fe60 g    DF .text      00000000000000e2  GLIBC_2.4   open_wmemstream
00000000001209c0  w   DF .text      00000000000000ec  GLIBC_2.2.5 posix_openpt
0000000000069e50 g    DF .text      00000000000003fb  GLIBC_2.2.5 _IO_proc_open
00000000000dcf70 g    DF .text      0000000000000021  GLIBC_2.7   __open64_2
0000000000068a10 g    DF .text      00000000000000f5  GLIBC_2.2.5 fopencookie
000000000006a250 g    DF .text      000000000000009b  GLIBC_2.2.5 popen
00000000000d7b10  w   DF .text      0000000000000080  GLIBC_2.2.5 __open64
0000000000068850 g    DF .text      000000000000000a  GLIBC_2.2.5 _IO_fopen
00000000000d7e70  w   DF .text      0000000000000020  GLIBC_2.7   __openat64_2
00000000000e1ef0 g    DF .text      000000000000005b  GLIBC_2.2.5 openlog
00000000000d7b10  w   DF .text      0000000000000080  GLIBC_2.2.5 open64
0000000000370c10 g    DO .bss       0000000000000008  GLIBC_PRIVATE _dl_open_hook
0000000000031680 g    DF .text      0000000000000240  GLIBC_2.2.5 catopen
000000000006a250 g    DF .text      000000000000009b  GLIBC_2.2.5 _IO_popen
0000000000071af0 g    DF .text      000000000000026a  GLIBC_2.2.5 freopen64
00000000000723a0 g    DF .text      0000000000000183  GLIBC_2.2.5 fmemopen
00000000000a44f0  w   DF .text      0000000000000088  GLIBC_2.4   fdopendir
00000000000d7e70 g    DF .text      0000000000000020  GLIBC_2.7   __openat_2
00000000000a3d00  w   DF .text      0000000000000095  GLIBC_2.2.5 opendir
00000000000dcf40 g    DF .text      0000000000000021  GLIBC_2.7   __open_2
00000000000d7b10  w   DF .text      0000000000000080  GLIBC_2.2.5 __open
0000000000074370 g    DF .text      00000000000000d7  GLIBC_2.2.5 _IO_file_open
0000000000070b40 g    DF .text      00000000000000d2  GLIBC_2.2.5 open_memstream
0000000000070450 g    DF .text      0000000000000272  GLIBC_2.2.5 freopen
00000000000318c0 g    DF .text      00000000000008c4  GLIBC_PRIVATE __open_catalog
00000000000d7b10  w   DF .text      0000000000000080  GLIBC_2.2.5 open
0000000000067e80 g    DF .text      0000000000000332  GLIBC_2.2.5 fdopen
000000000001e9b0 g    DF .text      00000000000003f5  GLIBC_2.2.5 iconv_open
00000000000daca0 g    DF .text      000000000000067b  GLIBC_2.2.5 fts_open
00000000000d7d60  w   DF .text      0000000000000109  GLIBC_2.4   openat
0000000000068850  w   DF .text      000000000000000a  GLIBC_2.2.5 fopen64
00000000000d7d60  w   DF .text      0000000000000109  GLIBC_2.4   openat64
00000000000d6490 g    DF .text      00000000000000b6  GLIBC_2.2.5 posix_spawn_file_actions_addopen
0000000000121b80 g    DF .text      000000000000008a  GLIBC_PRIVATE __libc_dlopen_mode
0000000000067e80 g    DF .text      0000000000000332  GLIBC_2.2.5 _IO_fdopen

为了完整性,你可能需要挂钩所有这些功能 - 至少不带 _ 前缀的那些功能应该被挂钩。特别是,一定要单独挂钩 fopen,因为从 fopen() 到 open() 的 libc-内部调用不会被 LD_PRELOAD 库挂钩。

类似的警告也适用于 strace - 也有 'openat' 系统调用,根据您的架构可能还有其他遗留系统调用。但没有 LD_PRELOAD 钩子那么多,所以如果您不介意性能损失,这可能是一个更容易的选择。


4
man strace 

示例(假设 2343 是进程 ID):

# logging part
strace -p 2343 -ff -o strace_log.txt 

# displaying part
grep ^open strace_log.txt

2
我使用的是类似以下内容的东西:

代码示例:

strace -o file.txt ./command

你可以随后进行以下操作:
cat file.txt | grep open

获取程序打开的所有文件列表。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接