/proc/$pid/maps
/proc/$pid/mem
hiển thị nội dung trong bộ nhớ của $ pid được ánh xạ giống như trong quy trình, tức là, byte tại offset x trong tệp giả giống như byte tại địa chỉ x trong quy trình. Nếu một địa chỉ không được ánh xạ trong quá trình, đọc từ phần bù tương ứng trong tệp trả về EIO
(Lỗi đầu vào / đầu ra). Ví dụ, do trang đầu tiên trong một quy trình không bao giờ được ánh xạ (do đó, việc hủy bỏ một NULL
con trỏ không hoàn toàn thay vì truy cập bộ nhớ thực tế), đọc byte đầu tiên /proc/$pid/mem
luôn luôn gây ra lỗi I / O.
Cách để tìm ra phần nào của bộ nhớ quá trình được ánh xạ là đọc /proc/$pid/maps
. Tệp này chứa một dòng trên mỗi vùng được ánh xạ, trông như thế này:
08048000-08054000 r-xp 00000000 08:01 828061 /bin/cat
08c9b000-08cbc000 rw-p 00000000 00:00 0 [heap]
Hai số đầu tiên là ranh giới của vùng (địa chỉ của byte đầu tiên và byte sau cuối, trong hexa). Cột tiếp theo chứa các quyền, sau đó có một số thông tin về tệp (offset, thiết bị, inode và tên) nếu đây là ánh xạ tệp. Xem proc(5)
trang hướng dẫn hoặc Hiểu Linux / Proc / id / maps để biết thêm thông tin.
Đây là một kịch bản bằng chứng khái niệm loại bỏ nội dung của bộ nhớ của chính nó.
#! /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
Nếu bạn cố đọc từ mem
tệp giả của quy trình khác, nó không hoạt động: bạn gặp lỗi ESRCH
(Không có quy trình như vậy).
Các quyền trên /proc/$pid/mem
( r--------
) tự do hơn so với trường hợp nên có. Ví dụ: bạn không thể đọc bộ nhớ của quy trình setuid. Hơn nữa, cố gắng đọc bộ nhớ của một tiến trình trong khi quá trình đang sửa đổi nó có thể cung cấp cho người đọc một cái nhìn không nhất quán về bộ nhớ, và tệ hơn nữa, có những điều kiện chủng tộc có thể theo dõi các phiên bản cũ hơn của nhân Linux (theo chủ đề lkml này , mặc dù tôi không biết chi tiết). Vì vậy, kiểm tra bổ sung là cần thiết:
- Quá trình muốn đọc từ
/proc/$pid/mem
phải đính kèm với quy trình sử dụng ptrace
với PTRACE_ATTACH
cờ. Đây là những gì trình gỡ lỗi làm khi họ bắt đầu gỡ lỗi một quy trình; đó cũng là những gì mà strace
hệ thống của một quá trình gọi. Khi người đọc đã đọc xong /proc/$pid/mem
, nó sẽ tách ra bằng cách gọi ptrace
bằng PTRACE_DETACH
cờ.
- Quá trình quan sát không được chạy. Thông thường gọi
ptrace(PTRACE_ATTACH, …)
sẽ dừng quá trình đích (nó gửi STOP
tín hiệu), nhưng có một điều kiện cuộc đua (phân phối tín hiệu không đồng bộ), vì vậy người theo dõi nên gọi wait
(như được ghi trong tài liệu ptrace(2)
).
Một tiến trình đang chạy như root có thể đọc bất kỳ bộ nhớ nào của tiến trình, mà không cần gọi ptrace
, nhưng quá trình quan sát phải được dừng lại, hoặc đọc vẫn sẽ quay trở lại ESRCH
.
Trong mã nguồn kernel Linux, mã cung cấp mục mỗi quá trình trong /proc
là trong fs/proc/base.c
, và các chức năng để đọc từ /proc/$pid/mem
là mem_read
. Việc kiểm tra bổ sung được thực hiện bởi check_mem_permission
.
Dưới đây là một số mã C mẫu để đính kèm vào một quy trình và đọc một đoạn của mem
tệp (kiểm tra lỗi bị bỏ qua):
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);
Tôi đã đăng một kịch bản bằng chứng về khái niệm để bán phá giá /proc/$pid/mem
trên một chủ đề khác .