我正在研究 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.HudBncQ4Ni
rdi
rsi
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>
-f
strace
follow-*
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