mprotect系统调用用法及示例

mprotect 是 Linux 系统中用于修改内存区域保护权限的系统调用。它允许动态调整已分配内存的访问权限(如读、写、执行),常用于内存安全优化或动态代码生成等场景。


函数原型

1
2
3
#include <sys/mman.h>

int mprotect(void *addr, size_t len, int prot);
  • 参数

    • addr:内存区域的起始地址(必须页对齐)。
    • len:内存区域的长度(必须是页大小的整数倍)。
    • prot:新的保护权限,取以下值的位或组合:
      • PROT_NONE:不可访问。
      • PROT_READ:可读。
      • PROT_WRITE:可写。
      • PROT_EXEC:可执行。
  • 返回值

    • 成功时返回 0,失败返回 -1 并设置 errno

使用步骤

  1. 获取系统页大小:通过 sysconf(_SC_PAGESIZE) 获取。
  2. 分配对齐的内存:使用 mmap 分配页对齐的内存(malloc 分配的内存可能不符合要求)。
  3. 修改权限:调用 mprotect 调整内存保护权限。
  4. 错误处理:检查返回值,处理可能的错误。

示例代码

以下示例演示如何通过 mprotect 修改内存权限,并观察权限变更后的行为:

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
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

int main() {
// 获取系统页大小
long page_size = sysconf(_SC_PAGESIZE);
if (page_size == -1) {
perror("sysconf");
return 1;
}

// 分配页对齐的内存,初始权限为 PROT_NONE(不可访问)
void *mem = mmap(NULL, page_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mem == MAP_FAILED) {
perror("mmap");
return 1;
}

// 修改权限为可读可写
if (mprotect(mem, page_size, PROT_READ | PROT_WRITE) == -1) {
perror("mprotect");
munmap(mem, page_size);
return 1;
}

// 安全写入数据
*((int *)mem) = 123;
printf("写入成功:%d\n", *((int *)mem));

// 修改权限为 PROT_NONE(不可访问)
if (mprotect(mem, page_size, PROT_NONE) == -1) {
perror("mprotect");
munmap(mem, page_size);
return 1;
}

// 尝试访问会触发段错误(程序崩溃)
printf("尝试访问无权限内存...\n");
printf("%d\n", *((int *)mem)); // 此行会导致 SIGSEGV

// 清理内存(实际上不会执行到这里)
munmap(mem, page_size);
return 0;
}

关键说明

  1. 页对齐与长度

    • mprotect 要求地址和长度是页对齐的,因此使用 mmap 而非 malloc 分配内存。
    • 通过 sysconf(_SC_PAGESIZE) 获取页大小(通常为 4096 字节)。
  2. 权限变更效果

    • 初始分配内存为 PROT_NONE,任何访问均会导致段错误。
    • 修改为 PROT_READ | PROT_WRITE 后,可正常读写。
    • 再次修改为 PROT_NONE 后,访问会触发崩溃。
  3. 错误处理

    • 检查 mprotect 返回值,失败时通过 perror 输出错误信息。
    • 使用 munmap 释放内存,避免泄漏。

注意事项

  • 信号处理:若需在权限禁止时避免崩溃,可通过捕获 SIGSEGV 信号处理段错误。
  • 执行权限:将内存设置为 PROT_EXEC 后,可执行其中的机器码(需谨慎使用,存在安全风险)。
  • 性能影响:频繁调用 mprotect 可能导致性能下降,因涉及内核操作。