字符串常量区读写属性强制修改为可写

常见的0xC0000005错误,往往是程序尝试对不可写内存区域写入数据导致的,这是使用指针的时候的常见错误。不过字符串常量区(也叫数据区,确切的说,字符串常量区是数据区的一部分)、代码区、堆、栈都是系统临时分配的一段内存而已,他们本质上没有区别。通过修改PE文件,我们可以修改对应内存的读写属性,从而规避0xC0000005错误(程序成功地把数据写入字符串常量区)。示例程序:

#include "stdafx.h"
#include <stdlib.h>

int _tmain(int argc, _TCHAR* argv[])
{
  char *p = "hello world";

  system("pause");

  *(p + 2) = 'm';    //此处触发C05错误

  printf("%s\r\n", p);

  system("pause");
  return 0;
}

如图所示,这个程序是可以编译通过并运行的:

但是当我们把程序运行到*(p + 2) = 'm'这一行,程序会因为我们尝试对字符串常量区进行写入而触发C05错误,如图所示:

现在我们对字符串常量区的读写属性进行修改。停止VS中的调试,用文本编辑器打开刚才编译链接生成的可执行文件,如图所示:

可以看到里面有各种区域,我们关心的是rdata区,也就是只读数据区。这个区域的读写属性被编码成了一个字节,位于段落开始后的两行半处(习惯上每行看16字节,两行半就是40字节,十六进制偏移为0x28)。把它从原来的40修改成和data区一样的C0,如图所示:

保存文件,再次运行(注意不要用VS重新编译源代码。重新编译的话,链接器会把rdata区域的读写属性重新置为默认的只读)。可以发现,我们成功地执行了程序的意图,把“hello”里的字母l改成了m,如图所示:

PE文件中各个区域的读写属性是通过位来编码的,前四位二进制数(0000)分别代表写、读、执行、共享(英文缩写为WRES)。所以原来的40(4的二进制0100)代表的只读,被我们修改成了C0(C的二进制1100),也就是可写可读。

posted @ 2017-03-15 19:12  zhugehq  阅读(2352)  评论(0编辑  收藏  举报