На моей машине, nologin
дисплеи всегда то же сообщение, на английском языке, игнорируя аргументы. /bin/false
отвечает на --version
и --help
на языке, обозначенном $LC_CTYPE
. Кроме этих косметических различий, они имеют тот же эффект.
Мудрый удобством использования, nologin
лучше, если это используется на учетной записи живого человека, который говорит на английском языке. Мудрый безопасностью, нет никакого различия.
/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
на другом потоке.
Когда Вы выполняетесь 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
не может считать пространство памяти моей оболочки.
$pid
. Как я объясняю в своем ответе, читать память другого процесса требует Вас к ptrace он.
– Gilles 'SO- stop being evil'
24.01.2011, 21:35
$$
когда Вы пишете (и читаете), $pid
?
– Gilles 'SO- stop being evil'
01.02.2011, 00:22
$$
и помещенный $pid
в конце. Я транспонировал его в голове, не понимая его. Мой весь ответ должен относиться к $$
, нет $pid
.
– bahamat
01.02.2011, 00:44
Эта команда (от gdb) выводит память надежно:
gcore pid
Дампы могут быть большими, использовать -o outfile
если Ваш текущий каталог не имеет достаточного количества комнаты.
Вот небольшая программа, которую я написал на 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);
}
}
Выполнить чтение с помощью 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
/proc/$pid/mem
непосредственно (ли сcat
илиdd
или что-либо еще), не работает. Прочитайте мой ответ. – Gilles 'SO- stop being evil' 04.03.2012, 19:55/proc/self/mem
. Процесс может считать свое собственное пространство памяти очень хорошо, он читает пространство памяти другого процесса, которое требуетPTRACE_ATTACH
. – Gilles 'SO- stop being evil' 04.03.2012, 22:49process_vm_readv()
системный вызов (Linux 3.2). – ysdx 24.09.2015, 13:11ESRCH
ошибка в этом сценарии. – maxschlepzig 30.12.2017, 17:57