如何复制进程创建和删除的 /tmp/ 目录?

如何复制进程创建和删除的 /tmp/ 目录?

我正在研究 Oracle Linux 9(XFS 文件系统)上二进制文件的行为。当进程调用此二进制文件时,会在其下创建一个目录/tmp并将一些文件复制到其中。每次进程运行时,此目录都会获得一个随机名称(关键字+ GUID)。

之后,它立即删除该目录。我想在删除该目录之前访问该目录中包含的文件,但对于我的任何命令来说,整个过程结束得太快了。

有什么方法可以在删除该目录之前“拦截”并复制该目录吗?

答案1

您始终可以在以下位置运行应用程序:

gdb --args /path/to/your/your-program and its args

unlink()然后在、unlinkat()rmdir()函数或系统调用上添加断点:

catch syscall unlink
catch syscall unlinkat
catch syscall rmdir
run

然后,每次到达断点时,检查是否要删除该目录中的文件并检查其中的文件或将它们复制到其他地方。在gdb中输入cont以恢复执行(直到下一个断点)。

示例rm -rf

$ gdb -q --args rm -rf /tmp/tmp.HudBncQ4Ni
Reading symbols from rm...
Reading symbols from /usr/lib/debug/.build-id/f6/7ac1d7304650a51950992d074f98ec88fe2f49.debug...
(gdb) catch syscall unlink
Catchpoint 1 (syscall 'unlink' [87])
(gdb) catch syscall unlinkat
Catchpoint 2 (syscall 'unlinkat' [263])
(gdb) catch syscall rmdir
Catchpoint 3 (syscall 'rmdir' [84])
(gdb) run
Starting program: /bin/rm -rf /tmp/tmp.HudBncQ4Ni

Catchpoint 2 (call to syscall unlinkat), 0x00007ffff7eb6fa7 in __GI_unlinkat () at ../sysdeps/unix/syscall-template.S:120
120     ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) info registers
rax            0xffffffffffffffda  -38
rbx            0x555555569830      93824992319536
rcx            0x7ffff7eb6fa7      140737352789927
rdx            0x0                 0
rsi            0x555555569938      93824992319800
rdi            0x4                 4
rbp            0x555555568440      0x555555568440
rsp            0x7fffffffda48      0x7fffffffda48
r8             0x3                 3
r9             0x0                 0
r10            0xfffffffffffffa9c  -1380
r11            0x206               518
r12            0x0                 0
r13            0x7fffffffdc30      140737488346160
r14            0x0                 0
r15            0x555555569830      93824992319536
rip            0x7ffff7eb6fa7      0x7ffff7eb6fa7 <__GI_unlinkat+7>
eflags         0x206               [ PF IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
(gdb) x/s $rsi
0x555555569938: "test"
(gdb) info proc
process 7524
cmdline = '/bin/rm -rf /tmp/tmp.HudBncQ4Ni'
cwd = '/export/home/stephane'
exe = '/bin/rm'
(gdb) !readlink /proc/7524/fd/4
/tmp/tmp.HudBncQ4Ni
(gdb) !find /tmp/tmp.HudBncQ4Ni -ls
  1875981      4 drwx------   2 stephane stephane     4096 Aug  8 09:30 /tmp/tmp.HudBncQ4Ni
  1835128      4 -rw-r--r--   1 stephane stephane        5 Aug  8 09:30 /tmp/tmp.HudBncQ4Ni/test

此处,断点位于x86_64 Linux 系统内部条目unlinkat()的系统调用上,其中系统调用的前两个参数位于和寄存器中。test/tmp/tmp.HudBncQ4Nirdirsi

strace可以在调用系统调用时向进程注入信号(strace -e inject=unlink,unlinkat,rmdir:signal=STOP例如挂起),但 AFAICT 它总是这样做系统调用返回,因此一旦文件已被删除。

但是,您可以延迟输入,以便可以使用Ctrl+手动暂停Z,例如:

$ strace -e inject=unlink,unlinkat,rmdir:delay_enter=5s -e unlink,unlinkat,rmdir rm -rf /tmp/tmp.HudBncQ4Ni
unlinkat(4, "test", 0^Z
zsh: suspended  strace -e inject=unlink,unlinkat,rmdir:delay_enter=10s -e  rm -rf

或者,按照@PhilippWendler的建议,您可以使用:

strace -e inject=unlink,unlinkat,rmdir:retval=0 -e unlink,unlinkat,rmdir ...

或者:

strace -e inject=unlink,unlinkat,rmdir:error=EACCES -e unlink,unlinkat,rmdir ...

劫持系统调用并假装它们成功(带有retval=0)或失败(EACCES此处的含义没有权限)而不实际打电话给他们。

gdb和都可以分别使用/strace附加到已经运行的进程 。还可以告诉他们跟踪 fork 和 exec 并跟踪子级,以便您可以附加到父级并监视或劫持子级中的取消链接(请参阅和中的设置)--pid <the-process-id>-p <the-process-id>-fstracefollow-*gdb

答案2

我发现这个使用 inotify-tools 的 shell 脚本,它完全符合我的要求(作者:https://unix.stackexchange.com/a/265995/536771):

#!/bin/sh

TMP_DIR=/tmp
CLONE_DIR=/tmp/clone
mkdir -p $CLONE_DIR

wait_dir() {
  inotifywait -mr --format='%w%f' -e create "$1" 2>/dev/null | while read file; do
    echo $file
    DIR=`dirname "$file"`
    mkdir -p "${CLONE_DIR}/${DIR#$TMP_DIR/}"
    cp -rl "$file" "${CLONE_DIR}/${file#$TMP_DIR/}"
  done
}

trap "trap - TERM && kill -- -$$" INT TERM EXIT

inotifywait -m --format='%w%f' -e create "$TMP_DIR" | while read file; do
  if ! [ -d "$file" ]; then
    continue
  fi

  echo "setting up wait for $file"
  wait_dir "$file" &
done

对我来说更简单的解决方案甚至比脚本更好: chattr +a /tmp

这是因为,如果二进制文件在 /tmp 下创建单个文件而不是文件夹,则脚本会失败。如果二进制文件在 /tmp 下创建多个文件夹,也会失败。

编辑:一个更简单的有效解决方案是运行:

cp -rp /source /clone

chattr 干扰了我正在检查的内容,第一个脚本适用于 /tmp 下创建的目录,但不适用于 /tmp 下创建的文件

答案3

我过去也有过类似的情况。我依稀记得运行过类似的东西chattr -R -a /tmp,本质上是/tmp只进行追加。进程可以创建文件/目录,但不能删除它们。请在运行之前仔细检查命令,并确保尽快撤消属性。

答案4

我曾经用于不同函数(listen)的解决方案是创建一个简单的动态库,在其中重新定义感兴趣的函数(例如 unlink 或 fopen)。

编译,链接它以-fPIC创建动态库,然后将其注入到二进制文件中,例如

LD_PRELOAD=/path/to/mylib.so ./binary

相关内容