লিনাক্সের অধীনে / প্রো / / পিড / মেম থেকে কীভাবে পড়ব?


142

লিনাক্স proc(5)man পৃষ্ঠা আমাকে বলে যে /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
আরও অনেকগুলি পদ্ধতি রয়েছে যা এই প্রশ্নোত্তর শিরোনামে এসএফ-এ কীভাবে করবেন তা দেখায়: ফাইলটিতে একটি লিনাক্স প্রক্রিয়াটির মেমরি ডাম্প করুন
স্ল্যাম

উত্তর:


140

/proc/$pid/maps

/proc/$pid/memid পিডের মেমরির বিষয়বস্তু প্রক্রিয়াটির মতোই ম্যাপযুক্ত দেখায়, অর্থাৎ সিউডো-ফাইলে অফসেট এক্স -এ বাইট প্রসেসের অ্যাড্রেস 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)ম্যান পৃষ্ঠা বা বোধগম্য লিনাক্স / প্রোক / আইডি / মানচিত্রগুলি দেখুন।

এখানে একটি প্রুফ-অফ কনসেপ্ট স্ক্রিপ্ট রয়েছে যা তার নিজস্ব স্মৃতিতে থাকা সামগ্রীকে ফেলে দেয়।

#! /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অবশ্যই পতাকাটির সাহায্যে প্রক্রিয়াটির ptraceসাথে সংযুক্ত করতে হবে PTRACE_ATTACH। ডিবাগাররা যখন কোনও প্রক্রিয়া ডিবাগ করা শুরু করেন এটিই; এটি straceএকটি প্রক্রিয়া সিস্টেম কল করতে কি। পাঠক একবার থেকে পড়া শেষ করার পরে, পতাকাটির সাথে /proc/$pid/memডাক ptraceদিয়ে এটি পৃথক করা উচিত PTRACE_DETACH
  • পর্যবেক্ষণ প্রক্রিয়া চলমান হবে না। সাধারণত কলিং ptrace(PTRACE_ATTACH, …)লক্ষ্য প্রক্রিয়া বন্ধ করে দেয় (এটি একটি STOPসংকেত প্রেরণ করে ), তবে একটি রেসের শর্ত রয়েছে (সিগন্যাল বিতরণটি অ্যাসিনক্রোনাস হয়), সুতরাং ট্রেসারকে কল করা উচিত wait(ডকুমেন্টেড হিসাবে ptrace(2))।

রুট হিসাবে চলমান একটি প্রক্রিয়া কল করার প্রয়োজন ছাড়াই কোনও প্রক্রিয়ার স্মৃতি পড়তে পারে ptrace, তবে পর্যবেক্ষণ প্রক্রিয়াটি অবশ্যই বন্ধ করা উচিত, বা পড়ার পরেও ফিরে আসবে ESRCH

লিনাক্স কার্নেল উত্সে, প্রতি-প্রক্রিয়াতে প্রবেশের কোড সরবরাহ /procকরা রয়েছে fs/proc/base.cএবং সেগুলি পড়তে /proc/$pid/memহবে mem_read। অতিরিক্ত চেক দ্বারা সম্পাদিত হয় check_mem_permission

কোনও প্রক্রিয়াতে সংযুক্ত করার জন্য এবং এর 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
@ এবিসি তিনি পড়ছেন /proc/self/mem। একটি প্রক্রিয়া তার নিজস্ব মেমরি স্পেসটি ঠিক সূক্ষ্মভাবে পড়তে পারে, এটি অন্য প্রক্রিয়ার মেমোরি স্পেসের প্রয়োজন পড়ছে PTRACE_ATTACH
গিলস

2
নোট করুন যে সাম্প্রতিক লিনাক্স কার্নেলগুলির সাথে আপনার PTRACE_ATTACH লাগবে না to এই পরিবর্তনটি process_vm_readv()সিস্টেম কল (লিনাক্স 3.2) এর সাথে আসে ।
ysdx

2
এইচএম, লিনাক্স ৪.১.8.৮ সহ এটি আমার পক্ষে কাজ করে: একটি দীর্ঘ চলমান প্রক্রিয়া শুরু করুন যা / dev / নালকে আউটপুট লিখতে ব্যস্ত। তারপরে অন্য একটি প্রক্রিয়া / প্রো / / $ অন্যান্যপিড / মেম (যেমন সহায়ক ভেক্টরের মাধ্যমে উল্লেখ করা কিছু অফসেটে) থেকে কিছু বাইট খুলতে, সন্ধান করতে এবং পড়তে সক্ষম হয় - প্রসেস-সংযুক্ত / বিচ্ছিন্নকরণ বা প্রক্রিয়াটি বন্ধ / না করে। প্রক্রিয়া যদি একই ব্যবহারকারীর অধীনে এবং রুট ব্যবহারকারীর জন্য চলে তবে কাজ করে। অর্থাৎ আমি এই দৃশ্যে কোনও ESRCHত্রুটি দিতে পারি না ।
maxschlepzig

1
@ ম্যাক্সচলেপজিগ আমার ধারণা, উপরের মন্তব্যে ysdx দ্বারা উল্লিখিত পরিবর্তনটি।
গিলস

28

এই কমান্ডটি (জিডিবি থেকে) মেমরিটিকে নির্ভরযোগ্যভাবে ফেলে দেয়:

gcore pid

ডাম্পগুলি বড় হতে পারে, -o outfileআপনার বর্তমান ডিরেক্টরিটিতে পর্যাপ্ত জায়গা না থাকলে ব্যবহার করুন ।


12

আপনি যখন cat /proc/$$/memভেরিয়েবলটি এক্সিকিউট করেন $$তা বাশ দ্বারা মূল্যায়ন করা হয় যা এর নিজস্ব পিড inোকায়। এটি তখন কার্যকর করে 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তার স্মৃতিটি পড়ার চেষ্টা করে শেষ করেন । আমি আমার উত্তরে যেমন বর্ণনা করেছি, ভিন্ন প্রক্রিয়ার স্মৃতি পড়ার জন্য আপনাকে এটির ছত্রাক ছড়িয়ে দেওয়া দরকার।
গিলস

যা বাশ হতে চলেছে। আমি বলছিলাম না যে আপনার উত্তরটি ভুল ছিল। আমি কেবল আরও সাধারণ ব্যক্তির পদগুলিতে উত্তর দিচ্ছিলাম "কেন এটি কাজ করে না"।
বাহামাত

@ বাহাহাত: আপনি $$কখন লিখবেন (এবং পড়বেন) তা ভেবে দেখছেন $pid?
গিলস

হ্যাঁ ... তিনি উল্লেখ করতে শুরু করেছিলেন এবং শেষে $$রেখেছিলেন $pid। আমি এটি বুঝতে না পেরে এটি আমার মাথায় স্থানান্তরিত করেছি। আমার পুরো উত্তর উল্লেখ করা উচিত $$, না $pid
বাহামাত

@ বাহাহাত: প্রশ্নটি কি এখন পরিষ্কার? (বিটিডব্লিউ আমি আপনার মন্তব্যগুলি দেখতে পাচ্ছি না যতক্ষণ না আপনি "@ গিলস" ব্যবহার করেন, আমি কেবল আপনার সম্পাদনাটি দেখতে পেয়েছি এবং দেখতে এসেছি))
গিলস

7

আমি সিতে একটি ছোট প্রোগ্রাম লিখেছি:

ব্যবহার:

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

প্রক্রিয়াটির সমস্ত ম্যাপযুক্ত মেমরি অঞ্চলগুলি অনুসন্ধান করার জন্য প্রোগ্রামটি / proc / $ pid / মানচিত্রগুলি ব্যবহার করে এবং সেগুলি একবারে / পৃষ্ঠা / proc / id পিড / মেম থেকে পড়বে one এই পৃষ্ঠাগুলি stdout বা IP ঠিকানা এবং টিসিপি পোর্ট আপনি লিখেছেন।

কোড (অ্যান্ড্রয়েডে পরীক্ষিত, সুপারসারের অনুমতি প্রয়োজন):

#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)। দেখুন programmers.stackexchange.com/questions/119600/...
muru

আপনি বলেছিলেন যে আপনি এটি কেবল অ্যান্ড্রয়েডে পরীক্ষা করেছেন, তাই আমি কেবল এটি নিশ্চিত করতে চেয়েছিলাম, এটি লিনাক্স 4.4.0-28 x86_64 তে ভাল কাজ করে, যেমনটি আপনি প্রত্যাশা করেছিলেন
বালক

আমি স্টাডআউটে / @ 8 l / @ likel এর মতো গুচ্ছ ডেটা পেয়েছি যা কখনই কোনও ধারণা কেন শেষ করে না? লিনাক্স ৪.৯.০-৩-এএমডি #৪ # 1 এসএমপি ডেবিয়ান 4.9.25-1 (2017-05-02) x86_64 জিএনইউ / লিনাক্স থ্রেড মডেল: পোস্টিক্স জিসিসি সংস্করণ 6.3.0 20170516 (দেবিয়ান 6.3.0-18)
সিফহিউস

সিফএইচ 3us, সাধারণ ব্যবহার হ'ল কোনও ফাইলের ডেটা পাইপ করা (যেমন মেমডাম্প <পিড>> / এসডিকার্ড / মেমডাম্প.বিন)
তাল
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.