Как я читаю из/proc/$pid/mem в соответствии с Linux?

На моей машине, nologin дисплеи всегда то же сообщение, на английском языке, игнорируя аргументы. /bin/false отвечает на --version и --help на языке, обозначенном $LC_CTYPE. Кроме этих косметических различий, они имеют тот же эффект.

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

143
01.02.2011, 01:22
5 ответов

/proc/$pid/maps

/proc/$pid/mem показывает содержание $pid's, с отображенной памятью тот же путь как в процессе, т.е. байт при смещении x в псевдофайле совпадает с байтом в адресе x в процессе. Если адрес не отображается в процессе, читая из соответствующего смещения в возвратах файла EIO (Ошибка ввода/вывода). Например, так как первая страница в процессе никогда не отображается (так, чтобы разыменование a 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]

Первые два числа являются границами региона (адреса первого байта и байта после в последний раз в hexa). Следующий столбец содержит полномочия, затем существует некоторая информация о файле (смещение, устройство, inode и имя), если это - отображение файла. Посмотрите 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 (Никакой такой процесс) ошибка.

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

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

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

В источнике ядра Linux, код, обеспечивающий записи для каждого процесса в /proc находится в fs/proc/base.c, и функция для чтения из /proc/$pid/mem mem_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 на другом потоке.

144
27.01.2020, 19:28
  • 1
    @abc, нет, читающий из /proc/$pid/mem непосредственно (ли с cat или dd или что-либо еще), не работает. Прочитайте мой ответ. –  Gilles 'SO- stop being evil' 04.03.2012, 19:55
  • 2
    @abc Он читает из /proc/self/mem. Процесс может считать свое собственное пространство памяти очень хорошо, он читает пространство памяти другого процесса, которое требует PTRACE_ATTACH. –  Gilles 'SO- stop being evil' 04.03.2012, 22:49
  • 3
    Обратите внимание, что с недавними ядрами Linux, Вам не нужно к PTRACE_ATTACH. Это изменение идет process_vm_readv() системный вызов (Linux 3.2). –  ysdx 24.09.2015, 13:11
  • 4
    Гм, с Linux 4.14.8 это действительно работает на меня: запустите длительный процесс, который занят, пишущий вывод в/dev/null. Затем другой процесс может открыть, искать и считать некоторые байты из/proc/$otherpid/mem (т.е. при некоторых смещениях, на которые ссылаются через вспомогательный вектор) - не имея необходимость к ptrace-attach/detach или останавливаясь/начиная процесс. Работы, если процесс работает при том же пользователе и для пользователя root. Т.е. Я не могу привести к a ESRCH ошибка в этом сценарии. –  maxschlepzig 30.12.2017, 17:57
  • 5
    @maxschlepzig я предполагаю, что это - изменение, упомянутое ysdx в комментарии выше. –  Gilles 'SO- stop being evil' 31.12.2017, 14:01

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

Вот пример:

$ echo $$
17823

Отметьте это $$ оценивает к 17 823. Давайте посмотрим, какой процесс, который является.

$ 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

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

12
27.01.2020, 19:28
  • 1
    Вы заканчиваете тем, что пытались считать память что $pid . Как я объясняю в своем ответе, читать память другого процесса требует Вас к ptrace он. –  Gilles 'SO- stop being evil' 24.01.2011, 21:35
  • 2
    Который будет ударом. Я не говорил, что Ваш ответ был неправильным. Я просто отвечал в терминах большего количества неспециалиста, "почему это не работает". –  bahamat 24.01.2011, 23:07
  • 3
    @bahamat: Вы думаете $$ когда Вы пишете (и читаете), $pid? –  Gilles 'SO- stop being evil' 01.02.2011, 00:22
  • 4
    Да... он начал спрашивать обращение к $$ и помещенный $pid в конце. Я транспонировал его в голове, не понимая его. Мой весь ответ должен относиться к $$, нет $pid. –  bahamat 01.02.2011, 00:44
  • 5
    @bahamat: действительно ли вопрос более ясен теперь? (BTW, я не вижу Ваши комментарии, если Вы не используете “@Gilles”, я просто, оказалось, видел Ваше редактирование и прибыл для наблюдения.) –  Gilles 'SO- stop being evil' 01.02.2011, 01:23

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

gcore pid

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

28
27.01.2020, 19:28

Вот небольшая программа, которую я написал на 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);
    }
}
7
27.01.2020, 19:28

Выполнить чтение с помощью bash можно также с помощьюdd(1)

Если вы используете ограниченную и базовую систему Unix, в которой нет некоторых из упомянутых выше команд (от pythonдо memdumpи тому подобное)

Вы можете использовать dd(1), который должен быть доступен в самой ограниченной среде Unix.

Пример дампа первых нескольких байтов из процесса:

$ dd if=/proc/1337/mem of=/tmp/dump bs=1 skip=$((0x400000)) count=128

, а затем вы можете прочитать его с помощью

hexdump -Cv./tmp/dump
1
04.11.2020, 09:19

Теги

Похожие вопросы