/proc/$pid/maps
/proc/$pid/mem
mostra o conteúdo da memória do $ pid mapeado da mesma maneira que no processo, ou seja, o byte no deslocamento x no pseudo-arquivo é o mesmo que o byte no endereço x no processo. Se um endereço não estiver mapeado no processo, a leitura do deslocamento correspondente no arquivo retornará EIO
(erro de entrada / saída). Por exemplo, como a primeira página de um processo nunca é mapeada (para que o cancelamento de referência de um NULL
ponteiro falhe de maneira limpa em vez de acessar a memória real sem intenção), a leitura do primeiro byte /proc/$pid/mem
sempre gera um erro de E / S.
A maneira de descobrir quais partes da memória do processo são mapeadas é ler /proc/$pid/maps
. Este arquivo contém uma linha por região mapeada, assim:
08048000-08054000 r-xp 00000000 08:01 828061 /bin/cat
08c9b000-08cbc000 rw-p 00000000 00:00 0 [heap]
Os dois primeiros números são os limites da região (endereços do primeiro byte e o byte depois do último, em hexa). A próxima coluna contém as permissões, e há algumas informações sobre o arquivo (deslocamento, dispositivo, inode e nome) se este for um mapeamento de arquivo. Consulte a proc(5)
página do manual ou Noções básicas sobre Linux / proc / id / maps para obter mais informações.
Aqui está um script de prova de conceito que despeja o conteúdo de sua própria memória.
#! /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
Se você tentar ler o mem
pseudo-arquivo de outro processo, ele não funcionará: você receberá um ESRCH
erro (sem esse processo).
As permissões em /proc/$pid/mem
( r--------
) são mais liberais do que deveria ser o caso. Por exemplo, você não deve conseguir ler a memória de um processo setuid. Além disso, tentar ler a memória de um processo enquanto o processo está modificando pode dar ao leitor uma visão inconsistente da memória e, pior ainda, havia condições de corrida que poderiam rastrear versões mais antigas do kernel Linux (de acordo com este segmento lkml , embora eu não conheço os detalhes). Portanto, são necessárias verificações adicionais:
- O processo que deseja ler
/proc/$pid/mem
deve ser anexado ao processo usando ptrace
com o PTRACE_ATTACH
sinalizador. É isso que os depuradores fazem quando começam a depurar um processo; é também o strace
que as chamadas de sistema de um processo fazem. Depois que o leitor terminar de ler /proc/$pid/mem
, ele deve se desconectar chamando ptrace
com o PTRACE_DETACH
sinalizador.
- O processo observado não deve estar em execução. Normalmente, a chamada
ptrace(PTRACE_ATTACH, …)
interrompe o processo de destino (envia um STOP
sinal), mas há uma condição de corrida (a entrega do sinal é assíncrona), portanto o rastreador deve chamar wait
(conforme documentado em ptrace(2)
).
Um processo em execução como root pode ler a memória de qualquer processo, sem precisar chamar ptrace
, mas o processo observado deve ser parado ou a leitura ainda retornará ESRCH
.
Na fonte do kernel Linux, o código que fornece entradas por processo /proc
está em fs/proc/base.c
, e a função para ler /proc/$pid/mem
é mem_read
. A verificação adicional é realizada por check_mem_permission
.
Aqui está um exemplo de código C para anexar a um processo e ler um pedaço do mem
arquivo (verificação de erro omitida):
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);
Já publiquei um script de prova de conceito para despejo /proc/$pid/mem
em outro segmento .