Funny.exe分析
样本分析
初步过程
~~(拿到程序,就被Windows Defender给杀了)~~我们运行一下程序会发现其在一直输出Hello word
,我们先过一遍 ExeinfoPe.exe
发现这个是一个32位的程序
将其再拖入我们的~~【好朋友】~~ IDA里面进行反汇编的分析
打开一看,就仅有 2 个函数
Fun() 分析
点开 fun 函数,我们可以看到内部的构造也是十分的简单
也就仅仅一个循环,一个数组,在循环的过程中将arr[i]
的值赋值为 0,输出一句 Hello word
不过要注意的是这个i
可以到达 7 而数组arr[i]
最多也就只有arr[5]
我们怀疑可能是越界而导致栈溢出的一直循环输出
_main()分析
打开_main()
函数同样也是十分的 “ 简略 ” 可以看到组成的部分函数还是一样的少:
我们再对里面的各个函数进行分析,我们先判断_initialized
的值在最开始是否为 0 ,而进而执行下面的函数转入汇编层打上对应的断点进行判断。
我们启动调试
可以的看到在开始前_initialized
的值是 0,那么在 if 语句中就会在非的转化下变为真,而执行后面的的__do_global_ctors()
__do_global_ctors()
里面构造了一个指针 i ,并对其分配相应的地址值,在 fun 的函数中被调用,同时在里面也创建了一个一个初始化函数的列表,称为 __CTOR_LIST__
,和一个终止函数列表,称为 __DTOR_LIST__
,这个函数可以说是为 i 的生成创建了空间,同时也为下面的死循环埋下了伏笔。
分析循环为什么一直不停
栈的理解
我们一般来说程序的数据是存储在一个栈上面的,可栈又是什么东西呢?
栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针)。
栈是允许在同一端进行插入和删除操作的特殊线性表。允许进行插入和删除操作的一端称为栈顶(top),另一端为栈底(bottom);栈底固定,而栈顶浮动;栈中元素个数为零时称为空栈。插入一般称为进栈(PUSH),删除则称为退栈(POP)。栈也称为先进后出表。
这里贴一张图:
我们可以把这个栈简单的理解为一个收纳的箱子(盒子),我们把一件件的衣服放入这个箱子里面,如果我们想要去取出最后一件衣服的话是不是需要拿走上面堆着的衣服?拿走衣服的过程我们就可以理解为出栈,而放入衣服的和过程则就是入栈啦。
简单的理解了栈后,可这个栈上面存储的数据与我们的死循环又有什么关系?
我们模仿fun()
同样写一个程序,如下:
1 |
|
可以试着拿去运行一下下呢?
你会发现这个就是和那个程序一样陷入了死循环里,回到我们刚刚讲到的栈,这个时候我们会提到一个新的名字栈溢出,听名字我们便可以知道溢出,肯定是箱子装不下了(回到之前装衣服的例子),但是这个多出来的衣服会到哪里去呢?
我们之前不是分析到了有一个__do_global_ctors()
函数,我们在里面发现了有一个指针 i ,这个 i 是也有一个自己的 ”箱子“ 的,我们多出来的衣服可能就会被放到 i 的 ”箱子“ 里面去。我们画一个图来看看 :
简单的画一个图我们可以发现,arr[i]
会在越界的时候占用掉 i 的箱子而改变 i 的值,也就是我们之前的那一个程序中的 :
1 |
|
arr[i]
在重新赋值为 0 的过程中因为越界了会让我们的 i 没有地方放自己的东西了而被迫改变成arr[i]
的数值~~( 多 坏 啊 ~ )~~
IDA分析
我们回到 IDA 里看看是不是发生了这个过程,我们到输出的部分的汇编层看看呢 :
我们在这个地方对程序进行对应的调试,简单重复for里面的看看便会发现 i 的值每次在越界之后又会 ”神奇“ 的再次回到 0 ,我们不免想起之前的赋值 arr[i]=0
这个过程,同时也可以猜测出死循环的原因了,arr[i]
在循环过程中发生了越界而占用了 i 的栈空间使得 i 的值被 arr
所替代,而回归到 0 ,而继续循环。回 到 梦 开 始 的 地 方
至此程序的原理就分析完啦。我们也可以发现题目的问题,了解到了 arr[i]
在溢出后的栈而改变了 i ,所以~~arr[i]
是真的坏!~~栈溢出是一个非常严重的问题。