Linux

如何在 Linux 下從 /proc/$pid/mem 讀取數據?

  • July 7, 2021

Linuxproc(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)?當我嘗試列印 shell 的記憶體(/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]

前兩個數字是區域的邊界(第一個字節和最後一個字節的地址,以十六進製表示)。下一列包含權限,如果這是文件映射,則有一些關於文件的資訊(偏移量、設備、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", 'rb', 0)
output_file = open("self.dump", 'wb')
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
       output_file.write(chunk)  # dump contents to standard output
maps_file.close()
mem_file.close()
output_file.close()

/proc/$pid/mem

$$ The following is for historical interest. It does not apply to current kernels. $$

核心 3.3 版本開始/proc/$pid/mem,只要您僅在映射的偏移量處訪問它並且您有權跟踪它(與ptrace只讀訪問相同的權限),您就可以正常訪問。但在較舊的核心中,還有一些額外的複雜性。

如果您嘗試從mem另一個程序的偽文件中讀取,它不起作用:您會收到ESRCH(No such process) 錯誤。

/proc/$pid/mem( )上的權限r--------比實際情況更寬鬆。例如,您不應該能夠讀取 setuid 程序的記憶體。此外,在程序修改程序時嘗試讀取程序的記憶體可能會給讀者帶來不一致的記憶體視圖,更糟糕的是,存在可以跟踪舊版本 Linux 核心的競爭條件(根據這個 lkml 執行緒,儘管我不知道詳情)。所以需要額外的檢查:

  • 想要讀取/proc/$pid/mem的程序必須附加到使用ptracePTRACE_ATTACH標誌的程序。這就是調試器在開始調試程序時所做的;這也是strace對程序的系統呼叫的影響。一旦讀者完成閱讀/proc/$pid/mem,它應該通過呼叫標誌ptrace來分離。PTRACE_DETACH
  • 觀察到的程序不得正在執行。通常呼叫ptrace(PTRACE_ATTACH, …)將停止目標程序(它發送一個STOP信號),但存在競爭條件(信號傳遞是非同步的),因此跟踪器應該呼叫wait(如 中所述ptrace(2))。

以 root 身份執行的程序可以讀取任何程序的記憶體,無需呼叫ptrace,但必須停止觀察到的程序,否則讀取仍將返回ESRCH

在 Linux 核心原始碼中,提供每個程序條目的程式碼/proc是 in fs/proc/base.c,要讀取的函式/proc/$pid/memmem_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在另一個執行緒上轉儲。

引用自:https://unix.stackexchange.com/questions/6301