process_vm_readv/process_vm_writev 接口详解

基础接口

process_vm_readv(2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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 结构体

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

使用示例

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

示例1:基本内存读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#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&#91;] = "Hello from target process!";

int read_remote_memory(pid_t target_pid) {
struct iovec local_iov&#91;2];
struct iovec remote_iov&#91;2];
ssize_t result;
int local_int;
char local_buffer&#91;100];

// 设置本地缓冲区
local_iov&#91;0].iov_base = &local_int;
local_iov&#91;0].iov_len = sizeof(local_int);
local_iov&#91;1].iov_base = local_buffer;
local_iov&#91;1].iov_len = sizeof(local_buffer);

// 设置远程内存地址(需要知道目标进程中的确切地址)
remote_iov&#91;0].iov_base = &target_global_var; // 实际使用中需要通过其他方式获取
remote_iov&#91;0].iov_len = sizeof(target_global_var);
remote_iov&#91;1].iov_base = target_string;
remote_iov&#91;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:内存写入操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#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&#91;256] = "Original content";

int write_remote_memory(pid_t target_pid) {
struct iovec local_iov&#91;2];
struct iovec remote_iov&#91;2];
ssize_t result;
int new_value = 999;
char new_string&#91;] = "Modified by process_vm_writev!";

// 设置本地数据源
local_iov&#91;0].iov_base = &new_value;
local_iov&#91;0].iov_len = sizeof(new_value);
local_iov&#91;1].iov_base = new_string;
local_iov&#91;1].iov_len = strlen(new_string) + 1;

// 设置远程内存地址
remote_iov&#91;0].iov_base = &target_writable_var;
remote_iov&#91;0].iov_len = sizeof(target_writable_var);
remote_iov&#91;1].iov_base = target_writable_buffer;
remote_iov&#91;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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

volatile int shared_int = 12345;
char shared_string&#91;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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#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&#91;]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <target_pid>\n", argv&#91;0]);
exit(1);
}

pid_t target_pid = atoi(argv&#91;1]);
struct iovec local_iov&#91;2];
struct iovec remote_iov&#91;2];
ssize_t result;
int local_int;
char local_string&#91;256];
int new_int = 99999;
char new_string&#91;] = "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&#91;0].iov_base = &local_int;
local_iov&#91;0].iov_len = sizeof(local_int);
local_iov&#91;1].iov_base = local_string;
local_iov&#91;1].iov_len = sizeof(local_string);

// 注意:这里需要知道目标进程的确切内存地址
// 在实际应用中,这些地址需要通过调试信息或其他方式获取
remote_iov&#91;0].iov_base = (void*)0x601040; // 需要根据实际情况调整
remote_iov&#91;0].iov_len = sizeof(int);
remote_iov&#91;1].iov_base = (void*)0x601060; // 需要根据实际情况调整
remote_iov&#91;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&#91;0].iov_base = &new_int;
local_iov&#91;0].iov_len = sizeof(new_int);
local_iov&#91;1].iov_base = new_string;
local_iov&#91;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:实用工具 - 进程内存检查器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#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&#91;1];
struct iovec remote_iov&#91;1];
ssize_t result;
size_t i;

buffer = malloc(size);
if (!buffer) {
perror("malloc");
return -1;
}

local_iov&#91;0].iov_base = buffer;
local_iov&#91;0].iov_len = size;

remote_iov&#91;0].iov_base = (void*)addr;
remote_iov&#91;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&#91;i]);
}
printf("\n");

// 显示可打印字符
printf("\nASCII: ");
for (i = 0; i < (size_t)result; i++) {
if (i % 16 == 0 && i > 0) {
printf("\n ");
}
printf("%c", (buffer&#91;i] >= 32 && buffer&#91;i] <= 126) ? buffer&#91;i] : '.');
}
printf("\n");

free(buffer);
return 0;
}

int main(int argc, char *argv&#91;]) {
if (argc != 4) {
fprintf(stderr, "Usage: %s <pid> <address> <size>\n", argv&#91;0]);
fprintf(stderr, "Example: %s 1234 0x601040 64\n", argv&#91;0]);
exit(1);
}

pid_t target_pid = atoi(argv&#91;1]);
unsigned long address = strtoul(argv&#91;2], NULL, 0);
size_t size = strtoul(argv&#91;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:批量内存操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#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&#91;MAX_IOV];
struct iovec remote_iov&#91;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&#91;i].iov_base = regions&#91;i].local_buffer;
local_iov&#91;i].iov_len = regions&#91;i].size;
remote_iov&#91;i].iov_base = regions&#91;i].remote_addr;
remote_iov&#91;i].iov_len = regions&#91;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&#91;MAX_IOV];
struct iovec remote_iov&#91;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&#91;i].iov_base = regions&#91;i].local_buffer;
local_iov&#91;i].iov_len = regions&#91;i].size;
remote_iov&#91;i].iov_base = regions&#91;i].remote_addr;
remote_iov&#91;i].iov_len = regions&#91;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;
}

使用限制和注意事项

权限要求

相同用户: 调用进程和目标进程必须有相同的有效用户ID

ptrace权限: 或者调用进程需要 CAP_SYS_PTRACE 权能

进程状态: 目标进程必须正在运行(不是僵尸进程)

安全限制

内存保护: 不能访问目标进程的受保护内存区域

地址有效性: 必须确保远程地址在目标进程的有效地址空间内

对齐要求: 某些体系结构可能有内存对齐要求

技术限制

最大IOV数量: 系统可能限制iovec数组的最大长度

性能考虑: 大量小的传输可能不如批量传输高效

原子性: 单次调用内的多个传输不是原子的

错误处理

部分传输: 可能只传输部分数据,需要检查返回值

地址错误: 无效地址会导致整个操作失败

进程终止: 目标进程在操作过程中终止会导致错误

最佳实践

地址获取: 使用调试信息或符号表获取准确的内存地址

缓冲区管理: 确保本地缓冲区足够大且生命周期正确

错误检查: 始终检查返回值并处理错误情况

权限验证: 在执行操作前验证权限和进程状态

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

data-ad-format="auto" data-full-width-responsive="true">