GNU/Linux >> Znalost Linux >  >> Linux

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

/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 není adresa v procesu namapová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 /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 od mem pseudo-soubor 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, mohl čtenáři poskytnout nekonzistentní pohled na paměť, a co je horší, existovaly rasové podmínky, které mohly sledovat starší verze jádra Linuxu (podle tohoto vlákna lkml, i když 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 s 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 s PTRACE_DETACH vlajka.
  • Pozorovaný proces nesmí běžet. Normálně volá ptrace(PTRACE_ATTACH, …) zastaví cílový proces (odešle STOP signál), ale je zde podmínka závodu (doručování signálu je asynchronní), takže sledovač by měl zavolat wait (jak je zdokumentováno v ptrace(2) ).

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 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ákně.


Tento příkaz (z gdb) spolehlivě vypíše paměť:

gcore pid

Výpisy mohou být velké, použijte -o outfile pokud váš aktuální adresář nemá dostatek místa.


Když spustíte cat /proc/$$/mem proměnnou $$ je vyhodnocován pomocí bash, který vkládá vlastní pid. Poté provede cat který má jiný pid. Skončíte s cat pokoušíte se přečíst paměť bash , jeho nadřazený proces. Protože neprivilegované procesy mohou číst pouze svůj vlastní paměťový prostor, jádro to odmítne.

Zde je příklad:

$ echo $$
17823

Všimněte si, že $$ vyhodnotí na 17823. Podívejme se, který proces to je.

$ ps -ef | awk '{if ($2 == "17823") print}'
bahamat  17823 17822  0 13:51 pts/0    00:00:00 -bash

Je to můj aktuální shell.

$ cat /proc/$$/mem
cat: /proc/17823/mem: No such process

Zde opět $$ vyhodnotí na 17823, což je můj shell. cat nemohu číst paměťový prostor mého shellu.


Linux
  1. /proc/[pid]/pagemaps a /proc/[pid]/maps | linux

  2. Jak získat počet CPU/jádr v Linuxu z příkazového řádku?

  3. Jak zjistit, ze které složky běží proces?

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

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

  3. Jak nastavit swapnost procesu pro linux?

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

  2. Linux – Získávání informací o využití paměti procesu z /proc/pid/smaps?

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