/proc/$pid/maps
/proc/$pid/mem
показує вміст пам'яті $ pid, відображену так само, як у процесі, тобто байт при зміщенні x у псевдофайлі такий же, як байт за адресою x у процесі. Якщо адреса не відображається в ході процесу, зчитування з відповідного зміщення у файлі повертається EIO
(помилка вводу / виводу). Наприклад, оскільки перша сторінка в процесі ніколи не відображається (таким чином, що перенаправлення NULL
покажчика не вдається чисто, а не ненавмисно отримати доступ до фактичної пам'яті), зчитування першого байта /proc/$pid/mem
завжди приводить до помилки вводу / виводу.
Спосіб дізнатися, які частини картографічної пам'яті відображені, - це прочитати /proc/$pid/maps
. Цей файл містить один рядок у картографічному регіоні, виглядає так:
08048000-08054000 r-xp 00000000 08:01 828061 /bin/cat
08c9b000-08cbc000 rw-p 00000000 00:00 0 [heap]
Перші два числа - це межі області (адреси першого байта і байта після останнього, в гексах). У наступному стовпці містяться дозволи, тоді є деяка інформація про файл (зсув, пристрій, введення та ім'я), якщо це відображення файлу. Додаткову інформацію див. У розділі 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", '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
Якщо ви спробуєте прочитати з mem
псевдофайлу іншого процесу, він не працює: ви отримуєте ESRCH
помилку (Немає такого процесу).
Дозволи на /proc/$pid/mem
( r--------
) є більш ліберальними, ніж те, що має бути у випадку. Наприклад, ви не повинні мати можливість читати пам'ять процесу налаштування. Крім того, спроба прочитати пам'ять процесу, коли процес змінюється, може дати читачеві непослідовність уявлення про пам'ять, і ще гірше, були гоночні умови, які могли простежити старіші версії ядра 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
файлу (перевірка помилок пропущена):
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
на інший потік .