Zapis do nieistniejącego pliku

Dziś zagadał do mnie kumpel na jabberze z ciekawym pytaniem:

<Maho> wiesz może jak dobrać się do pliku którego znam inode

<Maho> ale plik już nie ma nazwy?

Chodziło o to, żeby zapisać coś do pliku, otwartego przez jakiś proces, ale
już skasowanego z systemu plików. Alternatywą było zapisanie coś do rurki
(pipe) między dwoma procesami.

Pierwsze co mi przyszło do głowy, to echo "cokolwiek" >>
/proc/${pid}/fd/${fd}
, ale kumpel stwierdził, że to nie zadziała, bo
w /proc są symlinki (o tym dalej).

No to trzeba było wymyślić coś innego. Skoro jakiś proces może zapisywać do
tego pliku, no najlepiej byłoby zmusić ten proces do zapisania tego co chcemy.
Ale jak się podpiąć pod działający proces? Prosto: gdb.

No to przygotujmy sobie środowisko testowe, dwa skrypty:

  • skrypt1.sh:
    #!/bin/sh
    
    while : ; do
            echo PUK
            sleep 1
    done | ./skrypt2.sh
    
  • skrypt2.sh:
    #!/bin/sh
    
    cat
    

Drugiego skryptu mogłoby nie być — wystarczył by sam ‚cat’, ale niech
będzie, że to dwa skrypty się ze sobą komunikują. Po uruchomieniu,
skrypt1.sh uruchamia skrypt2.sh i wysyła do niego
PUK co sekundę. skrypt2.sh po prostu wypluwa to „PUK” na
standardowe wyjście.

No więc próbujemy, w jednym terminalu uruchamiamy skrypt1.sh:

$ ./skrypt1.sh
PUK
PUK
PUK
PUK
PUK

Teraz spróbujmy zlokalizować tę rurkę między skryptami:

$ /usr/sbin/lsof -c skrypt1.sh | grep pipe
skrypt1.s 11108 jacek    1w  FIFO    0,5          40246 pipe
skrypt1.s 11108 jacek   14w  FIFO    0,5          40246 pipe

Są dwie, mniejsza z tym skąd, spróbujemy się podpiąć pod tę drugą. Znamy
identyfikator procesu (pid=10980) oraz numer deskryptora pliku (fd=14). To
powinno wystarczyć. No to podpinamy się pod proces:

$ gdb --pid=11108
GNU gdb 6.5
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i686-pld-linux".
Attaching to process 11108
Reading symbols from /bin/ksh...(no debugging symbols found)...done.
Using host libthread_db library "/lib/libthread_db.so.1".
Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2

warning: Lowest section in system-supplied DSO at 0xffffe000 is .hash at ffffe0b4
(no debugging symbols found)
0xffffe405 in __kernel_vsyscall ()
(gdb)

No to się podpięliśmy… Skrypt został zatrzymany i przestał wyświetlać
PUK. Teraz tylko należy wywołać funkcję write(). Ale jak?
Wydaje się, że nie ma żadnego polecenia do uruchamiania funkcji systemowych,
czy bibliotecznych… Ale jest print, który pozwala wyświetlić
wartość wyrażenia w C… a wyrażeniem może być wywołanie funkcji…
spróbujmy:

(gdb) print write(14, "BU!\n", 4)
$1 = 4

…czyli jakby zadziałało i zapisało 4 znaki. No to co jest na terminalu ze
skryptami? Oczywiście wysłane właśnie BU!. Można jeszcze sobie coś tak
popisać, a potem wznowić wykonywanie skryptu:

(gdb) quit
The program is running.  Quit anyway (and detach it)? (y or n) y
Detaching from program: /bin/ksh, process 11108

A teraz, wróćmy do wspomnianego echo "cokolwiek" >>
/proc/${pid}/fd/${fd}
… Przed napisaniem tego wpisu jeszcze sprawdziłem jaki do da efekt. I:

$ echo "cokolwiek" > /proc/11108/fd/14

Dało na terminalu ze skryptami:

PUK
PUK
cokolwiek
PUK
PUK

