You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不使用mmap()的情况下用mprotect()为已有地址开启PROT_WRITE?

问题分析与解决方案

首先,mprotect返回-1时,第一步必须先查看具体错误原因——直接用strerror(errno)打印错误信息,这能帮你快速定位问题,比如是地址无效、权限不足还是参数错误。

结合你的场景,常见的问题和解决方法如下:

1. 地址计算的类型溢出问题

你用int存储对齐后的地址,在64位系统下会直接截断64位地址,导致mprotect传入错误的地址。必须用uintptr_t(标准库定义的指针宽度整数类型)来处理地址计算:

#include <stdint.h>
#define PAGE_SIZE 4096

// 正确的页对齐计算
uintptr_t page_start = ((uintptr_t)base_address + PAGE_SIZE - 1) & ~(uintptr_t)(PAGE_SIZE - 1);
void *addr = (void *)page_start;

2. mprotect的长度参数错误

mprotect操作的是整个内存页,长度必须是页大小的整数倍,不能只传sizeof(int)。哪怕你只需要修改页内的一个int,也要传入PAGE_SIZE:

int ret = mprotect(addr, PAGE_SIZE, PROT_READ | PROT_WRITE);

3. 查看内存映射确认段属性

可以通过/proc/self/maps查看进程的内存分布,确认base_address所在段的初始权限:

# 在程序运行时,另开终端执行(替换成你的进程PID)
cat /proc/[PID]/maps | grep 5555555551e7

如果该段是代码段(.text),部分系统可能限制修改其权限,这时候你可以尝试加上PROT_EXEC(恢复执行权限):

int ret = mprotect(addr, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC);

修正后的完整代码示例

#include <sys/mman.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#define PAGE_SIZE 4096

int main() {
    uintptr_t base_address = 0x00005555555551e7;
    uintptr_t page_start = ((uintptr_t)base_address + PAGE_SIZE - 1) & ~(uintptr_t)(PAGE_SIZE - 1);
    void *addr = (void *)page_start;

    int ret = mprotect(addr, PAGE_SIZE, PROT_READ | PROT_WRITE);
    if (ret == -1) {
        fprintf(stderr, "mprotect failed: %s\n", strerror(errno));
        return 1;
    }

    int *target = (int *)base_address;
    *target = 1;
    printf("Successfully set *%p to %d\n", target, *target);

    return 0;
}

额外排查点

如果还是失败,打印的错误信息会给出明确方向:

  • EINVAL:地址未对齐到页边界(检查page_start计算是否正确)
  • EACCES:进程无权限修改该页属性(尝试添加PROT_EXEC或确认进程权限)
  • ENOMEM:地址不在进程的有效地址空间(但GDB能修改的话,这个可能性很低)

内容的提问来源于stack exchange,提问作者user1094822

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 16:36:07