/proc/$pid/maps
/proc/$pid/mem
menampilkan isi memori $pid yang dipetakan dengan cara yang sama seperti dalam proses, yaitu, byte pada offset x dalam pseudo-file sama dengan byte pada alamat x dalam proses. Jika alamat tidak dipetakan dalam proses, pembacaan dari offset yang sesuai dalam file mengembalikan EIO
(Kesalahan masukan/keluaran). Misalnya, karena halaman pertama dalam suatu proses tidak pernah dipetakan (sehingga mendereferensi NULL
pointer gagal bersih daripada tanpa sengaja mengakses memori yang sebenarnya), membaca byte pertama dari /proc/$pid/mem
selalu menghasilkan kesalahan I/O.
Cara mengetahui bagian mana dari memori proses yang dipetakan adalah dengan membaca /proc/$pid/maps
. File ini berisi satu baris per wilayah yang dipetakan, terlihat seperti ini:
08048000-08054000 r-xp 00000000 08:01 828061 /bin/cat
08c9b000-08cbc000 rw-p 00000000 00:00 0 [heap]
Dua angka pertama adalah batas wilayah (alamat byte pertama dan byte setelah terakhir, dalam hexa). Kolom berikutnya berisi hak akses, kemudian ada beberapa informasi tentang file (offset, perangkat, inode dan nama) jika ini adalah pemetaan file. Lihat proc(5)
halaman manual atau Memahami Linux /proc/id/maps untuk informasi lebih lanjut.
Berikut adalah skrip proof-of-concept yang membuang konten memorinya sendiri.
#! /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
[Berikut ini untuk kepentingan sejarah. Itu tidak berlaku untuk kernel saat ini.]
Sejak kernel versi 3.3, Anda dapat mengakses /proc/$pid/mem
biasanya selama Anda mengakses hanya mengaksesnya di offset yang dipetakan dan Anda memiliki izin untuk melacaknya (izin yang sama seperti ptrace
untuk akses hanya baca). Tapi di kernel lama, ada beberapa komplikasi tambahan.
Jika Anda mencoba membaca dari mem
pseudo-file dari proses lain, tidak berfungsi:Anda mendapatkan ESRCH
Kesalahan (Tidak ada proses seperti itu).
Izin pada /proc/$pid/mem
(r--------
) lebih liberal dari yang seharusnya. Misalnya, Anda seharusnya tidak dapat membaca memori proses setuid. Selain itu, mencoba membaca memori proses saat proses memodifikasinya dapat memberi pembaca pandangan memori yang tidak konsisten, dan lebih buruk lagi, ada kondisi balapan yang dapat melacak versi kernel Linux yang lebih lama (menurut utas lkml ini, meskipun saya tidak tahu detailnya). Jadi diperlukan pemeriksaan tambahan:
- Proses yang ingin membaca dari
/proc/$pid/mem
harus dilampirkan ke proses menggunakanptrace
denganPTRACE_ATTACH
bendera. Inilah yang dilakukan para debugger ketika mereka mulai men-debug suatu proses; itu juga apastrace
tidak untuk panggilan sistem proses. Setelah pembaca selesai membaca dari/proc/$pid/mem
, itu harus dilepas dengan memanggilptrace
denganPTRACE_DETACH
bendera. - Proses yang diamati tidak boleh berjalan. Biasanya memanggil
ptrace(PTRACE_ATTACH, …)
akan menghentikan proses target (mengirimkanSTOP
sinyal), tetapi ada kondisi balapan (pengiriman sinyal tidak sinkron), sehingga pelacak harus memanggilwait
(seperti yang didokumentasikan dalamptrace(2)
).
Proses yang berjalan sebagai root dapat membaca memori proses apa pun, tanpa perlu memanggil ptrace
, tetapi proses yang diamati harus dihentikan, atau pembacaan akan tetap mengembalikan ESRCH
.
Di sumber kernel Linux, kode menyediakan entri per proses di /proc
ada di fs/proc/base.c
, dan fungsi untuk membaca dari /proc/$pid/mem
adalah mem_read
. Pemeriksaan tambahan dilakukan oleh check_mem_permission
.
Berikut beberapa contoh kode C untuk dilampirkan ke suatu proses dan membaca sebagian dari mem
file (pemeriksaan kesalahan dihilangkan):
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);
Saya sudah memposting skrip proof-of-concept untuk membuang /proc/$pid/mem
di utas lain.
Perintah ini (dari gdb) membuang memori dengan andal:
gcore pid
Dump bisa besar, gunakan -o outfile
jika direktori Anda saat ini tidak memiliki cukup ruang.
Saat Anda menjalankan cat /proc/$$/mem
variabel $$
dievaluasi oleh oleh bash yang menyisipkan pidnya sendiri. Ini kemudian mengeksekusi cat
yang memiliki pid berbeda. Anda berakhir dengan cat
mencoba membaca memori bash
, proses induknya. Karena proses non-istimewa hanya dapat membaca ruang memorinya sendiri, hal ini ditolak oleh kernel.
Berikut contohnya:
$ echo $$
17823
Perhatikan bahwa $$
mengevaluasi ke 17823. Mari kita lihat proses apa itu.
$ ps -ef | awk '{if ($2 == "17823") print}'
bahamat 17823 17822 0 13:51 pts/0 00:00:00 -bash
Ini shell saya saat ini.
$ cat /proc/$$/mem
cat: /proc/17823/mem: No such process
Di sini sekali lagi $$
mengevaluasi ke 17823, yang merupakan cangkang saya. cat
tidak dapat membaca ruang memori shell saya.