前面我们介绍过 /proc/<pid>/maps 这个伪文件,/proc/<pid>/mem 和 /proc/<pid>/maps 一样,都是 Linux proc 文件系统中的一个特殊文件,它提供了对某个进程整个虚拟地址空间的字节级访问接口。你可以把它理解为"该进程内存的镜像"以文件的形式暴露出来,允许其他进程(通常是调试器、注入工具或诊断程序)直接读写目标进程的内存。
既然通过 /proc/<pid>/mem 也能访问目标进程的地址空间,我们就来改写一下之前写的通过 process_vm_readv 和 process_vm_writev 访问目标进程地址空间的程序,这次我们使用读写文件的系统调用来读写目标进程的 /proc/<pid>/mem 文件,以达到访问目标进程地址空间的目的。下面是我改写后的代码,我使用了 pread64 和 pwrite64 这两个读写文件的系统调用,这俩系统调用的作用和 read 和 write 类似,只不过它们允许你在指定的偏移量(offset)处对文件进行读写操作,不需要你对文件频繁的进行 seek 操作,也不会修改文件描述符当前的读写位置(即不影响内核维护的文件偏移指针)。使用 pread64 和 pwrite64 来读写/proc/<pid>/mem 文件的不同位置会让代码更加简洁一些。

改完后的程序本质上就是基于 proc 文件系统的两个文件协作来完成跨进程内存访问:
/proc/<pid>/maps —— 充当"地图"(元数据)
解析出每个内存区域的起止地址和权限(r/w/x),告诉你"哪里能读、哪里能写"。没有它,你不知道目标进程的虚拟地址空间里哪些范围是有效的、哪些是可写的。
/proc/<pid>/mem —— 充当"通道"(数据)
以文件偏移量 = 虚拟地址的方式,用 pread64/pwrite64 实际读写对应地址处的字节内容。没有它,你只有布局信息却碰不到真实数据。
下面我们看下这个程序的执行效果,也是可以成功的把 victim 进程中的目标字符串修改掉的。

/proc/<pid>/mem 的设计也是 Linux “一切皆文件”设计思想的另一种体现,Linux 把进程的虚拟地址空间也抽象成了一个文件,用户不需要学习一套全新的专用 API(像 Windows 的 ReadProcessMemory/WriteProcessMemory 那样),而是用最普通的 open/read/write/lseek 就能访问远程进程内存。
文件偏移量(offset)= 虚拟地址
文件内容 = 该地址处的原始字节
这种抽象让"进程内存"和"磁盘上的文件"在接口层面变得完全同构。