Linux跨进程内存操作的3种方法及防护方案

最近有开发者在群里问:怎么实现一个进程去读写另一个进程的内存?这个问题在游戏外挂、调试工具、系统监控等场景都很常见。今天就给大家分享Linux下三种主流的跨进程内存操作方式,以及相应的防护思路。

一、ptrace:老牌的进程追踪神器

说到ptrace,搞过调试的都不陌生。gdb、strace这些工具底层用的就是它。说白了,ptrace就是系统给你开的一扇"后门",让你能控制另一个进程的执行流,顺带读写它的内存。

核心原理

ptrace是个系统调用,长这样:

c 复制代码
long ptrace(enum __ptrace_request op, pid_t pid, void *addr, void *data);

要用它读写内存,主要靠这两个操作:

  • PTRACE_PEEKDATA:一次读一个long大小的数据
  • PTRACE_POKEDATA:一次写一个long大小的数据

代码实战

我封装了个Tracer类,用起来比较直观:

cpp 复制代码
// Tracer.h
class Tracer {
public:
    bool attach(int pid);      // 附加到目标进程
    void detach();             // 脱离
    bool continueRun();        // 让目标继续跑
    void stop();               // 暂停目标
    
    size_t readMemory(uintptr_t address, void* buffer, size_t size);
    size_t writeMemory(uintptr_t address, void* buffer, size_t size);
};

实现的时候有个坑:必须先让目标进程进入STOP状态才能操作。来看核心代码:

cpp 复制代码
size_t Tracer::readMemory(uintptr_t address, void *buffer, size_t size) {
    // 按long大小分块读写,效率很一般
    size_t readsize = 0;
    long tmp;
    
    for(size_t i = 0; i < size / sizeof(long); ++i) {
        errno = 0;
        tmp = ptrace(PTRACE_PEEKDATA, pid_, (void*)(address + readsize), nullptr);
        if(tmp == -1 && errno != 0) return readsize; // 出错了
        
        memcpy((uint8_t*)buffer + i * sizeof(long), &tmp, sizeof(tmp));
        readsize += sizeof(tmp);
    }
    // 处理剩余字节...
}

写内存更麻烦,最后几个字节要"读-改-写"合并,不然会把后面的数据覆盖掉。

优缺点总结

  • ✅ 权限控制严格,需要root或同用户
  • ✅ 功能强大,还能单步调试
  • ❌ 效率低,每次都是单次syscall
  • ❌ 目标进程必须暂停,影响业务

二、/proc/pid/mem:虚拟文件的妙用

这招更直接。Linux的/proc目录下,每个进程都有一个mem文件,映射了它的整个虚拟内存空间。我们用普通的文件API就能操作。

实现要点

关键代码很简单,就是open/read/write:

cpp 复制代码
bool ProcFile::openMemory() {
    char path[64];
    snprintf(path, sizeof(path), "/proc/%d/mem", pid_);
    mem_fd_ = open(path, O_RDWR); // 要读写权限
    return mem_fd_ != -1;
}

size_t ProcFile::readMemory(intptr_t address, void *buffer, size_t size) {
    if (mem_fd_ == -1) return 0;
    
    // 定位到目标地址
    lseek(mem_fd_, static_cast<off_t>(address), SEEK_SET);
    
    // 必须STOP目标进程
    kill(pid_, SIGSTOP);
    size_t read_size = read(mem_fd_, buffer, size);
    kill(pid_, SIGCONT); // 操作完赶紧恢复
    
    return read_size;
}

注意几个细节:

  1. 需要O_RDWR权限,普通用户只能读自己进程的
  2. 操作前后要用SIGSTOP/SIGCONT包裹,不然会报错
  3. 地址必须用lseek定位,不能像普通文件那样顺序读

优缺点总结

  • ✅ 代码简单,符合Unix一切皆文件的理念
  • ✅ 批量读写效率比ptrace高
  • ❌ 还是要暂停进程
  • ❌ 需要处理权限问题,SELinux可能会拦截

三、process_vm_readv/writev:现代API

