it-swarm-ko.tech

Linux에서 / proc / $ pid / mem을 어떻게 읽습니까?

Linux proc(5) man page/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)? 그리고 셸의 메모리를 인쇄하려고 할 때이 이상한 "No such process"오류는 무엇입니까 (/proc/$$/mem, 분명히 프로세스가 존재합니다)? 그렇다면 /proc/$pid/mem에서 어떻게 읽을 수 있습니까?

/proc/$pid/maps

/proc/$pid/mem는 프로세스에서와 같은 방식으로 매핑 된 $ pid의 메모리 내용, 즉 의사의 오프셋 x 바이트를 보여줍니다. -file은 프로세스의 주소 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 (해당 프로세스 없음) 오류가 발생합니다.

/proc/$pid/mem (r--------)에 대한 권한은 상황보다 더 자유 롭습니다. 예를 들어 setuid 프로세스의 메모리를 읽을 수 없어야합니다. 또한 프로세스가 수정되는 동안 프로세스의 메모리를 읽으려고 시도하면 독자에게 메모리에 대한 일관되지 않은 관점을 제공 할 수 있으며 더 나쁜 것은 이전 버전의 Linux 커널을 추적 할 수있는 경쟁 조건이 있다는 것입니다 ( 이 lkml에 따라) thread , 나는 세부 사항을 모른다). 따라서 추가 점검이 필요합니다.

  • /proc/$pid/mem에서 읽으려는 프로세스는 PTRACE_ATTACH 플래그와 함께 ptrace 를 사용하여 프로세스에 연결해야합니다. 디버거가 프로세스 디버깅을 시작할 때 수행하는 작업입니다. 또한 프로세스의 시스템 호출에 strace 하는 기능입니다. 독자가 /proc/$pid/mem에서 읽기를 마치면 PTRACE_DETACH 플래그로 ptrace를 호출하여 분리해야합니다.
  • 관찰 된 프로세스가 실행되고 있지 않아야합니다. 일반적으로 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);

[다른 스레드에서 /proc/$pid/mem를 덤프하기위한 개념 증명 스크립트를 이미 게시했습니다 .

이 명령 (gdb에서)은 메모리를 안정적으로 덤프합니다.

gcore pid

덤프는 클 수 있습니다. -o outfile 현재 디렉토리에 충분한 공간이없는 경우.

28
Tobu

cat /proc/$$/mem를 실행할 때 변수 $$는 자체 pid를 삽입하는 bash에 의해 평가됩니다. 그런 다음 다른 pid를 가진 cat를 실행합니다. 부모 프로세스 인 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 쉘의 메모리 공간을 읽을 수 없습니다.

12
bahamat

다음은 내가 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);
    }
}
8
Tal Aloni