GNU/Linux >> Znalost Linux >  >> Linux

Linux – Jak číst z /proc/$pid/mem pod Linuxem?

Linux proc(5) manuálová stránka mi říká, že /proc/$pid/mem „lze použít k přístupu na stránky paměti procesu“. Ale přímý pokus o jeho použití mi dává pouze

$ cat /proc/$$/mem /proc/self/mem
cat: /proc/3065/mem: No such process
cat: /proc/self/mem: Input/output error

Proč není cat dokáže vytisknout vlastní paměť (/proc/self/mem )? A co je to za podivnou chybu „žádný takový proces“, když se pokouším vytisknout paměť shellu (/proc/$$/mem , proces evidentně existuje)? Jak mohu číst z /proc/$pid/mem , tedy?

Přijatá odpověď:

/proc/$pid/maps

/proc/$pid/mem zobrazuje obsah paměti $pid mapovaný stejným způsobem jako v procesu, tj. bajt na offsetu x v pseudosouboru je stejný jako bajt na adrese x v průběhu. Pokud je adresa v procesu nenamapována, čtení z odpovídajícího offsetu v souboru vrátí EIO (Chyba vstupu/výstupu). Například, protože první stránka v procesu není nikdy mapována (takže dereferencování NULL ukazatel selže čistě, místo aby neúmyslně přistupoval ke skutečné paměti), čte první bajt z /proc/$pid/mem vždy způsobí chybu I/O.

Způsob, jak zjistit, jaké části paměti procesu jsou mapovány, je přečíst si /proc/$pid/maps . Tento soubor obsahuje jeden řádek na mapovanou oblast a vypadá takto:

08048000-08054000 r-xp 00000000 08:01 828061     /bin/cat
08c9b000-08cbc000 rw-p 00000000 00:00 0          [heap]

První dvě čísla jsou hranice oblasti (adresy prvního bajtu a bajtu za posledním, v hexa). Další sloupec obsahuje oprávnění, pak jsou zde nějaké informace o souboru (offset, zařízení, inode a název), pokud se jedná o mapování souboru. Viz proc(5) man page nebo Understanding Linux /proc/id/maps, kde najdete další informace.

Zde je skript proof-of-concept, který vypíše obsah své vlastní paměti.

#! /usr/bin/env python
import re
maps_file = open("/proc/self/maps", 'r')
mem_file = open("/proc/self/mem", 'rb', 0)
output_file = open("self.dump", 'wb')
for line in maps_file.readlines():  # for each mapped region
    m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])', line)
    if m.group(3) == 'r':  # if this is a readable region
        start = int(m.group(1), 16)
        end = int(m.group(2), 16)
        mem_file.seek(start)  # seek to region start
        chunk = mem_file.read(end - start)  # read region contents
        output_file.write(chunk)  # dump contents to standard output
maps_file.close()
mem_file.close()
output_file.close()

/proc/$pid/mem

[Následuje pro historické zajímavosti. Neplatí pro aktuální jádra.]

Od verze 3.3 jádra máte přístup k /proc/$pid/mem normálně, pokud k němu přistupujete pouze s namapovanými offsety a máte oprávnění je sledovat (stejná oprávnění jako ptrace pro přístup pouze pro čtení). Ale ve starších jádrech se objevily další komplikace.

Pokud se pokusíte číst z mem pseudosoubor jiného procesu, nefunguje to:dostanete ESRCH (Žádný takový proces) chyba.

Oprávnění na /proc/$pid/mem (r-------- ) jsou liberálnější, než by mělo být. Například byste neměli být schopni číst paměť setuid procesu. Kromě toho pokus o načtení paměti procesu, zatímco proces ji upravuje, by mohl čtenáři poskytnout nekonzistentní pohled na paměť, a co je horší, existovaly rasové podmínky, které mohly sledovat starší verze linuxového jádra (podle tohoto vlákna lkml, ačkoli jsem neznám podrobnosti). Jsou tedy nutné další kontroly:

  • Proces, který chce číst z /proc/$pid/mem musí se připojit k procesu pomocí ptrace pomocí PTRACE_ATTACH vlajka. To je to, co debuggery dělají, když začnou ladit proces; je to také to, co strace provádí systémová volání procesu. Jakmile čtečka dokončí čtení z /proc/$pid/mem , měl by se odpojit voláním ptrace pomocí PTRACE_DETACH vlajka.
  • Pozorovaný proces nesmí běžet. Normálně volá ptrace(PTRACE_ATTACH, …) zastaví cílový proces (odešle STOP signál), ale existuje spor (doručování signálu je asynchronní), takže sledovač by měl zavolat wait (jak je zdokumentováno v ptrace(2) ).
Související:Nechat NumLock vždy zapnutý?

Proces běžící jako root může číst paměť libovolného procesu, aniž by musel volat ptrace , ale pozorovaný proces musí být zastaven, jinak bude čtení stále vracet ESRCH .

Ve zdrojovém kódu linuxového jádra kód poskytující položky pro jednotlivé procesy v /proc je v fs/proc/base.c a funkce ke čtení z /proc/$pid/mem je mem_read . Dodatečnou kontrolu provádí check_mem_permission .

Zde je ukázkový kód C, který lze připojit k procesu a přečíst si jeho část mem soubor (kontrola chyb vynechána):

sprintf(mem_file_name, "/proc/%d/mem", pid);
mem_fd = open(mem_file_name, O_RDONLY);
ptrace(PTRACE_ATTACH, pid, NULL, NULL);
waitpid(pid, NULL, 0);
lseek(mem_fd, offset, SEEK_SET);
read(mem_fd, buf, _SC_PAGE_SIZE);
ptrace(PTRACE_DETACH, pid, NULL, NULL);

Již jsem zveřejnil skript proof-of-concept pro dumping /proc/$pid/mem v jiném vláknu.


Linux
  1. Jak Linux zpracovává více po sobě jdoucích oddělovačů cest (/home////username///soubor)?

  2. Linux – jak se liší symbolický odkaz /proc//exe od běžných symbolických odkazů?

  3. Linux – Jak získat adresu IPv4 pro rozhraní z /proc?

  1. Linux – propojení /proc/mnt s /proc/mounts?

  2. Linux – Jak zjistit jmenný prostor konkrétního procesu?

  3. Linux – Jak otestovat, zda je blokové zařízení pouze pro čtení z /sys nebo /proc?

  1. Jak číst jednu zprávu najednou z /var/mail?

  2. Jak získám cestu k procesu v Unixu / Linuxu

  3. /proc/[pid]/pagemaps a /proc/[pid]/maps | linux