मैं लिनक्स के तहत / proc / $ pid / mem से कैसे पढ़ूं?


142

लिनक्स 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तब से कैसे पढ़ सकता हूं ?


1
वहाँ कई अन्य तरीकों पता चलता है कि कैसे इस क्यू एंड ए शीर्षक में एस एफ पर यह करने के लिए कर रहे हैं: एक लिनक्स प्रक्रिया के दाखिल करने के लिए स्मृति डंप
SLM

जवाबों:


140

/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]

पहले दो नंबर क्षेत्र की सीमाएं हैं (पहले बाइट के पते और आखिरी के बाद बाइट, हेक्सा में)। अगले कॉलम में अनुमतियाँ हैं, फिर फ़ाइल (ऑफ़सेट, डिवाइस, इनोड और नाम) के बारे में कुछ जानकारी है यदि यह फ़ाइल मैपिंग है। देखें proc(5)आदमी पेज या समझौता लिनक्स / proc / आईडी / नक्शे और जानकारी के लिए।

यहां एक प्रूफ-ऑफ-कॉन्सेप्ट स्क्रिप्ट है जो अपनी मेमोरी की सामग्री को डंप करता है।

#! /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--------) मामले की तुलना में अधिक उदार हैं। उदाहरण के लिए, आपको एक सेट्युइड प्रक्रिया की मेमोरी पढ़ने में सक्षम नहीं होना चाहिए। इसके अलावा, एक प्रक्रिया की मेमोरी को पढ़ने की कोशिश करते हुए, प्रक्रिया को संशोधित करते हुए यह पाठक को स्मृति का एक असंगत दृश्य दे सकता है, और इससे भी बदतर, ऐसी दौड़ स्थितियां थीं जो लिनक्स कर्नेल के पुराने संस्करणों का पता लगा सकती थीं ( इस lkml थ्रेड के अनुसार , हालांकि मैं विवरण नहीं जानते)। तो अतिरिक्त जांच की जरूरत है:

  • जिस प्रक्रिया से पढ़ना चाहता है , उसे ध्वज के साथ /proc/$pid/memप्रक्रिया का उपयोग करना चाहिए । यह तब होता है जब डिबगर किसी प्रक्रिया को डीबग करना शुरू करते हैं; यह एक प्रक्रिया के सिस्टम कॉल के लिए क्या करता है। एक बार पाठक से पढ़ना समाप्त कर लेने के बाद , उसे ध्वज के साथ बुलाकर अलग करना चाहिए ।ptracePTRACE_ATTACHstrace/proc/$pid/memptracePTRACE_DETACH
  • देखी गई प्रक्रिया चल नहीं रही होगी। आम तौर पर कॉल ptrace(PTRACE_ATTACH, …)करने से लक्ष्य प्रक्रिया बंद हो जाएगी (यह एक STOPसंकेत भेजता है ), लेकिन एक दौड़ की स्थिति है (संकेत वितरण अतुल्यकालिक है), इसलिए ट्रेसर को कॉल करना चाहिए wait(जैसा कि दस्तावेज में है ptrace(2))।

रूट के रूप में चल रही एक प्रक्रिया किसी भी प्रक्रिया की मेमोरी को पढ़ सकती है, बिना कॉल किए ptrace, लेकिन देखी गई प्रक्रिया को रोकना होगा, या रीड अभी भी वापस आ जाएगा ESRCH

लिनक्स कर्नेल स्रोत में, प्रति-प्रक्रिया प्रविष्टियाँ प्रदान करने वाला कोड /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और थ्रेड पर डंपिंग के लिए एक प्रूफ-ऑफ-कॉन्सेप्ट स्क्रिप्ट पोस्ट की है


2
@abc नहीं है, से पढ़ /proc/$pid/memसीधे (साथ चाहे catया ddकाम नहीं करता है या कुछ और)। मेरा जवाब पढ़ें।
गाइल्स

4
@abc वह से पढ़ रहा है /proc/self/mem। एक प्रक्रिया अपने स्वयं के मेमोरी स्पेस को ठीक-ठीक पढ़ सकती है, यह एक अन्य प्रक्रिया की मेमोरी स्पेस को पढ़ रही है जिसकी आवश्यकता है PTRACE_ATTACH
गाइल्स

2
ध्यान दें कि हाल ही में लिनक्स कर्नेल के साथ, आपको PTRACE_ATTACH की आवश्यकता नहीं है। यह परिवर्तन process_vm_readv()सिस्टम कॉल (लिनक्स 3.2) के साथ आता है ।
ysdx

