如何在不使用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
相关产品推荐
相关产品推荐

