Linux proc(5)
halaman manual memberitahu saya bahwa /proc/$pid/mem
"dapat digunakan untuk mengakses halaman memori proses". Tetapi upaya langsung untuk menggunakannya hanya memberi saya
$ cat /proc/$$/mem /proc/self/mem
cat: /proc/3065/mem: No such process
cat: /proc/self/mem: Input/output error
Mengapa tidak cat
dapat mencetak memorinya sendiri (/proc/self/mem
)? Dan apa kesalahan "tidak ada proses seperti itu" yang aneh ketika saya mencoba mencetak memori shell (/proc/$$/mem
, jelas prosesnya ada)? Bagaimana saya bisa membaca dari /proc/$pid/mem
, lalu?
Jawaban yang Diterima:
/proc/$pid/maps
/proc/$pid/mem
menunjukkan isi memori $pid yang dipetakan dengan cara yang sama seperti pada proses, yaitu byte pada offset x dalam pseudo-file sama dengan byte di alamat x dalam proses. Jika alamat tidak dipetakan dalam proses, membaca dari offset yang sesuai dalam file akan mengembalikan EIO
(Kesalahan masukan/keluaran). Misalnya, karena halaman pertama dalam suatu proses tidak pernah dipetakan (sehingga dereferensi NULL
pointer gagal dengan bersih daripada mengakses memori aktual secara tidak sengaja), membaca byte pertama /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 yang terakhir, dalam heksa). 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 isi 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. Ini 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 dengan ptrace
untuk akses hanya baca). Namun pada kernel yang lebih lama, ada beberapa komplikasi tambahan.
Jika Anda mencoba membaca dari mem
pseudo-file dari proses lain, tidak berhasil:Anda mendapatkan ESRCH
(Tidak ada proses seperti itu) kesalahan.
Izin pada /proc/$pid/mem
(r--------
) lebih liberal dari yang seharusnya. Misalnya, Anda seharusnya tidak dapat membaca memori proses setuid. Lebih jauh lagi, mencoba membaca memori proses saat proses sedang memodifikasinya dapat memberikan 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 pemeriksaan tambahan diperlukan:
- Proses yang ingin dibaca dari
/proc/$pid/mem
harus melampirkan ke proses menggunakanptrace
denganPTRACE_ATTACH
bendera. Inilah yang dilakukan debugger ketika mereka mulai men-debug suatu proses; itu juga yangstrace
lakukan untuk panggilan sistem suatu proses. Setelah pembaca selesai membaca dari/proc/$pid/mem
, itu harus dilepaskan 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), jadi pelacak harus memanggilwait
(seperti yang didokumentasikan dalamptrace(2)
).
Sebuah proses yang berjalan sebagai root dapat membaca memori proses apapun, 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 sepotong 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 telah memposting skrip proof-of-concept untuk membuang /proc/$pid/mem
di utas lain.