process_vm_readv/process_vm_writev 接口详解

基础接口

process_vm_readv(2)

NAME
       process_vm_readv, process_vm_writev - 在进程间传输数据

SYNOPSIS
       #define _GNU_SOURCE
       #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);
       
       ssize_t process_vm_writev(pid_t pid,
                                const struct iovec *local_iov,
                                unsigned long liovcnt,
                                const struct iovec *remote_iov,
                                unsigned long riovcnt,
                                unsigned long flags);

DESCRIPTION
       这些系统调用允许直接在调用进程和指定进程(pid)的内存之间传输数据,
       无需通过内核缓冲区复制。

       process_vm_readv() 从远程进程读取数据到本地进程
       process_vm_writev() 从本地进程写入数据到远程进程

       参数说明:
       - pid: 目标进程ID
       - local_iov: 本地内存区域描述符数组
       - liovcnt: 本地iovec数组元素个数
       - remote_iov: 远程内存区域描述符数组
       - riovcnt: 远程iovec数组元素个数
       - flags: 保留字段,必须为0

RETURN VALUE
       成功时返回传输的字节数,失败时返回-1并设置errno

ERRORS
       EACCES     没有权限访问目标进程内存
       EFAULT     指定的地址范围无效
       EINVAL     参数无效
       ENOMEM     内存不足
       EPERM      没有权限操作目标进程
       ESRCH      目标进程不存在

VERSIONS
       Linux 3.2+ 支持这些系统调用

CONFORMING TO
       这些是Linux特有的系统调用

NOTES
       需要相同用户ID或CAP_SYS_PTRACE权限
       目标进程必须正在运行
       不会触发目标进程的信号处理程序

数据结构

iovec 结构体

struct iovec {
    void  *iov_base;    /* 起始地址 */
    size_t iov_len;     /* 缓冲区长度 */
};

使用示例

示例1:基本内存读取

#define _GNU_SOURCE
#include <sys/uio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

// 目标进程中需要读取的变量
int target_global_var = 42;
char target_string[] = "Hello from target process!";

int read_remote_memory(pid_t target_pid) {
    struct iovec local_iov[2];
    struct iovec remote_iov[2];
    ssize_t result;
    int local_int;
    char local_buffer[100];
    
    // 设置本地缓冲区
    local_iov[0].iov_base = &local_int;
    local_iov[0].iov_len = sizeof(local_int);
    local_iov[1].iov_base = local_buffer;
    local_iov[1].iov_len = sizeof(local_buffer);
    
    // 设置远程内存地址(需要知道目标进程中的确切地址)
    remote_iov[0].iov_base = &target_global_var;  // 实际使用中需要通过其他方式获取
    remote_iov[0].iov_len = sizeof(target_global_var);
    remote_iov[1].iov_base = target_string;
    remote_iov[1].iov_len = strlen(target_string) + 1;
    
    // 读取远程进程内存
    result = process_vm_readv(target_pid,
                             local_iov, 2,
                             remote_iov, 2,
                             0);
    
    if (result == -1) {
        perror("process_vm_readv");
        return -1;
    }
    
    printf("Read %zd bytes\n", result);
    printf("Remote int value: %d\n", local_int);
    printf("Remote string: %s\n", local_buffer);
    
    return 0;
}

示例2:内存写入操作

#define _GNU_SOURCE
#include <sys/uio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

// 目标进程中的可修改变量
int target_writable_var = 100;
char target_writable_buffer[256] = "Original content";

int write_remote_memory(pid_t target_pid) {
    struct iovec local_iov[2];
    struct iovec remote_iov[2];
    ssize_t result;
    int new_value = 999;
    char new_string[] = "Modified by process_vm_writev!";
    
    // 设置本地数据源
    local_iov[0].iov_base = &new_value;
    local_iov[0].iov_len = sizeof(new_value);
    local_iov[1].iov_base = new_string;
    local_iov[1].iov_len = strlen(new_string) + 1;
    
    // 设置远程内存地址
    remote_iov[0].iov_base = &target_writable_var;
    remote_iov[0].iov_len = sizeof(target_writable_var);
    remote_iov[1].iov_base = target_writable_buffer;
    remote_iov[1].iov_len = strlen(new_string) + 1;
    
    // 写入远程进程内存
    result = process_vm_writev(target_pid,
                              local_iov, 2,
                              remote_iov, 2,
                              0);
    
    if (result == -1) {
        perror("process_vm_writev");
        return -1;
    }
    
    printf("Wrote %zd bytes to remote process\n", result);
    return 0;
}

示例3:完整的工作示例

目标进程代码 (target.c)

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

volatile int shared_int = 12345;
char shared_string[256] = "This is shared data from target process";
int running = 1;

void signal_handler(int sig) {
    printf("Received signal %d\n", sig);
    running = 0;
}

