当前位置: 首页 > news >正文

process_vm_readv/process_vm_writev 接口详解

process_vm_readv/process_vm_writev 接口详解

基础接口

process_vm_readv(2)

NAMEprocess_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: 保留字段,必须为0RETURN VALUE成功时返回传输的字节数,失败时返回-1并设置errnoERRORSEACCES     没有权限访问目标进程内存EFAULT     指定的地址范围无效EINVAL     参数无效ENOMEM     内存不足EPERM      没有权限操作目标进程ESRCH      目标进程不存在VERSIONSLinux 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 1024typedef 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. 权限验证: 在执行操作前验证权限和进程状态

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

http://www.dtcms.com/a/311557.html

相关文章:

  • 如何在 Ubuntu 24.04 或 22.04 LTS Linux 上安装 Guake 终端应用程序
  • Next.js 怎么使用 Chakra UI
  • LINUX82 shell脚本变量分类;系统变量;变量赋值;四则运算;shell
  • 落霞归雁·思维框架
  • 队列的使用【C++】
  • 【王阳明代数讲义】基本名词解释
  • InfluxDB 与 Node.js 框架:Express 集成方案(一)
  • 【RK3568 RTC 驱动开发详解】
  • 操作系统-lecture5(线程)
  • Terraria 服务端部署(Docker)
  • Trae + Notion MCP:将你的Notion数据库升级为智能对话机器人
  • 自动驾驶中的传感器技术14——Camera(5)
  • C#开发入门指南_学习笔记
  • Clickhouse#表记录转换为insert语句
  • 回归预测 | Matlab实现CNN-LSTM-Multihead-Attention多变量回归预测
  • Spring AI MCP 技术深度解析:从工具集成到企业级实战
  • PyQt6教程(003):运行QTDesigner生成的UI文件
  • 零基础 “入坑” Java--- 十六、字符串String 异常
  • 深入理解C++中的Lazy Evaluation:延迟计算的艺术
  • 搜索与图论(最小生成树 二分图)
  • 无人机光伏巡检漏检率↓78%!陌讯多模态融合算法实战解析
  • 关于解决wandb无法连接的问题(timed out problem)
  • spring学习笔记三
  • pyqt5显示任务栏菜单并隐藏主窗口,环境pyqt5+vscode
  • Python序列去重高级指南:保持顺序的高效去重技术
  • python:如何调节机器学习算法的鲁棒性,以支持向量机SVM为例,让伙伴们看的更明白
  • Linux 系统管理-15-OpenSSH 服务管理
  • NLP——Transformer
  • flutter实时播报的桌面应用遇到的问题
  • I2C(韦东山HAL库)