反汇编变量
全局变量
全局变量和常量有着相似的性质,都是在程序执行之前就存在了。常量在PE的不可写数据节中,全局变量和局部变量在PE的可读可写数据节中。
下面来看一个简单的例子:
#include<stdio.h>intg_num=0x12345678;intmain(){printf("%d\r\n",g_num);return0;}调试该程序时我们发现传递g_num变量的时候,直接传递了一个常数地址。我们用该地址减去模块的加载起始地址,就得到了该变量的相对虚拟地址。
通过PE分析工具,我们就可以利用该相对虚拟地址在可执行文件中找到该变量。
具有初始值的全局变量,其值在链接时被写入创建的PE文件中,当用户执行该文件时,操作系统先分析这个PE中的数据,将各节中的数据填入对应的虚拟内存地址中。这时全局变量就已经存在了,PE的分析和加载工作完成后,才开始执行入口点的代码。
由于这种性质,可以解释为什么全局变量会有一个默认值。
访问全局变量与访问常量类似,都是通过立即数访问。由于全局变量在编译期就已经确定了地址,因此编译器在编译的过程中可以计算出一个固定的地址值。
若定义多个全局变量,它们的内存地址根据定义先后从低到高排列。
局部变量
局部变量和全局变量最大的区别就是声明周期不同,在访问的限制上是编译期检测的。在访问方式上,局部变量是通过栈指针相对间接访问的,而全局变量的内存地址在全局数据区中,通过栈指针无法访问到。
访问局部变量是通过esp和ebp进行访问的。
定义多个局部变量,它们的内存地址是从高到低排列的。
局部静态变量
全局静态变量和全局变量相似,只是在访问范围上不同,前者只能在当前文件中访问。
实际上,局部静态变量和全局变量都保存在执行文件的数据区中,但由于局部静态变量被定义在某一作用域内,让我们产生了错觉,误认为此处为它的生命起始点。实则不然,局部静态变量会预先被作为全局变量处理,而它的初始化部分只是在做赋值操作而已。
对于局部静态变量,在汇编层面上会使用一个标志来检测静态局部变量是否被初始化过。这个标志是区分全局变量和局部静态变量的重要依据。初始化的过程可以类比单例模式。在c++11之前静态局部变量的初始化不是线程安全的。
既然局部静态变量和全局变量一样都保存在执行文件的数据区中,那么如何避免函数外部访问该局部静态变量呢?答案是永固名称粉碎法,对变量的名称进行适当的修改。C++的函数重载也是如此,同样是先粉碎函数名称再组合出新名称)。
堆变量
堆变量的识别要点是调用了malloc或者new(实际上掉哟个了malloc)初始化。堆在内存中由一个结构体体现,结构体中除了保存有堆数据的指针,还有上溢和下溢检测标志字节,还包括堆的大小,使用次数等。
在指定了-MDd编译选项的情况下,在堆变量指向的内存中,前后分别有四个字节的0xFD,这是避免内存上溢和下溢的保护字节。如果没有指定使用-MDd编译选项,那么编译器为了优化程序的运行速度和程序的大小,会优化掉这些字节,在内存中就看不到结构体中的内容。