一起读反汇编代码之变量在映像中的存放及访问
不知道小白有没有这样的困惑,就是单独一条的汇编语句能读懂,但是好几条组合到一起就感觉云里雾里了那你就适合跟我们一起来读反汇编代码了,把基础指令的反汇编代码读懂才能逆向出一些更难的算法,今天我们一起来读的就是反汇编的基础部分———变量在映像中的存放及访问。
上一节我们一起观察了函数的局部变量在栈帧中的存放,但是C++中可不只有局部变量这一种,其他几种变量也是存放在栈帧中的吗?访问它们的方式又是怎样的呢?今天我们就一起来看一看局部变量、全局变量、静态局部变量、静态全局变量这四种变量
一.局部变量及全局变量
从定义方式来看,局部变量就是定义在函数内的变量;全局变量就是定义在函数外的变量。
那么这两种变量在内存中究竟是怎样存储的呢,访问它们的方式又是否相同呢?
上一节我们一起看了函数的调用,知道了函数的局部变量是存放在当前函数的栈帧中的。
因为函数调用完成后会清除该函数的栈帧,所以局部变量才有了生命周期。
而我们知道全局变量的生命周期是跟程序一样长的,那么它还能存放在栈帧/栈空间中吗?
这里我们定义了两个变量,一个全局变量、一个局部变量
看一下反汇编的结果
这个参数2(局部变量),是不是很熟悉,就是上一章我们讲过的局部变量。
再看全局变量,发现是立即数寻址,其是直接存放在内存地址中的。
现在有理由猜测全局变量的值是存在于PE文件中的,因为根据其是立即数寻址我们能判断它在内存中的地址是固定的,而程序运行时文件又是映射到内存中去的,所以既然它在内存映像中的地址是固定的那么在PE文件中的地址也应该是固定的。
这里补充一句:PE文件加载到内存中的时候,文件并不是原封不动的加载,而是根据各节区头中定义的节区起始地址、节区大小等信息加载的。所以磁盘文件中的PE与内存中的PE具有不同的形态,而我们又把内存中的形态称为“映像”。
这里涉及到了一些PE知识,精力有限而且PE知识又比较多所以我这就不多说了,以后有能力有精力了可能会发一篇制作PEView的帖子和大家一起学习一下PE格式。
哈哈,思路再转回来,我们猜想全局变量在PE文件中的地址是固定的,那么我们就看看呗
这里涉及到RVA(相对虚拟地址)和RAW(文件偏移)之间的转换,其实就是在内存中的地址和在磁盘中的地址之间的转换,因为前面说了,这两个地址是不同的。
这里有个公式RAW=RVA-VA+RawOffset
哈哈,我直接解释下吧,虽然PE文件是映射到内存中去的,但是地址间仍然是有关系的,我们知道PE文件是一段一段的,有数据段、代码段等等,那么数据存放的地址跟段首的偏移值应该是固定的。举个例子,两个数组,一个是从1~10,一个是从11~20,现在第一个数组的第5个数要被映射到第二个数组中去,我们会把它放在哪呢,是15对不对,为什么想把它放在15这个位置呢,因为5-1=4,而15-11也等于4,对不对,这个偏移量是固定的。
而PE文件映射到内存中也是这样
我们首先查看0042 4d8c这个地址在哪个区段
很明显是在.data段,再减去段首地址,得出偏移地址是d8c
再试用Stud_PE工具查看.data段在PE文件中的起始地址(吾爱工具包中也有)
是24000,所以得知我们的全局变量在PE文件中的地址应该是24d8c
使用winHex查看
果不其然,我们的全局变量正安静地在里面躺着呢。
所以我们可以下这么个结论:全局变量的初始值在链接时就被写入到所创建的PE文件中去了,当PE文件执行时,数据被映射到内存中去之后,全局变量就已经存在了。
二.全局静态变量和局部静态变量
在我们观察这几种变量的反汇编代码之前,我们先讲一讲C++中的关键字static
一谈到static关键字,可能很多人想到不就是用来定义一个静态变量的吗,当然这是static的一个作用,除此之外它其实还有些别的作用:
1.用于隐藏变量
这个甚至是static最重要的作用,就是用于隐藏变量及函数,我们有时编程的时候会同时编译多个文件,而如果你在A.c中定义的变量和函数没有加static前缀,那么你在b.c中就也能使用这些变量和函数,换句话说,不加static的变量和函数具有全局可见性。
2.保持变量内容的持久化
静态变量会在程序刚开始运行的时候完成初始化,也是唯一的一次(等一下会解释这是什么意思),共有两种变量存放在静态存储区--静态变量和全局变量,不过静态变量会控制其可见范围。
3.默认变量的初始化为零
直接上图
带有static前缀,哪怕你不初始化,它也默认为0
好了说完static修饰符,我们正式来看看全局静态变量和局部静态变量
全局静态变量我就不说了:) ,它的存储跟访问方式跟全局变量差不多,只不过由于static修饰符,限制了外部源文件访问。
前面我们说过局部静态变量只会被初始化一次,我们验证一下
我们大胆猜测一下,上面这段代码,究竟会输出什么呢,是全部都是0,还是输出0~9呢?
猜对了吗,果然全都是0,因为前面说过,静态变量只会被初始化一次
那么我们大胆猜测一下,编译器是如何知道这个变量是被初始化过了呢,是这个变量有一个标志位标志它被初始化过了呢;还是就是专门划分了一个区域,里面放的全是被赋过值的静态变量,然后里面的值不再被允许初始化了;还是说有一张表,里面放着指向初始化过的静态变量的指针,凡是指向的静态变量都不能初始化了,还是别的什么原因呢。
我们一起来看看反汇编代码
进到main函数里,发现这么一段代码,典型的for循环语句,我们进到循环中的call去看看,应该就是调用my_printf函数了
发现前四行代码就是将427C4C中的值传给eax,然后又进行了是否等于0的判断,不等于0则跳转。
如果不跳转呢,发现用一个or 1将这个值变成了1,然后又将传进来的参数()赋给了一个变量,这个变量就是我们定义的静态局部变量,因为你发现之后这个变量会作为printf的参数压栈。
那么我们就知道了,是因为有一个标志位,它用来标志这个局部静态变量有没有被赋值,如果赋过值,那么标志位变为1,以后都会直接跳过赋值阶段。
最后再附上源码跟程序:https://www.lanzouj.com/i68yd6h
好了,到这里今天的学习就结束了,能力有限,可能会有疏漏之处,欢迎在评论区指出。
感谢各位看官!
如果觉得这篇帖子对你有一点帮助的话,希望能动动小手帮忙给下免费的评分,谢谢大家了! keyyan 发表于 2019-9-19 21:49
楼主您好,按你说的在易语言试了下静态变量,好像不会都一样吧???
我记得易语言其实就是C语言衍生出来的,所以变化应该不会太大。
那为什么你的结果不一样呢,其实是因为你的rr=tt是在给静态变量赋值,并不是在初始化,静态变量只能初始化一次不假,但哪有不能赋值的道理是不。
直接上图
有用的话,希望能给个免费的评分哦,爱你!{:1_887:} 挺不错了. 看下C++反汇编与逆向解密.里面都有说. 不过实践出真知. 特别是静态变量这里. 标志赋值从而保证静态变量不被修改. 其实如果可以的话.看看附近. 附近不只有标志. 应该还有其它内容. 还有一种比较特殊的情况是把变量分配到寄存器中。 第一个代码会非法访问吧?0x1314不可读(闪电逃 进来学习!谢谢提供! 楼主您好,按你说的在易语言试了下静态变量,好像不会都一样吧??? 学习一下,顺便楼主ID自带音频剪辑(滑稽 本帖最后由 keyyan 于 2019-9-20 08:34 编辑
雪魔王遗风 发表于 2019-9-19 22:47
我记得易语言其实就是C语言衍生出来的,所以变化应该不会太大。
那为什么你的结果不一样呢,其实是因为 ...
好的,大概理解您的意思,谢谢!!!期待您的PE教程{:1_918:} 谢谢,多学习一下这种技术文章。期待后续
页:
[1]
2