Могу ли я защитить от записи каждую страницу в адресном пространстве процесса 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 Сам файл, скорее всего, приведет к записи в вашем адресном пространстве.

Начните просто. Защитите от записи несколько страниц и убедитесь, что ваш обработчик сигналов работает на этих страницах. Тогда беспокойтесь о расширении области защиты. Например, вам, вероятно, не нужно защищать от записи секцию кода: операционные системы могут реализовывать семантику защиты от записи или исполнения в памяти, которая предотвратит запись разделов кода в:

Другие вопросы по тегам