如何在 Linux 下從 /proc/$pid/mem 讀取數據?
Linux
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
)?當我嘗試列印 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
的程序必須附加到使用ptrace
該PTRACE_ATTACH
標誌的程序。這就是調試器在開始調試程序時所做的;這也是strace
對程序的系統呼叫的影響。一旦讀者完成閱讀/proc/$pid/mem
,它應該通過呼叫標誌ptrace
來分離。PTRACE_DETACH
- 觀察到的程序不得正在執行。通常呼叫
ptrace(PTRACE_ATTACH, …)
將停止目標程序(它發送一個STOP
信號),但存在競爭條件(信號傳遞是非同步的),因此跟踪器應該呼叫wait
(如 中所述ptrace(2)
)。以 root 身份執行的程序可以讀取任何程序的記憶體,無需呼叫
ptrace
,但必須停止觀察到的程序,否則讀取仍將返回ESRCH
。在 Linux 核心原始碼中,提供每個程序條目的程式碼
/proc
是 infs/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);