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
から読み取るにはどうすればよいですか 、じゃあ?
承認された回答:
/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)
に記載されています 。
ルートとして実行されているプロセスは、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
をダンプするための概念実証スクリプトをすでに投稿しました 別のスレッドで。