Могу ли я защитить от записи каждую страницу в адресном пространстве процесса Linux?
Мне интересно, есть ли способ защиты от записи каждой страницы в адресном пространстве процесса Linux (изнутри самого процесса, посредствомmprotect()
). Под "каждой страницей" я подразумеваю каждую страницу адресного пространства процесса, на которую может записать обычная программа, работающая в пользовательском режиме, то есть текст программы, константы, глобальные переменные и куча, но я был бы счастлив только с константами, глобальными переменными и кучей. Я не хочу защищать стек от записи - это кажется плохой идеей.
Одна проблема в том, что я не знаю, с чего начать защиту памяти от записи. Смотря на /proc/pid/maps
, который показывает разделы памяти, используемые для данного pid, они, кажется, всегда начинаются с адреса0x08048000
, с текстом программы. (В Linux, насколько я могу судить, память процесса размещается с текстом программы внизу, затем константами выше этого, затем глобальными значениями, затем кучей, а затем пустым пространством переменного размера в зависимости от размера кучи или стека, а затем стек, растущий вниз из верхней части памяти по виртуальному адресу 0xffffffff
.) Есть способ узнать, где находится вершина кучи (вызывая sbrk(0)
, который просто возвращает указатель на текущий "разрыв" (т. е. вершину кучи), но на самом деле не является способом определить, где начинается куча.
Если я попытаюсь защитить все страницы от 0x08048000
до перерыва, я в конечном итоге получаю mprotect: Cannot allocate memory
ошибка. Я не знаю почему mprotect
будет выделять память в любом случае - и Google не очень помогает. Есть идеи?
Кстати, причина, по которой я хочу это сделать, заключается в том, что я хочу создать список всех страниц, которые записываются во время выполнения программы, и способ, которым я могу думать об этом, заключается в защите от записи всех страниц, пусть любая попытка записи вызывает ошибку записи, затем реализует обработчик ошибок записи, который добавит страницу в список и затем снимет защиту от записи. Я думаю, что знаю, как реализовать обработчик, если бы я только мог понять, какие страницы защищать и как это сделать.
Спасибо!
2 ответа
Вы получаете ENOMEM
от mprotect()
если вы попытаетесь вызвать его на страницах, которые не отображаются.
Лучше всего открыть /proc/self/maps
и читать его по очереди fgets()
чтобы найти все сопоставления в вашем процессе. Для каждого доступного для записи отображения (указано во втором поле), которое не является стеком (указано в последнем поле), вызовите mprotect()
с правильным базовым адресом и длиной (рассчитывается из начального и конечного адресов в первом поле).
Обратите внимание, что на этом этапе вам необходимо настроить обработчик ошибок, потому что процесс чтения maps
Сам файл, скорее всего, приведет к записи в вашем адресном пространстве.
Начните просто. Защитите от записи несколько страниц и убедитесь, что ваш обработчик сигналов работает на этих страницах. Тогда беспокойтесь о расширении области защиты. Например, вам, вероятно, не нужно защищать от записи секцию кода: операционные системы могут реализовывать семантику защиты от записи или исполнения в памяти, которая предотвратит запись разделов кода в: