mprotect始终返回无效参数

时间:2013-12-04 17:21:34

标签: c linux unix memory mprotect

我正在尝试使用protect修改.text段中的值,以便为我提供写入权限:

 int pageSize = sysconf(_SC_PAGE_SIZE);

 int *toModify = (int *)(foo+5);
 if (mprotect(toModify, pageSize, PROT_WRITE) < 0 ) {
      perror("mprotect failed with error:");
      return -1;
  }
  *toModify = 5;
  printf("Modify :%i",foo());

mprotect永远不会工作。它总是返回mprotect failed with error:: Invalid argument错误。

foo是一个方法,它返回一个在函数后存储5个字节的int(这就是foo + 5的原因)

3 个答案:

答案 0 :(得分:5)

来自man mprotect

   EINVAL addr is not a valid pointer, or not a multiple of PAGESIZE.

您并未关注addr需要成为PAGESIZE的倍数的部分,显然......虽然在该手册页的至少一个版本中,该要求并未特别明确清除,简单说明“为包含部分或全部间隔[addr,addr + len-1]”的内存页面指定所需的保护。

查找包含特定地址的页面地址并不是特别难,因为您已经完成了pageSize = sysconf(_SC_PAGE_SIZE);位:

static inline void *pageof(const void* p)
{ return (p & ~(pageSize - 1));
}

然后修改您的mprotect来电说mprotect(pageof(toModify), pageSize, ...)。尽管如此,请参阅@Zack的答案,以获取有关您指定的权限的警告。请回过头来阅读mprotect()的手册页,确保您真正了解自己在做什么......

答案 1 :(得分:4)

我在OS X 10.9上执行了以下代码,它似乎具有所需的行为。输出是“foo返回23。”

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <sys/mman.h>


extern int foo(void);


int main(void)
{
    //  New value to write into foo+5.
    int NewValue = 23;

    //  Find page size for this system.
    size_t pagesize = sysconf(_SC_PAGESIZE);

    //  Calculate start and end addresses for the write.
    uintptr_t start = (uintptr_t) &foo + 5;
    uintptr_t end = start + sizeof NewValue;

    //  Calculate start of page for mprotect.
    uintptr_t pagestart = start & -pagesize;

    //  Change memory protection.
    if (mprotect((void *) pagestart, end - pagestart,
            PROT_READ | PROT_WRITE | PROT_EXEC))
    {
        perror("mprotect");
        exit(EXIT_FAILURE);
    }

    //  Write new bytes to desired location.
    memcpy((void *) start, &NewValue, sizeof NewValue);

    //  Some systems could require an invalidate of instruction cache here.

    //  Try modified function.
    printf("foo returns %d.\n", foo());

    return 0;
}

对于foo,我使用了这个汇编代码。这两个来源都是使用cc -arch i386构建的。

    .globl  _foo
_foo:
    nop
    nop
    nop
    nop
    mov $42, %eax
    ret

您应该以这种方式修改代码作为学习练习,而不是在任何已部署的应用程序中使用它。

答案 2 :(得分:1)

mprotect的地址参数需要页面对齐,而size参数是整数页面。此外,将页面设置为PROT_WRITE意味着您不再允许读取 - 此特定页面将位于文本段中,因此需要{{1同样,或者程序在从PROT_EXEC返回时会崩溃(因为该页面上的代码不再被认为是可执行的)。

您对程序的修改可以满足您的需求:

mprotect

警告:写入/* file A */ #include <stdio.h> #include <stdint.h> #include <unistd.h> #include <sys/mman.h> extern const int foo; int main (int argc, char *argv[]) { printf("Before modification: foo = %d @ %p\n", foo, (void *)&foo); size_t pagesize = sysconf(_SC_PAGESIZE); void *foo_page = (void *) (((uintptr_t)&foo) & ~(pagesize - 1)); if (mprotect(foo_page, pagesize, PROT_READ|PROT_WRITE|PROT_EXEC)) { perror("mprotect"); return 1; } *(int *)&foo = 42; /* this is still undefined behavior! */ printf("After modification: foo = %d @ %p\n", foo, (void *)&foo); return 0; } /* file B */ const int foo = 23; 数据会触发未定义的行为,无论您是否使用过操作系统原语来禁用包含它的页面的写保护。当我第一次测试此代码时,我在其自己的文件中没有const,并且GCC将两个 const int foo = 23;调用重写为printf这是一个有效的优化。如果我开启了链接时优化,我希望这会发生即使,常量的定义也会移动到自己的文件中。此外,GCC还有权使用无条件printf("...", 23, &foo)或陷阱指令替换*(int *)&foo = 42;