Не удалось открыть файл proc pid maps

Обновлено: 03.07.2024

При прохождении одного из онлайн тестов наткнулся на сложный вопрос. Вопрос формулируется примерно так:

Не удаётся прочитать файл /proc//mem даже из под рута из-за ошибки доступа (permission error). Как решить эту проблему? Код открывающий этот файл на чтение:

2. Послать сигналы SIGSTOP/SIGCONT:

Проверить атрибуты исполняемого файла и удалить immutable profile.

Это не работает из под рута. Нужно проверить пользовательский режим и задать разрешения.

Текст исходников приведён как есть, с сохранением опечаток в оригинале.

После чтения документации и компиляции и запуска кода, я обнаружил, что варианты 1 и 2 оба работают даже без рута. Но видимо какой-то один из них более правильный. Подскажите пожалуйста, какой из этих вариантов лучше подходит для доступа к памяти процесса?

если читал документацию, то должен понимать, что вариант (2), вообще говоря, не должен работать. код в студию — или в нём ошибка, или какой-то частный случай, или что-то не так с окружением. немного очитался в man 2 ptrace — думал, что оттуда следует, что ответ, очевидно — (1). Однако на последующих тестах заметил, что не могу воспроизвести отказ описанный в вопросе. возможно тест устарел? или это вопрос с какой-то подковыркой.

Чтение первого байта /proc/pid/mem всегда приводит к ошибке ввода-вывода. Так задумано. В этом файле надо читать со смещением. Смещение совпадает с реальным смещением в запущенной программе.

Что-бы узнать о смещениях памяти используемых программой нужно читать /proc/pid/maps , там указанны используемые регионы памяти.

Важно:

вы не сможете прочитать mem файл другого работающего процесса, получите ошибку Нет такого процесса .

если все же есть необходимость читать mem файл другого процесса, то необходимо присоединиться к процессу, используя ptrace() вызов. Но все не так просто, и процесс надо останавливать на время чтения.

процесс, выполняющийся от имени root, может читать память любого процесса без вызова ptrace, но наблюдаемый процесс должен быть остановлен, иначе все равно получите ошибку.

Есть ещё куча нюансов при работе с виртуальными файлами ядра, рекомендую знакомиться с исходными кодами кернеля если у вас возникают вопросы.

Вот примерный сценарий на C чтения mem файла по методу номер 2:

Посмотрите на mem_open и на check_mem_permission для понимания как там все устроено.

Я пытаюсь разобраться в двух файлах, упомянутых в названии. Я посмотрел, что это за биты; однако я не понимаю, как извлечь из них полезную информацию (или я просто неправильно подхожу к этому).

Позвольте мне объяснить: карты страниц - это довольно новый псевдо-файл с «особенностями», который содержит информацию о физическом фрейме виртуальных страниц, назначенных текущему [pid]. То есть, учитывая виртуальную страницу, которая начинается с адреса x, скажем «vas» для начала виртуального адреса, я могу проиндексировать файл карты страниц, используя vas, чтобы получить 64 бита сопоставленного физического фрейма страницы. Эти биты содержат информацию об этой виртуальной странице. Однако, когда я извлекаю кусочки и немного меняю, я теряюсь в том, что вижу.

