1

我正在尝试使用保护修改 .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 是一种返回 int 的方法,该 int 存储在函数之后 5 个字节(这就是 foo+5 的原因)

4

3 回答 3

5

来自man mprotect

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

您没有注意addr需要是 的倍数的部分PAGESIZE,显然......虽然在手册页的至少一个版本中,该要求并没有特别明确,只是简单地说“指定内存页面的所需保护(s) 包含部分或全部区间 [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()并确保您真正了解您在做什么......

于 2013-12-04T17:37:07.867 回答
5

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

#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

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

于 2013-12-04T18:16:15.293 回答
1

的地址参数mprotect需要是页面对齐的,并且大小参数是页面的整数。此外,将页面设置为PROT_WRITE单独意味着您不再允许阅读它 - 这个特定页面将在文本段中,所以它也需要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数据会触发未定义的行为,无论您是否使用操作系统原语来禁用包含它的页面的写保护。当我第一次测试这段代码时,我没有const int foo = 23;在它自己的文件中,并且 GCC将这两个 printf调用都重写为printf("...", 23, &foo). 这是一个有效的优化。 如果我打开链接时优化,我希望即使常量的定义被移动到它自己的文件中也会发生这种情况。此外,GCC 也有权替换*(int *)&foo = 42;为无条件abort()或陷阱指令。

于 2013-12-04T17:43:30.763 回答