c-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是一个返回int的方法,该int存储在函数后面5字节(这就是foo+5的原因)

我在OSX10.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

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

来自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()的手册页,确保你真正理解自己在做什么。。。

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 @ %pn", 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 @ %pn", foo, (void *)&foo);
  return 0;
}
/* file B */
const int foo = 23;

警告:写入const数据会触发未定义的行为,无论您是否已使用操作系统原语禁用对包含该数据的页面的写保护。当我第一次测试此代码时,它自己的文件中没有const int foo = 23;,GCC将两个printf调用重写为printf("...", 23, &foo)这是一个有效的优化如果我打开链接时间优化,我希望这种情况会发生,即使常量的定义被移到了它自己的文件中。此外,GCC也有权用无条件abort()或陷阱指令替换*(int *)&foo = 42;

相关内容

  • 没有找到相关文章

最新更新