这是Linux 3.2+引入的"官方"解决方案。专门为跨进程内存操作设计,用起来最优雅。

使用方法

cpp 复制代码
#include <sys/uio.h>

ssize_t process_vm_readv(pid_t pid,
    const struct iovec *local_iov,  // 本地缓冲区
    unsigned long liovcnt,
    const struct iovec *remote_iov, // 远程进程地址
    unsigned long riovcnt,
    unsigned long flags);

看实战代码:

cpp 复制代码
int api_memory(int pid) {
    intptr_t address;
    std::string content(256, '\0');
    
    // 读内存
    iovec local_iov = {content.data(), content.size()};
    iovec remote_iov = {(void*)address, content.size()};
    ssize_t nread = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0);
    
    // 写内存
    std::string new_data = "hacked";
    local_iov.iov_base = new_data.data();
    local_iov.iov_len = new_data.size();
    process_vm_writev(pid, &local_iov, 1, &remote_iov, 1, 0);
}

这个API最爽的地方:不需要暂停目标进程。系统帮你处理好原子性问题。

优缺点总结

  • ✅ 无需STOP,对业务无影响
  • ✅ 支持向量读写,一次多个不连续内存块
  • ✅ 效率最高
  • ❌ 需要Linux 3.2+
  • ❌ 权限限制严格,只能读写同用户进程

三种方案对比

特性 ptrace /proc/pid/mem process_vm_xxx
效率
需暂停进程
权限要求 严格
代码复杂度 最低
兼容性 所有Linux 所有Linux Linux 3.2+
典型应用 调试器 监控工具 现代注入工具

四、防护方案:让攻击者知难而退

了解攻击手段是为了更好防护。跨进程内存攻击通常分两步:先静态/动态分析找漏洞,再实施内存读写。

防静态分析

  1. 代码虚拟化:把核心函数翻译成自定义指令集,在虚拟机里执行。反汇编工具看到的是虚拟机代码,完全摸不清逻辑。
  2. 代码混淆:控制流平坦化+虚假分支,把代码逻辑搅成一锅粥。
  3. 符号隐藏:strip掉符号表,加密导入表,让攻击者找不到关键函数。

防动态调试

  1. 反ptrace:检测父进程是否在用ptrace调试,是就直接退出。还可以fork子进程互相ptrace,占用调试接口。
  2. 内存校验:定时检查代码段hash值,发现被修改就自毁。可以用信号或线程做后台监控。
  3. 权限最小化:以最低权限运行,减少被攻击面。敏感操作放独立进程,IPC通信。

产品化方案

如果是商业软件,建议用专业保护方案。比如Virbox Protector这类工具,把上述防护手段打包,提供一站式保护。从代码加密、虚拟化到反调试一条龙,比自己造轮子靠谱。当然,任何防护都不是绝对的安全,只是大幅提高攻击成本。

五、总结

三种方案各有适用场景:

  • 做调试器?用ptrace
  • 做监控采集?用/proc/mem
  • 做游戏注入?用process_vm_xxx

从攻防角度看,没有绝对安全的系统。了解攻击原理,针对性加固,把攻击成本抬高到不划算,就是胜利。

相关推荐
腻害兔8 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
Yana.nice9 小时前
Linux 只保留 30 天内日志(find命令删除日志文件)
linux·运维·chrome
码智社9 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
萧瑟余晖10 小时前
JDK 26 新特性详解
java·开发语言
马优晨11 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
andxe11 小时前
安科士 AndXe 技术博客:400G QSFP112 SR4 光模块|AI 算力与超算短距互联最优方案
网络·人工智能·光模块·光通信
shiyi.十一12 小时前
第2章:应用层 — 知识要点与架构
网络·计算机网络·架构
DFT计算杂谈12 小时前
无 Root 权限在 Tesla K80 零门槛部署 DeepSeek 大模型
linux·服务器·网络·数据库·机器学习
水境传感 李兆栋12 小时前
GNSS 位移监测站 :毫米级感知,筑牢安全监测防线
网络
维天说13 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json