/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 シェルのメモリ空間を読み取れません.