Linuxで/proc/$pid/memを読む方法は?

Linuxで/proc/$pid/memを読む方法は?

これLinuxのproc(5)マニュアルページ/proc/$pid/mem「プロセスメモリにアクセスするために使用できるページ」を示します。しかし、自分で使ってみると私に

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

catなぜ私の記憶()を印刷できないのですか/proc/self/mem?シェルのメモリを印刷しようとしたときに発生する奇妙な「該当プロセスなし」エラーは何ですか(明らか/proc/$$/memなプロセスが存在するようです)?それではどうやって読むことができますか/proc/$pid/mem

答え1

/proc/$pid/maps

/proc/$pid/memShow $pid メモリ内容はプロセスと同じ方法でマップされます。つまり、オフセットのバイトX擬似ファイルのバイトはアドレスのバイトと同じです。X進行中です。プロセスがアドレスがマップされていない場合、ファイルの対応するオフセットから読み取られたときに返されますEIO(入力/出力エラー)。たとえば、最初のバイトを読み取るとプロセスの最初のページがマップされないため、常にI / Oエラーが発生します。したがって、NULL誤って物理メモリにアクセスするのではなく、ポインタの逆参照は完全に失敗します。/proc/$pid/mem

プロセスメモリのどの部分がマッピングされているかを調べる方法は、を読むことです/proc/$pid/maps。ファイルには、次のようにマップされた領域ごとに1行が含まれています。

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

最初の2つの数字は領域の境界です(最初と最後のバイトのアドレス、16進数)。次の列には、権限が含まれ、ファイルマップの場合、ファイルに関するいくつかの情報(オフセット、デバイス、inode、および名前)が含まれています。よりproc(5)マンページまたはLinux /proc/id/mapsについてより多くの情報を知りたいです。

これは、独自のメモリの内容をダンプする概念証明スクリプトです。

#! /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

[次の内容は歴史的に興味深いものです。現在、カーネルでは動作しません。 ]

~からカーネルバージョン3.3/proc/$pid/mem、マップされたオフセットにのみアクセスし、権限追跡がある限り正常にアクセスできます(ptrace読み取り専用アクセスの場合)。しかし、古いカーネルにはいくつかの追加の問題があります。

別のプロセスの擬似ファイルを読み取ろうとするとmem動作しません。ESRCH(No such process) エラーが発生します。

/proc/$pid/mem()に対する権限は、r--------必要以上に許容されます。たとえば、setuidプロセスのメモリを読み取ることはできません。また、プロセスが変更中にプロセスのメモリを読み取ろうとすると、読者がメモリに対して一貫性のない視点を持つ可能性があります。によると)。このlmlスレッド、詳細はわかりませんが)。したがって、追加の確認が必要です。

  • 読みたいプロセスは、以下を/proc/$pid/mem使用するプロセスにリンクする必要があります。ptraceフラグ付きPTRACE_ATTACH。これは、プロセスデバッグを開始したときにデバッガが実行するアクションです。straceプロセスへのシステムコールによって実行されるアクション。リーダーが読み取りを終了したら、フラグを使用して呼び出して/proc/$pid/mem分離する必要があります。ptracePTRACE_DETACH
  • 観察されたプロセスは実行してはいけません。通常、呼び出しはターゲットptrace(PTRACE_ATTACH, …)プロセスを停止しますが(信号送信STOP)、競合状態(信号転送は非同期です)があるため、トラッカーが呼び出す必要がありますwait(文書に記載されているように)。ptrace(2))。

ルートとして実行されているプロセスは、呼び出しなしですべてのプロセスのメモリを読み取ることができますが、ptrace観察されたプロセスは停止する必要があります。それ以外の場合、読み取りは引き続き返されますESRCH

Linuxカーネルのソースコードには、プロセス固有のエントリを提供するコードが/procあります。fs/proc/base.c、読む機能/proc/$pid/memは次のとおりです。mem_read。追加のチェックは以下によって行われます。check_mem_permission

以下は、プロセスに接続し、memそのファイルのブロックを読み取るためのいくつかのサンプルCコードです(エラーチェックを省略)。

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);

/proc/$pid/mem別のスレッドにダンプするための概念証明スクリプトを公開しました。

答え2

gdbのこのコマンドはメモリを確実にダンプします。

gcore pid

ダンプ容量が大きい可能性があるため、-o outfile現在のディレクトリに十分なスペースがない場合は使用してください。

答え3

実行すると、cat /proc/$$/mem変数は$$独自のpidを挿入するbashによって評価されます。次に、cat別のPIDを使用してプログラムを実行します。結局、cat親プロセスのメモリを読み取ろうとします。bash権限のないプロセスは自分のメモリ空間のみを読み取ることができるため、カーネルはこれを拒否します。

例は次のとおりです。

$ echo $$
17823

計算は$$17823です。どのプロセスかを見てみましょう。

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

これが私の現在のシェルです。

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

ここでも$$私のシェルである17823と評価されます。cat私のシェルのメモリ空間を読み取ることができません。

答え4

bashを使用して読み取りを実行することもできます。dd(1)

上記のコマンドの一部を持たない制限的で基本的なUnixシステムを使用している場合(同様のコマンドpythonまで)memdump

が利用可能dd(1)で、ほとんどの制限されたUnix環境で動作します。

プロセスの最初の数バイトをダンプする例:

$ dd if=/proc/1337/mem of=/tmp/dump bs=1 skip=$((0x400000)) count=128

その後利用できます

hexdump -Cv ./tmp/dump

関連情報