Может ли мой процесс попасть в другой участок памяти?
Происходит трансляция адресов через MMU. Получается, в селектор записывается номер дескриптора и смещение, потом в таблице GDT (LDT) находится нужная запись например с правами и прочее, дальше страничное отображение памяти на физическую. А, предположим, я узнал, где у другого процесса что лежит и через селектор сделаю запрос, чтобы он выдал тот участок памяти. Может быть такое или я что-то не понимаю в этой логике отображение виртуального адреса на физический и прав доступа?
Ответы (2 шт):
У вас не получится. Операционная система при первом обращении к этой памяти перехватит управление и проверит, принадлежит ли она вашему процессу, и если нет - процесс получит SEGFAULT и будет прекращен.
Обращение к виртуальной памяти абсолютно прозрачно для процесса и проходит под контролем ОС, которая просто не даст вам выйти из вашего кусочка памяти. В то же время, любые инструкции CPU, которые могли бы эту прозрачность нарушить являются привилегированными и обычным процессам не доступны.
Однако ОС может нам и помочь. Например, в Linux, если ваш процесс запускается под root, есть целых два варианта заглянуть в память другого процесса:
Linux предоставляет доступ ко всей физической памяти через устройство
/dev/mem, с которыми можно обращаться, как с обычными файлами. Таким образом можно сделать полный дамп физической памяти с помощьюddи копаться уже в нем.Также Linux предоставляет доступ ко всему виртуальному адресному пространству каждого процесса через файл
/proc/[PID]/mem, а карта отображения лежит в файле/proc/[PID]/maps. Таким образом можно сделать дамп нужной секции или всей памяти отдельного процесса.
Пример
Напишем небольшую программу, которая будет исполнять роль жертвы:
#include <stdio.h>
#include <unistd.h>
static const char haystack[] = "haystack";
int
main(void)
{
while (1) {
puts(haystack);
sleep(5);
}
}
Скомпилируем её и запустим:
gcc -O0 -g victim.c -o victim && ./victim
Она будет выводить строку haystack каждые 5 секунд. Теперь давайте попробуем изменить строку, которую выводит жертва путем изменения данных в ее виртуальном адресном пространстве.
Для начала найдем PID жертвы:
$ pgrep victim
4595
Теперь подключим к запущенному процессу GDB и найдем адрес переменной haystack:
$ yes | sudo gdb -p 4595 -ex "p &haystack" -ex "quit" | grep haystack
$1 = (const char (*)[9]) 0x558f2b2ae008 <haystack>
Теперь самое интересное. Напишем программу, которая будет писать несколько байт по определенному адресу в виртуальном адресном пространстве жертвы. Для этого откроем файл /proc/4595/mem, сместимся до нужного нам адреса и выполним write:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#define PID "4595"
#define OFFSET 0x558f2b2ae008
#define DATA "dragons"
int
main(void) {
/* Открываем файл. */
int f = open("/proc/" PID "/mem", O_WRONLY);
/* Смещаемся до нужного адреса. */
lseek(f, OFFSET, SEEK_SET);
/* Пишем нужные байты. */
write(f, DATA, sizeof(DATA));
}
Если запустить программу под root и подождать несколько секунд до следующего вывода жертвы, то увидим что теперь жертва выводит dragons. Кажется, у нас получилось?
† Для наглядности сделал скриншот работы программ, который можно найти здесь.
‡ Интересно, что man 5 proc пишет, что к файлам /proc/[PID]/mem можно обратиться только через open, read и lseek, т. е. писать в них нельзя. Видимо, эта страница мануала устарела, потому что запрет на запись в /proc/[PID]/mem был отменен в этом коммите ядра. Хотя, может быть, проверку вернули — в общем случае запись в /proc/[PID]/mem будет зависеть от версии ядра. Также смотрите статью на LWN про эту уязвимость и связанный ответ на SO.