Биты представлены следующим образом: 0-54 - номер кадра страницы, 55-60 - сдвиг страницы, 63-й бит - текущий бит, есть другие биты, которые меня мало интересуют. После того, как я сделал небольшое сопоставление с использованием адресов vas из / proc / [pid] / maps, кажется, что почти все страницы процесса меняются местами, т.е. 63-й бит всегда равен нулю. :(

Я предполагаю, что вопрос будет в том, как мне эффективно использовать карты страниц, чтобы получить эквивалентный физический адрес адреса, заданного / proc / [pid] / maps

Честно говоря, я опубликовал аналогичный вопрос, но несколько дней назад подход был немного другим.

Если кто-нибудь может пролить свет на этот вопрос, я был бы очень признателен.

Чтобы ответить на комментарий ниже: я читаю строку из / proc / [pid] / maps, и строки выглядят так:

00400000-00401000 r-xp 00000000 08:01 8915461 / home / janjust / my_programs / shared_mem 7ffffef1b000-7ffffef3c000 rw-p 00000000 00:00 0 [стек]

Затем я извлекаю количество виртуальных страниц, которых он касается, и индексирую двоичный файл / proc / [pid] / pagemaps, и для каждой виртуальной страницы я могу извлечь физическую страницу, которой она назначена.

Результат выглядит так:

00400000-00401000 r-xp 00000000 08:01 8915461 / home / janjust / my_programs / shared_mem num_pages: 1: 86000000001464C6

Один физический адрес для каждой виртуальной страницы в виртуальном диапазоне.

Код для чтения строки и извлечения физического адреса:

Однако, хотя я думаю, что получаю правильный результат, индекс, похоже, либо несоответствие типа, либо происходит что-то еще: например, выходные данные для строки [shared mem] в картах дают неправильный индекс; но я все еще могу просканировать двоичный файл и получить физический адрес страницы.

Пример этого вывода приведен ниже:

Хорошо, теперь, наконец, я должен сказать, что это в 64-битной ОС, и эта проблема не сохраняется в 32-битной ОС.

Я пытаюсь понять использование памяти моего встроенного приложения Linux. The /proc/pid/maps утилита / файл кажется хорошим ресурсом для просмотра деталей. К сожалению, я не понимаю всех колонок и записей.

есть ли хороший ресурс / документация для proc/pid/maps файловые утилиты/?

что означает анонимный индекс 0 записей? Это, кажется, некоторые из больших сегментов памяти.

в каждой строке /proc/$PID/maps описывает область непрерывной виртуальной памяти в процессе или потоке. Каждая строка имеет следующие поля:

  • адрес - это начальный и конечный адрес региона в адресном пространстве процесса
  • разрешения - это описывает, как можно получить доступ к страницам в регионе. Существует четыре различных разрешения: чтение, запись, выполнение и общий доступ. Если чтение/запись/выполнение инвалидов, - вместо r / w / x . Если область не shared, это частная, так что p появится вместо s . Если процесс пытается получить доступ к памяти способом, который не разрешен, создается ошибка сегментации. Разрешения могут быть изменены с помощью mprotect системный вызов.
  • смещение - если область была отображена из файла (используя mmap ), это смещение в файл, где начинается сопоставление. Если память не была сопоставлена с файлом, это просто 0.
  • устройства - если область была отображена из файла, это основной и второстепенный номер устройства (в шестнадцатеричном формате), где живет файл.
  • inode - если область была сопоставлена из файла, это номер файла.
  • путь - если область была отображена из файла, это имя файла. Это поле является пустым для анонимные сопоставленные регионы. Есть также специальные регионы с именами, как [heap] , [stack] или [vdso] . [vdso] означает виртуальный динамический общий объект. Он используется системными вызовами для переключения в режим ядра. вот хорошая статья об этом.

вы можете заметить много анонимных регионов. Они обычно создаются mmap но не прикреплены ни к одному файлу. Они используются для многих разных вещей, таких как общая память или буферы, а не выделено в куче. Например, я думаю, что библиотека pthread использует анонимные сопоставленные области в качестве стеков для новых потоков.

"понимание ядра Linux" 9.3. Регионы Памяти; 16.2. Отображение Памяти

"понимание менеджера виртуальной памяти Linux" 4.4 области памяти

отображение памяти не только используется для отображения файлов в память, но также является инструментом для запроса ОЗУ из ядра. Это те записи inode 0-ваш стек, куча, сегменты bss и многое другое

поле адреса-это адресное пространство в процессе, в котором картирование занимает.

поле perms представляет собой набор разрешений:

поле offset-смещение в файле;

dev-это устройство (major: minor);

inode-это индекс на этом устройстве.0 означает, что операции, связанные с memoryregion, как и в случае с BSS (неинициализированные данные).

поле pathname обычно будет файлом, который поддерживает отображение. Для файлов ELF вы можете легко координировать с поле смещения, глядя на поле смещения в ELF заголовки программы (readelf выступает -л).

Следующий контент взят из руководства man, которое можно получить, запустив команду (man 5 proc).

Сегмент кода каждой разделяемой библиотеки хранит двоичные исполняемые машинные инструкции, а сегмент кода файла ELF библиотеки отображается ядром в виртуальное пространство хранения;
Сегмент данных каждой разделяемой библиотеки хранит глобальные переменные, необходимые для выполнения программы.Ядро сопоставляет сегмент данных файла ELF с виртуальной памятью;
Сегмент кода пользователя, в котором хранятся исполняемые машинные инструкции в двоичной форме.Ядро отображает сегмент кода файла ELF в пространство виртуальной памяти;
Над разделом пользовательских данных находится раздел кода, в котором хранятся глобальные переменные, необходимые для выполнения программы.Ядро сопоставляет раздел данных файла ELF с пространством виртуальной памяти;
Под сегментом пользовательских данных находится куча, которая существует только тогда, когда вызывается malloc. Ядро сопоставляет анонимную память с пространством виртуальной памяти, а куча не вызывает malloc в программе. Не существует при данных обстоятельствах;
Под сегментом пользовательских данных находится стек. В качестве области временных данных процесса анонимная память отображается ядром в пространство виртуальной памяти. Пространство стека увеличивается от высокого адреса до Низкий адрес.

Proc / pid / maps показывает область памяти и права доступа, отображаемые процессом. Соответствующая операция, установленная в ядре, - это proc_pid_maps_op, а конкретная функция экспорта - show_map. Сегмент адресного пространства процесса в ядре представлен структурой vm_area_struct, а все адресные пространства хранятся в связанном списке task-> mm-> mmap.

Файл может быть отображен в область памяти процесса. Отображенный дескриптор файла хранится в домене vm_area_struct-> vm_file. Эта область памяти называется хорошо известной областью памяти. Напротив, она принадлежит анонимно отображаемой области памяти. Соответствующий анализ каждого элемента Vm_area_struct показан в следующей таблице:


Давайте посмотрим на следующий пример карты proc.

Каждое адресное пространство процесса описывается структурой vm_area_struct. Каждая показанная выше строка соответствует структуре vm_area_struct. Файл может быть сопоставлен с памятью. Vm_file из vm_area_struct хранит дескриптор файла. Это сопоставление называется именованным сопоставлением, и наоборот - анонимным сопоставлением. Далее четырнадцатый акт используется для объяснения содержания каждого случая.

Первый столбец: 08049000-0804a000 ----- диапазон виртуального адресного пространства этого отображения памяти, соответствующий vm_start и vm_end в vm_area_struct.

Второй столбец: rw-p ---- разрешение r-чтение, w-запись x-исполняемый файл p-private, соответствующий vm_flags.

Третий столбец: 00000000 ---- Для известного сопоставления он относится к смещению адреса сопоставления этого сегмента в файле, соответствующего vm_pgoff. Для анонимного сопоставления это vm_area_struct-> vm_start.

Четвертый столбец: fd: 00 ---- Номер устройства, которому принадлежит отображаемый файл, соответствующий vm_file-> f_dentry-> d_inode-> i_sb-> s_dev. Анонимное сопоставление равно 0. Среди них fd - это старший номер устройства, а 00 - младший номер устройства.

Пятый столбец: 3145810 ---- Номер индексного узла файла, соответствующий vm_file-> f_dentry-> d_inode-> i_ino, который соответствует содержимому, отображаемому ls-i. Анонимное сопоставление равно 0.

Шестой столбец: / home / lijz / code / pthread --- имя отображаемого файла. Для хорошо известного сопоставления это имя файла сопоставления, для анонимного сопоставления это функция этой памяти в процессе. [stack] указывает, что память этого сегмента используется как стек, [heap] используется как куча, и в других случаях ее нет.

После приведенного выше анализа значение каждого столбца в картах процессов становится очень ясным. Далее посмотрим на анализ каждой строки в каждой карте proc. Сегмент кода каждой разделяемой библиотеки хранит двоичные исполняемые машинные инструкции.Ядро отображает сегмент кода файла ELF библиотеки в виртуальное пространство хранения; сегмент данных каждой разделяемой библиотеки хранит глобальные переменные, необходимые для выполнения программы. Ядро отображает сегмент данных файла ELF в пространство виртуальной памяти; сегмент кода пользователя хранит исполняемые машинные инструкции в двоичной форме, а ядро ​​отображает сегмент кода файла ELF в пространство виртуальной памяти; сегмент пользовательских данных хранит выполнение программы Требуемые глобальные переменные отображаются в пространство виртуальной памяти ядром из сегмента данных файла ELF; куча существует тогда и только тогда, когда вызывается malloc, а анонимная память отображается ядром в пространство виртуальной памяти, а куча находится в программе Он не существует без вызова malloc; стек как область временных данных процесса отображает анонимную память в пространство виртуальной памяти ядром, а пространство стека увеличивается от высоких адресов к низким адресам.

Приложение pthread занимает две строчки на картах, его содержимое выглядит следующим образом:

Разрешение первой строки доступно только для чтения и является исполняемым, что указывает на то, что первая строка является сегментом кода приложения, а разрешение второй строки доступно для чтения и записи, но нет разрешения на выполнение, что указывает на то, что этот сегмент является сегментом данных pthread .

Выше приведена запись общей библиотеки libc-2.5 в файле карт.Каждая общая библиотека соответствует трем строкам в файле карт, которые представляют собой сегмент данных и сегмент кода.

Раздел кучи [heap].

Эта запись не появляется в некоторых файлах карт. В основном это связано с тем, используется ли malloc в программе. Если основной поток использует malloc, эта запись будет, в противном случае такой записи нет. Вызов malloc в дочернем потоке сгенерирует другое отображение кучи, но не пометит [кучу]. Например, если 1 МБ пространства памяти динамически выделяется в дочернем потоке, результат выполнения приложения pthread2 будет следующим:

Соответствующий файл карт:

Строка, отмеченная красным цветом в файле карт, с точки зрения содержимого, размер памяти этого сегмента составляет 1 МБ, разрешения являются частными для чтения и записи, смещение - это начальный адрес этого сегмента памяти, номер устройства и узел индекса файла равны 0. Видно, что этот раздел памяти представляет собой раздел пространства, отображаемый процессом через mmap, который является анонимным отображением. В программе pthread2 с помощью malloc выделяется 1 МБ памяти, которая может соответствовать этой памяти. В то же время адрес 0x7754e008, выделенный malloc, попадает в этот интервал и смещен в сторону нижней адресной части интервала, указывая на то, что этот интервал является адресным пространством кучи. Это показывает, что эта 1M памяти действительно выделяется процессом, вызывающим malloc, а malloc вызывает систему mmap для вызова анонимного сопоставления.

Секция стека [стек], следующие примеры иллюстрируют секцию стека.

Для однопоточных приложений существует только один раздел [stack], который соответствует многопоточным приложениям. Раздел [stack] - это пространство стека основного потока, а пространство стека дочерних потоков автоматически выделяется библиотекой pthread.

Пример 1: Распечатайте адрес локальной переменной однопоточного приложения, и результат выполнения будет следующим:

Соответствующий файл карт:

Адрес локальной переменной 0xbfc73600 находится в интервале [stack].

Пример 2: Распечатайте локальную переменную приложения, которая имеет дочерний поток, и результат выполнения будет следующим:

Соответствующий файл карт выглядит следующим образом:

Результат выполнения показывает, что адрес локальной переменной 0xbfd64740 в основном потоке попадает в интервал [stack], а адрес локальной переменной подпотока 0xb7fc93c4 попадает в интервал b75ca000-b7fcc000 rw-p b75ca00, а адреса локальных переменных выделяются из верхнего адреса. Это показывает, что VMA - это адресное пространство стека дочернего потока. Кроме того, для пространства стека pthread по умолчанию устанавливает страницу защиты стека размером 4 КБ, а соответствующий интервал составляет: b75c9000-b75ca000 --- p b75c9000. Этот интервал не доступен для чтения, записи или выполнения. Может выполнять роль защиты от переполнения стека.

Пример 3: На основе примера 2 создать еще один поток, результат выполнения программы pthread2 будет следующим:

Соответствующий файл карт:

Из записей файла карт добавление подпотока добавит две записи в файл карт, а именно пространство стека подпотока и запись страницы защиты стека. По умолчанию пространство стека, зарезервированное pthread для дочерних потоков, составляет 1 МБ, а страница защиты стека - 4 КБ (в основном это связано с размером страницы).

Короче говоря, файл proc maps может просматривать карту памяти процесса, атрибут разрешения каждой памяти и другую информацию.

Читайте также: