ලිනක්ස් යටතේ / proc / $ pid / mem වෙතින් කියවන්නේ කෙසේද?


145

මෙම ලිනක්ස් 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
මෙම ප්‍රශ්නෝත්තරයේදී SF හි මෙය කරන්නේ කෙසේදැයි පෙන්වන තවත් ක්‍රම කිහිපයක් තිබේ: ලිනක්ස් ක්‍රියාවලියේ මතකය ගොනුවට
දමන්න

යාවත්කාලීන පිළිතුර
pizdelect

Answers:


146

/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)man පිටුව හෝ අවබෝධය ලිනක්ස් / proc / id / සිතියම් බලන්න.

මෙන්න එහි මතකයේ අන්තර්ගතය ඉවත ලන සංකල්ප-සනාථ කිරීමේ පිටපතක්.

#! /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--------) මත ඇති අවසරය කුමක් විය යුතුද යන්නට වඩා ලිබරල් ය. නිදසුනක් ලෙස, ඔබට සැකසුම් ක්‍රියාවලියක මතකය කියවීමට නොහැකි විය යුතුය. තවද, ක්‍රියාවලිය වෙනස් කරන අතරතුර ක්‍රියාවලියක මතකය කියවීමට උත්සාහ කිරීම පා the කයාට මතකය පිළිබඳ නොගැලපෙන දෘෂ්ටියක් ලබා දිය හැකි අතර වඩාත් නරක අතට, ලිනක්ස් කර්නලයේ පැරණි අනුවාදයන් සොයාගත හැකි ධාවන තත්වයන් තිබුණි ( මෙම lkml නූලට අනුව , මම වුවද විස්තර දන්නේ නැහැ). එබැවින් අමතර චෙක්පත් අවශ්‍ය වේ:

  • කියවීමට අවශ්‍ය /proc/$pid/memක්‍රියාවලිය ධජය ptraceසමඟ භාවිතා කරන ක්‍රියාවලියට අනුයුක්ත කළ යුතුය PTRACE_ATTACH. ක්‍රියාවලියක් නිදොස්කරණය කිරීම ආරම්භ කරන විට නිදොස් කරන්නන් කරන්නේ මෙයයි; එය straceක්‍රියාවලියක පද්ධති ඇමතුම් සඳහා කරන දේ ද වේ. පා /proc/$pid/memread කයා කියවීම අවසන් කළ පසු , එය ධජය ptraceසමඟ ඇමතීමෙන් වෙන් විය යුතුය PTRACE_DETACH.
  • නිරීක්ෂණය කළ ක්‍රියාවලිය ක්‍රියාත්මක නොවිය යුතුය. සාමාන්‍යයෙන් ඇමතීමෙන් ptrace(PTRACE_ATTACH, …)ඉලක්ක ක්‍රියාවලිය නවත්වනු ඇත (එය සං STOPsignal ාවක් යවයි ), නමුත් ධාවන තත්වයක් ඇත (සං signal ා භාරදීම අසමමුහුර්ත වේ), එබැවින් ට්‍රේසරය ඇමතිය යුතුය wait(ලේඛනගත කර ඇති පරිදි ptrace(2)).

මූලයක් ලෙස ක්‍රියාත්මක වන ක්‍රියාවලියකට ඕනෑම ක්‍රියාවලියක මතකය ඇමතීමට අවශ්‍ය නොවී කියවිය හැකිය ptrace, නමුත් නිරීක්ෂණය කළ ක්‍රියාවලිය නැවැත්විය යුතුය, නැතහොත් කියවීම තවමත් නැවත පැමිණේ ESRCH.

Linux කර්නලය ප්රභවය දී, දී එක්-ක්රියාවලිය ඇතුළත් කිරීම් ලබා කේතය /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වෙනත් දෙයක් වේවා) ක්‍රියා නොකරයි. මගේ පිළිතුර කියවන්න.
ගිලෙස්ගේ SO- නපුරු වීම නවත්වන්න '

4
ඔහු කියවන්නේ /proc/self/mem. ක්‍රියාවලියකට තමන්ගේම මතක අවකාශය හොඳින් කියවිය හැකිය, එය වෙනත් ක්‍රියාවලියක මතක අවකාශය කියවිය යුතුය PTRACE_ATTACH.
ගිලෙස්ගේ SO- නපුරු වීම නවත්වන්න '

2
මෑත කාලීන ලිනක්ස් කර්නල් සමඟ, ඔබට PTRACE_ATTACH අවශ්‍ය නොවන බව සලකන්න. මෙම වෙනස process_vm_readv()පද්ධති ඇමතුම සමඟ පැමිණේ (ලිනක්ස් 3.2).
ysdx