2
Hm, Linux 4.14.8 के साथ यह मेरे लिए काम करता है: एक लंबी चलने वाली प्रक्रिया शुरू करें जो कि / dev / null में आउटपुट लिखने में व्यस्त है। फिर एक और प्रक्रिया को खोलने, तलाशने और पढ़ने के लिए कुछ बाइट्स / proc / $ otherpid / mem (यानी सहायक वेक्टर के माध्यम से संदर्भित कुछ ऑफसेट पर) में सक्षम है - ptrace-संलग्न / अलग करने या रोकने / प्रक्रिया शुरू करने के बिना। काम करता है अगर प्रक्रिया उसी उपयोगकर्ता के तहत और रूट उपयोगकर्ता के लिए चलती है। यानी मैं ESRCHइस परिदृश्य में कोई त्रुटि उत्पन्न नहीं कर सकता ।
15

1
@ maxschlepzig मुझे लगता है कि उपरोक्त टिप्पणी में ysdx द्वारा उल्लिखित परिवर्तन है।
गिल्स

28

यह कमांड (जीडीबी से) मेमोरी को मज़बूती से डंप करती है:

gcore pid

डंप बड़े हो सकते हैं, -o outfileयदि आपकी वर्तमान निर्देशिका में पर्याप्त जगह नहीं है तो उपयोग करें।


12

जब आप cat /proc/$$/memचर $$को निष्पादित करते हैं तो बैश द्वारा मूल्यांकन किया जाता है जो अपना स्वयं का पिड सम्मिलित करता है। यह तब निष्पादित 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मेरे शेल की मेमोरी स्पेस नहीं पढ़ सकते।


आप अंत में जो कुछ भी $pidहै उसकी स्मृति को पढ़ने की कोशिश कर रहे हैं । जैसा कि मैं अपने उत्तर में समझाता हूं, एक अलग प्रक्रिया की स्मृति पढ़ने के लिए आपको इसे ptrace करने की आवश्यकता होती है।
जिल्स

जो बैश होने वाला है। मैं नहीं कह रहा था कि आपका उत्तर गलत था। मैं और अधिक आम आदमी की शर्तों में जवाब दे रहा था "यह काम क्यों नहीं करता है"।
बहमट

@ नौबत: क्या आप $$लिखने (और पढ़ने) के बारे में सोच रहे हैं $pid?
गाइल्स

हां ... वह अंत में जिक्र करते हुए पूछने $$लगा $pid। मैंने इसे साकार किए बिना इसे अपने सिर में बदल लिया। मेरे पूरे उत्तर को संदर्भित करना चाहिए $$, नहीं $pid
बहमट

@ नौबत: क्या अब सवाल साफ है? (BTW मैं आपकी टिप्पणियों को तब तक नहीं देखता जब तक कि आप "@ गिल्स" का उपयोग नहीं करते, मैं सिर्फ आपका संपादन देखने के लिए हुआ था और देखने आया था।)
गाइल्स

7

यहाँ एक छोटा प्रोग्राम है जो मैंने C में लिखा है:

उपयोग:

memdump <pid>
memdump <pid> <ip-address> <port>

प्रोग्राम प्रक्रिया के मैप किए गए मेमोरी क्षेत्रों को खोजने के लिए / खरीद / $ pid / मैप्स का उपयोग करता है, और फिर उन क्षेत्रों को / proc / $ pid / mem, एक समय में एक पृष्ठ से पढ़ता है। उन पृष्ठों को स्टडआउट या आपके द्वारा निर्दिष्ट आईपी पते और टीसीपी पोर्ट के लिए लिखा जाता है।

कोड (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);
    }
}

5
अपने कोड की कुछ व्याख्या जोड़ें। आपकी एकमात्र टिप्पणी थोड़े व्यर्थ है: write to stdoutतुरंत ऊपर fwrite(..., stdout)प्रोग्रामर
।stackexchange.com

आपने कहा कि आपने इसे केवल Android पर परीक्षण किया है, इसलिए मैं अभी पुष्टि करना चाहता था, यह लिनक्स 4.4.0-28 x86_64 पर अच्छा काम करता है, जैसा कि आप अपेक्षा करेंगे
खूबानी लड़का

मुझे स्टडआउट पर / bunch @ 8 l / @ एल जैसे डेटा का गुच्छा मिलता है जो कभी भी अपने विचार को समाप्त नहीं करता है क्यों? लिनक्स पर संकलित 4.9.0-3-amd64 # 1 एसएमपी डेबियन 4.9.25-1 (2017-05-02) x86_64 GNU / लिनक्स थ्रेड मॉडल: पॉज़िक्स जीसीसी संस्करण 6.3.0 20170516 (डेबियन 6.3.0-18)
chh3us

ceph3us, आम उपयोग डेटा को एक फ़ाइल (उदाहरण के लिए <pid>> /sdcard/memdump.bin) पर पाइप करने के लिए है
ताल अलोनी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.