【问题标题】:Writing to function pointer写入函数指针
【发布时间】:2013-09-21 20:50:44
【问题描述】:

为什么下面2个(简化的)例子会出现segfault(Win7下用GCC编译)?

案例 1- 写入函数指针

void f() {return;}
int main()
{
  memcpy(&f, "", 1);
  return 0;
}

案例 2 - 调用数据指针

char f[] ={0xC3};
typedef void(*p)();
int main()
{
  ((p)f)();
  return 0;
}

我知道这是因为我正在编写内存的 RO 部分并跳转到内存的不可执行部分。 (或类似的东西)。我的问题是“为什么”在大局意义上:
什么系统确切地执行了这一点?是在操作系统级别吗?在硬件层面?在硬件级别,但在将二进制文件加载到内存时需要由操作系统设置?这是什么子系统?等等……

也许“如何”比“为什么”更好。

【问题讨论】:

标签: c memory


【解决方案1】:

对于 Windows,这是在操作系统级别通过页面保护强制执行的。每个页面都为其分配了一组保护标志,指定其特征是什么(可读、可写、可执行、保护页面等)。可以使用VirtualProtect 更改页面保护,之后您将能够写入代码页。

在幕后代码不可执行的情况下,可能会受到硬件的支持,例如 NX 标志,这实际上是 VirtualProtect 正在切换的。

【讨论】:

    【解决方案2】:

    在 x86 平台上,它在硬件中实现:CPU 将内存划分为页面(在 32 位平台上通常大小为 4kb),每个页面都有不同的访问位(尽管直到相对较晚的时候才真正支持执行访问标志)。

    页面信息本身存储在内存中的表中; CPU 只保留一个指向主表的寄存器,可能还有一些缓存的内容。

    在操作系统级别,操作系统在每次上下文切换期间设置相应的表指针(并在分配内存的系统调用期间更改标志值),因此 CPU 始终知道当前进程可以访问哪些页面。

    【讨论】:

      猜你喜欢
      • 2016-11-18
      • 1970-01-01
      • 1970-01-01
      • 2014-09-08
      • 2020-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多