/proc/$pid/maps
/proc/$pid/mem
プロセスと同じ方法でマップされた $pid のメモリの内容、つまりオフセット x のバイトを示します 疑似ファイル内のアドレス x のバイトと同じです 過程の中で。プロセスでアドレスがマップされていない場合、ファイル内の対応するオフセットから読み取ると、EIO
が返されます。 (入出力エラー)。たとえば、プロセスの最初のページはマップされないため (そのため、NULL
を逆参照する ポインタは実際のメモリに意図せずにアクセスするのではなく、きれいに失敗します)、 /proc/$pid/mem
の最初のバイトを読み取ります 常に I/O エラーが発生します。
プロセス メモリのどの部分がマップされているかを調べる方法は、/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 進数で表したもの) です。次の列には権限が含まれており、これがファイル マッピングの場合は、ファイルに関する情報 (オフセット、デバイス、i ノード、および名前) があります。 proc(5)
を参照してください 詳細については、man ページまたは 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
が返されます (そのようなプロセスはありません) エラーです。
/proc/$pid/mem
の権限 (r--------
)は、本来あるべきものよりもリベラルです。たとえば、setuid プロセスのメモリを読み取ることはできません。さらに、プロセスが変更している間にプロセスのメモリを読み取ろうとすると、リーダーにメモリの一貫性のないビューが表示される可能性があり、さらに悪いことに、Linux カーネルの古いバージョンをトレースできる競合状態がありました (この lkml スレッドによると、私は詳細は不明)。したがって、追加のチェックが必要です:
/proc/$pid/mem
から読み込みたいプロセスptrace
を使用してプロセスにアタッチする必要がありますPTRACE_ATTACH
で 国旗。これは、デバッガーがプロセスのデバッグを開始するときに行うことです。それは何strace
プロセスのシステムコールに対して行います。リーダーが/proc/$pid/mem
からの読み取りを終了したら 、ptrace
を呼び出してデタッチする必要がありますPTRACE_DETACH
で フラグ。- 監視対象のプロセスが実行されていてはなりません。通常は
ptrace(PTRACE_ATTACH, …)
を呼び出します ターゲットプロセスを停止します (STOP
を送信します) シグナル)、しかし競合状態 (シグナル配信は非同期) があるため、トレーサーはwait
を呼び出す必要があります (ptrace(2)
に記載されているとおり) ).
root として実行されているプロセスは、ptrace
を呼び出す必要なく、任意のプロセスのメモリを読み取ることができます。 、しかし、監視されたプロセスを停止する必要があります。そうしないと、読み取りはまだ ESRCH
を返します .
Linux カーネル ソースでは、/proc
でプロセスごとのエントリを提供するコード fs/proc/base.c
にあります 、および /proc/$pid/mem
から読み取る関数 mem_read
です .追加のチェックは check_mem_permission
によって実行されます .
プロセスにアタッチして mem
のチャンクを読み取る C コードのサンプルを次に示します。 file (エラーチェック省略):
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
をダンプするための概念実証スクリプトを既に投稿しました 別のスレッドで。
このコマンド (gdb から) は確実にメモリをダンプします:
gcore pid
ダンプは大きくなる可能性があります。-o outfile
を使用してください 現在のディレクトリに十分なスペースがない場合。
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
シェルのメモリ空間を読み取れません.