A więc /proc/.../fd/... działa wystarczająco dobrze! Jednak
sztuczka z gdb i tak mi się spodobała, więc ją tu opisałem. Ma
swój potencjał, bo oprócz pisania do pliku pozwala na różne inne rzeczy.
Oczywiście, krzywdę sobie w ten sposób też można zrobić. :-)

7 uwag do wpisu “Zapis do nieistniejącego pliku

  1. No dobra, a coś takiego: mam plik film.avi i odpalam go mplayerem:

    mplayer plik.avi

    i na drugiej konsoli go kasuję. teraz pliku nie ma, ale mplayer jeszcze go czyta, czy da się podpiąć pod inode, gdzie leży ten plik (bo jeszcze leży, póki działa mplayer) i odzyskać? 🙂

    Polubienie

  2. @AlchemyX: Też się nad tym zastanawiałem. IMHO powinno się dać i powinien temu służyć odpowiednik funkcji link() przyjmujący numer deskryptora pliku zamiast ścieżki w pierwszym argumencie… Problem w tym, że takiej funkcji nie znalazłem. Ale może jeszcze coś wymyślę… 🙂

    Polubienie

  3. No to mogłoby być ciekawe, bo zdarzały się kiedyś wtopy, a można było dany plik odzyskać bez dłubania w backupu, jedynie przypinając go właśnie pod nazwę pliku :). No nic, wierzę w Ciebie, bo to wybiega daleko poza moją wiedzę i pojmowanie świata 😉

    Polubienie

  4. Próbowałem i z podpięciem pod nazwę nic nie wykombinowałem. Ale zawsze można zrobić kopię:

    cat /proc/$pid/fd/$fd > nowa_nazwa

    W takim przypadku trzeba oczywiście mieć dość miejsca na dysku.

    Polubienie

  5. Zawsze coś :). Co ciekawe

    alchemyx@cerber /proc $ ps aux | grep MOV
    alchemyx 11486 1.0 1.4 130932 15388 pts/3 S+ 22:39 0:00 mplayer MOV00002.3gp
    alchemyx 11487 0.0 0.2 124940 2804 pts/3 S+ 22:39 0:00 mplayer MOV00002.3gp
    alchemyx 11497 0.0 0.0 3104 732 pts/4 R+ 22:39 0:00 grep —colour=auto MOV
    alchemyx@cerber /proc $ cd 11486
    alchemyx@cerber /proc/11486 $ ls
    auxv clear_refs cmdline cwd environ exe fd fdinfo maps mem mounts mountstats oom_adj oom_score root seccomp smaps stat statm status task wchan
    alchemyx@cerber /proc/11486 $ cd fd
    alchemyx@cerber /proc/11486/fd $ ls
    0 1 2 3 4 6
    alchemyx@cerber /proc/11486/fd $ ls -la
    razem 0
    dr-x——— 2 alchemyx users 0 wrz 13 22:39 .
    dr-xr-xr-x 5 alchemyx users 0 wrz 13 22:39 ..
    lrwx——— 1 alchemyx users 64 wrz 13 22:39 0 -> /dev/pts/3
    lrwx——— 1 alchemyx users 64 wrz 13 22:39 1 -> /dev/pts/3
    lrwx——— 1 alchemyx users 64 wrz 13 22:39 2 -> /dev/pts/3
    lr-x——— 1 alchemyx users 64 wrz 13 22:39 3 -> /tmp/MOV00002.3gp (deleted)
    lrwx——— 1 alchemyx users 64 wrz 13 22:39 4 -> socket:[5065297]
    lrwx——— 1 alchemyx users 64 wrz 13 22:39 6 -> /dev/snd/pcmC0D0p

    Czyli niby widzi ten plik, ale jest deleted. Hm. Twardy link z ln niestety nie działa 🙂

    alchemyx@cerber /proc/11486/fd $ ln 3 /tmp/qpa
    ln: tworzenie dowiązania zwykłego `/tmp/qpa’ => `3’: Błędne dowiązanie między urządzeniami

    Polubienie

Co o tym sądzisz?