2
Hm, ලිනක්ස් 4.14.8 සමඟ මෙය මට ප්‍රයෝජනවත් වේ: / dev / null වෙත ප්‍රතිදානය ලිවීමේ කාර්යබහුල දිගුකාලීන ක්‍රියාවලියක් ආරම්භ කරන්න. එවිට තවත් ක්‍රියාවලියකට / proc / $ otherpid / mem වෙතින් බයිට් කිහිපයක් විවෘත කිරීමට, කියවීමට සහ කියවීමට හැකිය (එනම් සහායක දෛශිකය හරහා යොමු කර ඇති සමහර ඕෆ්සෙට් වලදී) - ක්‍රියාවලිය ptrace-attach / detach හෝ නැවැත්වීම / ආරම්භ කිරීමකින් තොරව. ක්‍රියාවලිය එකම පරිශීලකයා යටතේ සහ මූල පරිශීලකයා යටතේ ක්‍රියාත්මක වන්නේ නම් ක්‍රියා කරයි. ESRCHමෙම තත්වය තුළ මට දෝෂයක් ඉදිරිපත් කළ නොහැක .
maxschlepzig

1
@maxschlepzig ඉහත අදහස් දැක්වීමේදී ysdx විසින් සඳහන් කළ වෙනස එයයි.
ගිලෙස්ගේ SO- නපුරු වීම නවත්වන්න '

28

මෙම විධානය (gdb වෙතින්) මතකය විශ්වාසදායක ලෙස පහත හෙළයි:

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. මගේ පිළිතුරෙන් මා පැහැදිලි කරන පරිදි, වෙනත් ක්‍රියාවලියක මතකය කියවීමෙන් ඔබට එය දැනගත යුතුය.
ගිලෙස්ගේ SO- නපුරු වීම නවත්වන්න '

මොනවා කරන්නද යන්නේ. මම කිව්වේ නෑ ඔයාගේ පිළිතුර වැරදියි කියලා. මම පිළිතුරු දුන්නේ “ඇයි මේ වැඩ කරන්නේ නැත්තේ” යනුවෙනි.
බහමාත්

@ බහමාත්: $$ඔබ ලියන විට (සහ කියවන විට) ඔබ $pidසිතනවාද?
ගිලෙස්ගේ SO- නපුරු වීම නවත්වන්න '

ඔව් ... ඔහු යොමු කිරීම ඉල්ලා සිටිමින් අවසානයේ $$තැබුවේය $pid. මම එය නොදැන මගේ හිස තුළට මාරු කළෙමි. මගේ සම්පූර්ණ පිළිතුරම යොමු විය යුත්තේ $$, නොව $pid.
බහමාත්

@ බහමාත්: ප්‍රශ්නය දැන් පැහැදිලිද? (ඔබ "@Gilles" භාවිතා නොකරයි නම් මම BTW ඔබගේ අදහස් බලන්න එපා, මම ඔබගේ සංස්කරණය බලන්න සිදු බලන්න ආවා.)
ගිලෙස් 'SO- නැවතුම් වීම නපුර'

8

මෙන්න මම සී හි ලියා ඇති කුඩා වැඩසටහනක්:

භාවිතය:

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

ක්‍රියාවලියේ සියලුම සිතියම්ගත මතක ප්‍රදේශ සොයා ගැනීමට වැඩසටහන / proc / $ pid / සිතියම් භාවිතා කරයි, ඉන්පසු එම ප්‍රදේශ එකවර / පිටුවක / proc / $ pid / mem වෙතින් කියවන්න. එම පිටු stdout හෝ IP ලිපිනය සහ ඔබ සඳහන් කළ TCP port වෙත ලියා ඇත.

කේතය (ඇන්ඩ්‍රොයිඩ් මත පරීක්ෂා කර ඇත, සුපිරි පරිශීලක අවසර අවශ්‍ය වේ):

#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). Programmmers.stackexchange.com/questions/119600/…
muru

ඔබ එය ඇන්ඩ්‍රොයිඩ් වලින් පමණක් පරීක්‍ෂා කළ බව පැවසුවා, එබැවින් මට තහවුරු කිරීමට අවශ්‍ය විය, එය ඔබ බලාපොරොත්තු වන පරිදි ලිනක්ස් 4.4.0-28 x86_64 මත හොඳින් ක්‍රියාත්මක වේ
ඇප්රිකොට් ඇටයේ පිරිමි ළමයා

මට කිසිඳු අදහසක් අවසන් නොවන stdout හි / @ 8 l / වැනි දත්ත පොකුරක් මට ලැබේ. ලිනක්ස් 4.9.0-3-amd64 # 1 එස්එම්පී ඩේබියන් 4.9.25-1 (2017-05-02) x86_64 ග්නූ / ලිනක්ස් නූල් ආකෘතිය: පොසික්ස් ජීසීසී අනුවාදය 6.3.0 20170516 (ඩේබියන් 6.3.0-18)
සීෆස් 3

ceph3us, පොදු භාවිතය වන්නේ දත්ත ගොනුවකට නල කිරීමයි (උදා: memdump <pid>> /sdcard/memdump.bin)
Tal Aloni
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.