You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用sudo仍无法读写其他进程内存的解决方法咨询

解决Ubuntu下读取进程内存时部分区域无权限的问题

首先得明确一点:哪怕是以root身份运行,也不是所有/proc/<pid>/maps里列出的内存区域都能通过/proc/<pid>/mem读取。这不是你的脚本问题,而是Linux内核的安全机制和内存管理特性导致的,主要原因有这几个:

  • 内核空间区域:/proc/maps会列出进程的整个虚拟地址空间,其中高地址部分(通常是0x8000000000000000及以上)属于内核虚拟地址空间,用户态进程(哪怕root)根本无法访问这些区域,内核会直接拒绝读取请求。
  • 已失效的映射:有些区域可能已经被进程释放,但/proc/maps的信息存在延迟,或者是临时映射的内存块,此时读取会返回空或者抛出错误。
  • 安全保护机制:现代Linux内核的SMAP(Supervisor Mode Access Prevention)等安全特性,会严格阻止用户态进程访问内核内存,哪怕是root也不行。
  • 特殊映射区域:比如vDSO这类内核提供的虚拟共享对象,虽然权限显示为r-xp,但部分内容无法直接通过/proc/mem读取。

针对你的脚本的修复建议

你的代码里有几个小问题,先修正这些,再处理权限问题:

  1. 正则表达式匹配错误:你只匹配了权限的前两位,但/proc/maps的权限字段是三位(比如rw-、r-x)加上最后一位的p(私有)或s(共享),原正则会漏掉很多合法区域。
  2. 缺少异常处理:即使权限符合,读取/proc/mem时也可能抛出OSError(比如权限不足、地址无效),需要捕获这些异常。
  3. 字节序和长度计算错误:search16bits里的长度计算逻辑有问题,16位数据是2字节,应该按字节数整除2来循环。

修改后的完整代码:

import re
import sys

def search(number, chunk):
    for bit in chunk:
        if bit == number:
            print(f"Hit!: {number}")

def search16bits(number, chunk):
    # 按2字节为单位遍历,适配小端字节序(x86架构默认)
    for i in range(len(chunk) // 2):
        byte_low = chunk[i * 2]
        byte_high = chunk[i * 2 + 1]
        result = byte_low + (byte_high << 8)
        if number == result:
            print(f"Hit!: {number} at offset {i * 2}")
            exit()

def print_memory_of_pid(pid, only_writable=True):
    """ Run as root, take an integer PID and return the contents of memory to STDOUT """
    # 调整权限匹配规则
    permission_filter = r'rw' if only_writable else r'r.'

    sys.stderr.write(f"PID = {pid}\n")
    with open(f"/proc/{pid}/maps", 'r') as maps_file:
        with open(f"/proc/{pid}/mem", 'rb') as mem_file:
            for line in maps_file.readlines():
                # 匹配完整的地址范围和权限字段
                match = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([rwx-]{3})[ps]', line)
                if not match:
                    continue

                addr_start_str, addr_end_str, perm = match.groups()
                # 过滤不符合权限要求的区域
                if not re.match(permission_filter, perm):
                    sys.stderr.write(f"Skipping region with permission {perm}: {line.strip()}\n")
                    continue

                start = int(addr_start_str, 16)
                end = int(addr_end_str, 16)
                # 跳过64位内核的高地址内核空间
                if start >= 0x8000000000000000:
                    sys.stderr.write(f"Skipping kernel space region: {line.strip()}\n")
                    continue

                sys.stderr.write(f"Reading region {addr_start_str}-{addr_end_str} (perm: {perm})\n")
                try:
                    mem_file.seek(start)
                    chunk = mem_file.read(end - start)
                    if not chunk:
                        sys.stderr.write(f"Empty chunk for region {addr_start_str}-{addr_end_str}\n")
                        continue

                    print("###########################Next Chunk:######################################")
                    # 启用下面的行来搜索目标值
                    # search16bits(221, chunk)
                    # print(len(chunk))
                    # print(chunk)
                except OSError as e:
                    sys.stderr.write(f"Failed to read region {addr_start_str}-{addr_end_str}: {str(e)}\n")
                    continue

if __name__ == '__main__':
    try:
        assert len(sys.argv) == 2, "Provide exactly 1 PID (process ID)"
        pid = int(sys.argv[1])
        print_memory_of_pid(pid)
    except (AssertionError, ValueError) as e:
        print("Please provide 1 PID as a commandline argument.")
        print(f"You entered: {' '.join(sys.argv)}")
        raise e

更可靠的内存读取方式:使用ptrace

如果需要读取那些/proc/mem无法访问的用户态内存区域,可以使用ptrace系统调用——这是Linux调试器(比如gdb)使用的接口,能绕过一些/proc/mem的限制。

你可以用Python的python-ptrace库来实现:

  1. 先安装库:pip install python-ptrace
  2. 示例代码片段:
from ptrace.debugger import PtraceDebugger

def read_process_memory(pid, addr, size):
    debugger = PtraceDebugger()
    try:
        # 附加到目标进程(会暂时暂停进程)
        process = debugger.addProcess(pid, False)
        # 读取指定地址的内存
        return process.readBytes(addr, size)
    finally:
        # 退出调试器,恢复进程运行
        debugger.quit()

# 使用示例:读取PID为1234的进程中地址0x12345678处的16字节
# data = read_process_memory(1234, 0x12345678, 16)

注意:使用ptrace时,目标进程不能被其他调试器附加,而且附加过程会暂停进程(调试器退出后会恢复)。

不推荐的极端方案:禁用内核安全特性

如果是SMAP等内核安全特性阻止了你读取某些区域,可以临时在系统启动参数中添加nosmap来禁用,但这会显著降低系统安全性,只建议在测试环境中使用。

内容的提问来源于stack exchange,提问作者Greg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 20:07:57