/proc/$pid/maps
/proc/$pid/mem
memperlihatkan isi memori $ pid yang dipetakan dengan cara yang sama seperti dalam proses, yaitu byte pada offset x dalam file pseudo sama dengan byte pada alamat x dalam proses. Jika alamat tidak dipetakan dalam proses, bacalah dari offset yang sesuai dalam file yang dikembalikan EIO
(Kesalahan input / output). Sebagai contoh, karena halaman pertama dalam suatu proses tidak pernah dipetakan (sehingga dereferencing NULL
pointer gagal dengan bersih daripada mengakses memori yang sebenarnya), membaca byte pertama /proc/$pid/mem
selalu menghasilkan kesalahan I / O.
Cara untuk 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 yang terakhir, dalam heksa). Kolom berikutnya berisi izin, lalu 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 ini skrip proof-of-concept yang membuang konten dari ingatannya sendiri.
#! /usr/bin/env python
import re
maps_file = open("/proc/self/maps", 'r')
mem_file = open("/proc/self/mem", 'r', 0)
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
print chunk, # dump contents to standard output
maps_file.close()
mem_file.close()
/proc/$pid/mem
Jika Anda mencoba membaca dari file mem
pseudo dari proses lain, itu tidak berfungsi: Anda mendapatkan ESRCH
kesalahan (Tidak ada proses seperti itu).
Izin pada /proc/$pid/mem
( r--------
) lebih liberal daripada yang seharusnya. Misalnya, Anda seharusnya tidak dapat membaca memori proses setuid. Lebih jauh lagi, mencoba membaca memori suatu proses ketika proses memodifikasi itu dapat memberikan pembaca pandangan yang tidak konsisten dari memori, dan lebih buruk lagi, ada kondisi ras yang dapat melacak versi Linux kernel yang lebih lama (menurut utas lkml ini , meskipun saya tidak tahu detailnya). Jadi diperlukan pemeriksaan tambahan:
- Proses yang ingin dibaca
/proc/$pid/mem
harus dilampirkan ke proses menggunakan ptrace
dengan PTRACE_ATTACH
bendera. Inilah yang dilakukan para debugger ketika mereka memulai proses debug; itu juga apa yang strace
dilakukan untuk panggilan sistem suatu proses. Setelah pembaca selesai membaca dari /proc/$pid/mem
, pembaca harus melepaskan dengan memanggil ptrace
dengan PTRACE_DETACH
bendera.
- Proses yang diamati tidak boleh berjalan. Biasanya panggilan
ptrace(PTRACE_ATTACH, …)
akan menghentikan proses target (mengirimkan STOP
sinyal), tetapi ada kondisi balapan (pengiriman sinyal asinkron), sehingga pelacak harus menelepon wait
(seperti yang didokumentasikan dalam ptrace(2)
).
Sebuah 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 kembali ESRCH
.
Dalam sumber kernel Linux, kode menyediakan entri per-proses di /proc
dalam fs/proc/base.c
, dan fungsi untuk membaca dari /proc/$pid/mem
adalah mem_read
. Pemeriksaan tambahan dilakukan oleh check_mem_permission
.
Berikut ini beberapa contoh kode C untuk dilampirkan ke suatu proses dan membaca sebagian dari mem
filenya (pengecekan 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 memasang skrip proof-of-concept untuk dibuang /proc/$pid/mem
di utas lain .