Intereting Posts
Как мне вернуться к предыдущему ядру Ubuntu? (работает Ubuntu 16.04) Файл конфигурации сетевого менеджера по умолчанию? Скопировать массив из нескольких файлов с расширением globbed, в bash 3.2 Что такое команда Linux, в которой перечислены только USB-устройства хранения данных? вырваться из поиска, если сбой -exec Как использовать интегрированную Intel и выделенную графику nVidia для питания разных мониторов? Загрузить все Распаковать все несуществующие или более старые файлы из архива О секторе и блоках и немного путаницы в / proc / diskstats установить opencv-2.4-9 на centos 6.5 с поддержкой cuda 5.5 Как вычислить значения в сценарии оболочки? Не удается найти Krita в меню «Пуск» (Linux Mint) Возможно ли отображать шрифты в openSUSE так же, как и в предыдущей версии? Постоянная связь с китайским ip Как использовать printf с awk для обозначения вывода команд, разделенных столбцами

Как читать из / proc / $ pid / mem в Linux?

На странице man proc(5) мне сообщается, что /proc/$pid/mem "можно использовать для доступа к страницам памяти процесса". Но простая попытка использовать его только дает мне

 $ cat /proc/$$/mem /proc/self/mem cat: /proc/3065/mem: No such process cat: /proc/self/mem: Input/output error 

Почему cat не может печатать собственную память ( /proc/self/mem )? И что это за странная ошибка «нет такого процесса» при попытке распечатать память оболочки ( /proc/$$/mem , очевидно, что процесс существует)? Как я могу читать из /proc/$pid/mem ?

    /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) страницу proc(5) или « Общие сведения о 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 (No such process).

    Разрешения на /proc/$pid/mem ( r-------- ) более либеральны, чем должно быть. Например, вы не сможете прочитать память процесса setuid. Кроме того, пытаясь прочитать память процесса при изменении процесса, это может дать читателю непоследовательное представление о памяти, и, что еще хуже, были условия гонки, которые могли бы отслеживать более старые версии ядра Linux (в соответствии с этим потоком lkml , хотя я не знаю деталей). Поэтому необходимы дополнительные проверки:

    • Процесс, который хочет читать из /proc/$pid/mem должен присоединяться к процессу, используя ptrace с флагом PTRACE_ATTACH . Это то, что делают отладчики, когда они начинают отлаживать процесс; это также то, что strace делает для системных вызовов процесса. Когда читатель закончит чтение из /proc/$pid/mem , он должен отсоединиться, вызвав ptrace с PTRACE_DETACH флага PTRACE_DETACH .
    • Наблюдаемый процесс не должен работать. Обычно вызов ptrace(PTRACE_ATTACH, …) останавливает целевой процесс (он посылает сигнал STOP ), но есть условие гонки (доставка сигнала асинхронна), поэтому трассер должен вызвать wait (как ptrace(2) в ptrace(2) ).

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

    В исходном ядре Linux код, содержащий записи для каждого процесса в /proc находится в fs/proc/base.c , а функция для чтения из /proc/$pid/memmem_read . Дополнительная проверка выполняется check_mem_permission .

    Вот пример кода C для присоединения к процессу и чтения фрагмента его файла 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 в другом потоке .

    Эта команда (из gdb) надежно надевает память:

     gcore pid 

    Дампы могут быть большими, используйте -o outfile если в вашем текущем каталоге недостаточно места.

    Когда вы выполняете cat /proc/$$/mem переменная $$ оценивается bash, которая вставляет свой собственный pid. Затем он запускает cat которой есть другой pid. Вы получаете cat пытающуюся прочитать память bash , ее родительский процесс. Поскольку непривилегированные процессы могут читать только собственное пространство памяти, это становится явным.

    Вот пример:

     $ echo $$ 17823 

    Обратите внимание, что $$ оценивается до 17823. Давайте посмотрим, какой именно процесс.

     $ ps -ef | awk '{if ($2 == "17823") print}' bahamat 17823 17822 0 13:51 pts/0 00:00:00 -bash 

    Это моя текущая оболочка.

     $ cat /proc/$$/mem cat: /proc/17823/mem: No such process 

    Здесь снова $$ оценивается до 17823, что является моей оболочкой. cat не может прочитать память моей памяти.

    Вот небольшая программа, которую я написал в C:

    Применение:

     memdump <pid> memdump <pid> <ip-address> <port> 

    Программа использует / proc / $ pid / maps, чтобы найти все области отображенных областей процесса, а затем прочитать эти регионы из / proc / $ pid / mem, по одной странице за раз. эти страницы записываются в стандартный вывод или IP-адрес и TCP-порт, которые вы указали.

    Код (протестирован на Android, требует прав суперпользователя):

     #include <stdio.h> #include <stdlib.h> #include <limits.h> #include <sys/ptrace.h> #include <sys/socket.h> #include <arpa/inet.h> void dump_memory_region(FILE* pMemFile, unsigned long start_address, long length, int serverSocket) { unsigned long address; int pageLength = 4096; unsigned char page[pageLength]; fseeko(pMemFile, start_address, SEEK_SET); for (address=start_address; address < start_address + length; address += pageLength) { fread(&page, 1, pageLength, pMemFile); if (serverSocket == -1) { // write to stdout fwrite(&page, 1, pageLength, stdout); } else { send(serverSocket, &page, pageLength, 0); } } } int main(int argc, char **argv) { if (argc == 2 || argc == 4) { int pid = atoi(argv[1]); long ptraceResult = ptrace(PTRACE_ATTACH, pid, NULL, NULL); if (ptraceResult < 0) { printf("Unable to attach to the pid specified\n"); return; } wait(NULL); char mapsFilename[1024]; sprintf(mapsFilename, "/proc/%s/maps", argv[1]); FILE* pMapsFile = fopen(mapsFilename, "r"); char memFilename[1024]; sprintf(memFilename, "/proc/%s/mem", argv[1]); FILE* pMemFile = fopen(memFilename, "r"); int serverSocket = -1; if (argc == 4) { unsigned int port; int count = sscanf(argv[3], "%d", &port); if (count == 0) { printf("Invalid port specified\n"); return; } serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (serverSocket == -1) { printf("Could not create socket\n"); return; } struct sockaddr_in serverSocketAddress; serverSocketAddress.sin_addr.s_addr = inet_addr(argv[2]); serverSocketAddress.sin_family = AF_INET; serverSocketAddress.sin_port = htons(port); if (connect(serverSocket, (struct sockaddr *) &serverSocketAddress, sizeof(serverSocketAddress)) < 0) { printf("Could not connect to server\n"); return; } } char line[256]; while (fgets(line, 256, pMapsFile) != NULL) { unsigned long start_address; unsigned long end_address; sscanf(line, "%08lx-%08lx\n", &start_address, &end_address); dump_memory_region(pMemFile, start_address, end_address - start_address, serverSocket); } fclose(pMapsFile); fclose(pMemFile); if (serverSocket != -1) { close(serverSocket); } ptrace(PTRACE_CONT, pid, NULL, NULL); ptrace(PTRACE_DETACH, pid, NULL, NULL); } else { printf("%s <pid>\n", argv[0]); printf("%s <pid> <ip-address> <port>\n", argv[0]); exit(0); } }