int main() {
    printf("Target process PID: %d\n", getpid());
    printf("Shared int address: %p\n", &shared_int);
    printf("Shared string address: %p\n", shared_string);
    printf("Shared int value: %d\n", shared_int);
    printf("Shared string value: %s\n", shared_string);
    
    // 安装信号处理程序
    signal(SIGUSR1, signal_handler);
    
    printf("Target process running... Send SIGUSR1 to stop\n");
    
    while (running) {
        printf("shared_int = %d, shared_string = %s\n", 
               shared_int, shared_string);
        sleep(2);
    }
    
    printf("Target process exiting...\n");
    printf("Final values - shared_int = %d, shared_string = %s\n", 
           shared_int, shared_string);
    
    return 0;
}

访问进程代码 (accessor.c)

#define _GNU_SOURCE
#include <sys/uio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <target_pid>\n", argv[0]);
        exit(1);
    }
    
    pid_t target_pid = atoi(argv[1]);
    struct iovec local_iov[2];
    struct iovec remote_iov[2];
    ssize_t result;
    int local_int;
    char local_string[256];
    int new_int = 99999;
    char new_string[] = "Modified by accessor process!";
    
    printf("Accessing process PID: %d\n", getpid());
    printf("Target PID: %d\n", target_pid);
    
    // 读取远程进程内存
    printf("\n--- Reading remote memory ---\n");
    local_iov[0].iov_base = &local_int;
    local_iov[0].iov_len = sizeof(local_int);
    local_iov[1].iov_base = local_string;
    local_iov[1].iov_len = sizeof(local_string);
    
    // 注意:这里需要知道目标进程的确切内存地址
    // 在实际应用中,这些地址需要通过调试信息或其他方式获取
    remote_iov[0].iov_base = (void*)0x601040;  // 需要根据实际情况调整
    remote_iov[0].iov_len = sizeof(int);
    remote_iov[1].iov_base = (void*)0x601060;  // 需要根据实际情况调整
    remote_iov[1].iov_len = sizeof(local_string);
    
    result = process_vm_readv(target_pid,
                             local_iov, 2,
                             remote_iov, 2,
                             0);
    
    if (result == -1) {
        perror("process_vm_readv");
        printf("Note: You need to adjust memory addresses based on target process\n");
        return 1;
    }
    
    printf("Read %zd bytes\n", result);
    printf("Remote int value: %d\n", local_int);
    printf("Remote string: %s\n", local_string);
    
    // 修改远程进程内存
    printf("\n--- Writing to remote memory ---\n");
    local_iov[0].iov_base = &new_int;
    local_iov[0].iov_len = sizeof(new_int);
    local_iov[1].iov_base = new_string;
    local_iov[1].iov_len = strlen(new_string) + 1;
    
    result = process_vm_writev(target_pid,
                              local_iov, 2,
                              remote_iov, 2,
                              0);
    
    if (result == -1) {
        perror("process_vm_writev");
        return 1;
    }
    
    printf("Wrote %zd bytes to remote process\n", result);
    
    // 再次读取验证修改
    printf("\n--- Verifying changes ---\n");
    result = process_vm_readv(target_pid,
                             local_iov, 2,
                             remote_iov, 2,
                             0);
    
    if (result != -1) {
        printf("Remote int value after write: %d\n", local_int);
        printf("Remote string after write: %s\n", local_string);
    }
    
    // 发送信号给目标进程
    printf("\n--- Sending signal to target process ---\n");
    if (kill(target_pid, SIGUSR1) == -1) {
        perror("kill");
    } else {
        printf("Signal sent successfully\n");
    }
    
    return 0;
}

示例4:实用工具 – 进程内存检查器

#define _GNU_SOURCE
#include <sys/uio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

// 简单的内存转储工具
int dump_remote_memory(pid_t pid, unsigned long addr, size_t size) {
    char *buffer;
    struct iovec local_iov[1];
    struct iovec remote_iov[1];
    ssize_t result;
    size_t i;
    
    buffer = malloc(size);
    if (!buffer) {
        perror("malloc");
        return -1;
    }
    
    local_iov[0].iov_base = buffer;
    local_iov[0].iov_len = size;
    
    remote_iov[0].iov_base = (void*)addr;
    remote_iov[0].iov_len = size;
    
    result = process_vm_readv(pid,
                             local_iov, 1,
                             remote_iov, 1,
                             0);
    
    if (result == -1) {
        perror("process_vm_readv");
        free(buffer);
        return -1;
    }
    
    printf("Memory dump at 0x%lx (%zd bytes):\n", addr, result);
    for (i = 0; i < (size_t)result; i++) {
        if (i % 16 == 0) {
            printf("\n%08lx: ", addr + i);
        }
        printf("%02x ", (unsigned char)buffer[i]);
    }
    printf("\n");
    
    // 显示可打印字符
    printf("\nASCII: ");
    for (i = 0; i < (size_t)result; i++) {
        if (i % 16 == 0 && i > 0) {
            printf("\n       ");
        }
        printf("%c", (buffer[i] >= 32 && buffer[i] <= 126) ? buffer[i] : '.');
    }
    printf("\n");
    
    free(buffer);
    return 0;
}

