使用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读取。
针对你的脚本的修复建议
你的代码里有几个小问题,先修正这些,再处理权限问题:
- 正则表达式匹配错误:你只匹配了权限的前两位,但
/proc/maps的权限字段是三位(比如rw-、r-x)加上最后一位的p(私有)或s(共享),原正则会漏掉很多合法区域。 - 缺少异常处理:即使权限符合,读取
/proc/mem时也可能抛出OSError(比如权限不足、地址无效),需要捕获这些异常。 - 字节序和长度计算错误:
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库来实现:
- 先安装库:
pip install python-ptrace - 示例代码片段:
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
相关产品推荐
相关产品推荐

