리눅스 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
항상 I / O 오류가 발생합니다.
프로세스 메모리에서 매핑되는 부분을 찾는 방법은 읽는 것 /proc/$pid/maps
입니다. 이 파일에는 다음과 같이 매핑 된 영역 당 한 줄이 포함됩니다.
08048000-08054000 r-xp 00000000 08:01 828061 /bin/cat
08c9b000-08cbc000 rw-p 00000000 00:00 0 [heap]
처음 두 숫자는 영역의 경계입니다 (첫 번째 바이트와 마지막 바이트 이후의 16 진수). 다음 열에는 권한이 포함되어 있으며 파일 매핑 인 경우 파일에 대한 정보 (오프셋, 장치, 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
(No such process) 오류가 발생합니다.
/proc/$pid/mem
( r--------
) 에 대한 권한 은 상황보다 더 자유 롭습니다. 예를 들어 setuid 프로세스의 메모리를 읽을 수 없어야합니다. 또한, 공정 악화가 독자에게 메모리의 일관성보기를 줄 수있는 수정, 동안 프로세스의 메모리를 읽으려고, (에 따라 리눅스 커널의 이전 버전을 추적 할 수있는 경쟁 조건이 있었다 이 LKML 스레드 I 생각은, 세부 사항을 모른다). 따라서 추가 점검이 필요합니다.
- 읽을
/proc/$pid/mem
프로세스ptrace
는PTRACE_ATTACH
플래그를 사용하여 프로세스에 첨부해야합니다 . 디버거가 프로세스 디버깅을 시작할 때 수행하는 작업입니다. 또한strace
프로세스의 시스템 호출에도 영향을줍니다. 리더가에서 읽기를 마치면 플래그/proc/$pid/mem
로 호출ptrace
하여 분리해야합니다PTRACE_DETACH
. - 관찰 된 프로세스가 실행되고 있지 않아야합니다. 일반적으로 호출
ptrace(PTRACE_ATTACH, …)
하면 대상 프로세스가 중지STOP
되지만 (신호를 전송 함) 경쟁 조건 (신호 전달이 비동기 임)이 있으므로 추적 프로그램이 호출해야합니다wait
(에 설명 된대로ptrace(2)
).
루트로 실행중인 프로세스는을 호출 할 필요없이 프로세스의 메모리를 읽을 수 ptrace
있지만 관찰 된 프로세스를 중지해야합니다. 그렇지 않으면 읽기는 여전히을 반환 ESRCH
합니다.
Linux 커널 소스에서 프로세스 별 항목을 제공하는 코드 /proc
는에 fs/proc/base.c
있으며 읽을 함수 /proc/$pid/mem
는 mem_read
입니다. 추가 확인은에 의해 수행됩니다 check_mem_permission
.
다음은 프로세스에 첨부하고 mem
파일 청크를 읽는 샘플 C 코드입니다 (오류 검사 생략).
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);
답변
이 명령 (gdb에서)은 메모리를 안정적으로 덤프합니다.
gcore pid
덤프가 클 수 있습니다. -o outfile
현재 디렉토리에 충분한 공간이없는 경우 사용 하십시오.
답변
실행할 cat /proc/$$/mem
때 변수 $$
는 자체 pid를 삽입하는 bash에 의해 평가됩니다. 그런 다음 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에서 해당 영역을 읽습니다. 해당 페이지는 stdout 또는 지정한 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);
}
}