int main(int argc, char *argv[]) {
    if (argc != 4) {
        fprintf(stderr, "Usage: %s <pid> <address> <size>\n", argv[0]);
        fprintf(stderr, "Example: %s 1234 0x601040 64\n", argv[0]);
        exit(1);
    }
    
    pid_t target_pid = atoi(argv[1]);
    unsigned long address = strtoul(argv[2], NULL, 0);
    size_t size = strtoul(argv[3], NULL, 0);
    
    printf("Dumping %zu bytes from process %d at address 0x%lx\n", 
           size, target_pid, address);
    
    return dump_remote_memory(target_pid, address, size);
}

示例5:批量内存操作

#define _GNU_SOURCE
#include <sys/uio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define MAX_IOV 1024

typedef struct {
    void *remote_addr;
    size_t size;
    void *local_buffer;
} memory_region_t;

// 批量读取多个内存区域
int batch_read_memory(pid_t pid, memory_region_t *regions, int count) {
    struct iovec local_iov[MAX_IOV];
    struct iovec remote_iov[MAX_IOV];
    ssize_t result;
    int i;
    
    if (count > MAX_IOV) {
        fprintf(stderr, "Too many regions\n");
        return -1;
    }
    
    // 设置iovec数组
    for (i = 0; i < count; i++) {
        local_iov[i].iov_base = regions[i].local_buffer;
        local_iov[i].iov_len = regions[i].size;
        remote_iov[i].iov_base = regions[i].remote_addr;
        remote_iov[i].iov_len = regions[i].size;
    }
    
    result = process_vm_readv(pid,
                             local_iov, count,
                             remote_iov, count,
                             0);
    
    if (result == -1) {
        perror("process_vm_readv");
        return -1;
    }
    
    printf("Batch read completed: %zd bytes total\n", result);
    return 0;
}

// 批量写入多个内存区域
int batch_write_memory(pid_t pid, memory_region_t *regions, int count) {
    struct iovec local_iov[MAX_IOV];
    struct iovec remote_iov[MAX_IOV];
    ssize_t result;
    int i;
    
    if (count > MAX_IOV) {
        fprintf(stderr, "Too many regions\n");
        return -1;
    }
    
    // 设置iovec数组
    for (i = 0; i < count; i++) {
        local_iov[i].iov_base = regions[i].local_buffer;
        local_iov[i].iov_len = regions[i].size;
        remote_iov[i].iov_base = regions[i].remote_addr;
        remote_iov[i].iov_len = regions[i].size;
    }
    
    result = process_vm_writev(pid,
                              local_iov, count,
                              remote_iov, count,
                              0);
    
    if (result == -1) {
        perror("process_vm_writev");
        return -1;
    }
    
    printf("Batch write completed: %zd bytes total\n", result);
    return 0;
}

int main() {
    printf("Batch memory operations example\n");
    printf("This example shows how to perform batch operations\n");
    printf("You need to provide actual PID and memory addresses\n");
    return 0;
}

使用限制和注意事项

权限要求

  1. 相同用户: 调用进程和目标进程必须有相同的有效用户ID
  2. ptrace权限: 或者调用进程需要 CAP_SYS_PTRACE 权能
  3. 进程状态: 目标进程必须正在运行(不是僵尸进程)

安全限制

  1. 内存保护: 不能访问目标进程的受保护内存区域
  2. 地址有效性: 必须确保远程地址在目标进程的有效地址空间内
  3. 对齐要求: 某些体系结构可能有内存对齐要求

技术限制

  1. 最大IOV数量: 系统可能限制iovec数组的最大长度
  2. 性能考虑: 大量小的传输可能不如批量传输高效
  3. 原子性: 单次调用内的多个传输不是原子的

错误处理

  1. 部分传输: 可能只传输部分数据,需要检查返回值
  2. 地址错误: 无效地址会导致整个操作失败
  3. 进程终止: 目标进程在操作过程中终止会导致错误

最佳实践

  1. 地址获取: 使用调试信息或符号表获取准确的内存地址
  2. 缓冲区管理: 确保本地缓冲区足够大且生命周期正确
  3. 错误检查: 始终检查返回值并处理错误情况
  4. 权限验证: 在执行操作前验证权限和进程状态

这些系统调用提供了强大的进程间内存访问能力,但使用时需要谨慎处理安全和权限问题。

此条目发表在未分类分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注