프로세스에 의해 생성되고 삭제된 /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

그런 다음 중단점에 도달할 때마다 해당 디렉터리의 파일을 삭제할지 확인하고 그 안에 있는 파일을 확인하거나 다른 곳에 복사하세요. cont실행을 재개하려면(다음 중단점까지) gdb에 입력하세요 .

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

여기서 중단점은 unlinkat()x86_64 Linux 시스템 내부 항목의 시스템 호출에 배치됩니다. 여기서 시스템 호출의 처음 두 매개변수는 및 레지스터에 있습니다.test/tmp/tmp.HudBncQ4Nirdirsi

strace시스템 호출을 호출할 때 프로세스에 신호(예: 일시 중지)를 주입할 수 있지만 strace -e inject=unlink,unlinkat,rmdir:signal=STOPAFAICT는 항상 그렇습니다.뒤쪽에파일이 삭제되면 시스템 호출이 반환됩니다.

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여기서는 의미) 한 척합니다.허가가 거부되었습니다) 실제로 전화하지 않고.

gdbstrace이미 실행 중인 프로세스에 각각 사용/연결할 수 있습니다 . 또한 포크와 실행을 추적하고 하위 항목을 추적하여 상위 항목에 연결하고 하위 항목의 연결 해제를 모니터링하거나 하이재킹할 수 있도록 지시할 수도 있습니다( 및 의 설정 참조).--pid <the-process-id>-p <the-process-id>-fstracefollow-*gdb

답변2

내가 원하는 것을 정확히 수행하는 inotify-tools를 사용하여 이 쉘 스크립트를 찾았습니다(저자: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

내가 다양한 기능(듣기)에 사용한 솔루션은 관심 있는 기능(예: unlink 또는 fopen)을 재정의하는 간단한 동적 라이브러리를 만드는 것입니다.

컴파일하고 링크하여 -fPIC동적 라이브러리를 만든 다음 바이너리에 삽입합니다.

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

관련 정보