最简单的一个堆栈溢出攻击实例 (2) [by Progsoft]

下面我们以Release版本为例,解剖程序。

将exe反汇编得到关键代码如下:

函数ShowComputerName: 

  00401030: 8B 4C 24 04        mov         ecx,dword ptr [esp+4 ]
  
0040103483
 EC 0C           sub         esp,0Ch
  
00401037: 8D 44 24 00
        lea         eax,[esp]
  0040103B: 
50
                 push        eax
  0040103C: 
51
                 push        ecx
  0040103D: E8 BE FF FF FF     call        
00401000

  
0040104283 C4 14            add         esp,14h
  
00401045: C3                 ret

 主函数main:

  0040108068 30 50 40 00      push        405030h
  
00401085: E8 A6 FF FF FF     call        00401030
  0040108A: 83 C4 04           add         esp,4

 我们知道函数调用是采用堆栈方式,那么CPU是怎么工作的呢?

在WindowsXP/2000操作系统下,通过大量的汇编代码我们可以看到,函数调用的参数传递是采用堆栈进行。

何故?

原因是我们使用的个人计算机一般都是采用x86系列CPU,由于可用通用寄存器非常少的缘故,只能采用堆栈进行。而作者工作使用的PowerPC系列CPU,由于有32个通用寄存器,所以函数调用的参数都采用寄存器R3、R4……来完成。据说苹果机采用PowerPC应该就属于按照寄存器传递的方式,但作者没有苹果机,有大家可以自己看看。

下面还是回归正题,这个代码怎么跑呢?

 假定代码运行到00401080处,ESP指针为00130000

那么堆栈将如何发展呢?

|          …………             |  低地址
+------------------------------+ 
|                              | 
+------------------------------+  高地址 ESP 00130000

完成push 405030h后,405030h被压栈,ESP指针上移。

|          …………             | 
+------------------------------+  ESP 0012FFFC
|         00405030h            | 
+------------------------------+ 

完成call 00401030后,也就是开始调用函数ShowComputerName。

函数调用后,返回main主函数的 EIP地址0040108A被压栈,同时ESP指针上移。这个压栈数值就是我们要利用的溢出攻击目的。

|          …………             | 
+------------------------------+  ESP 0012FFF8
|         0040108Ah            |
+------------------------------+
|         00405030h            |
+------------------------------+

下面我们就来到溢出函数部分,我们可以知道现在ESP是0012FFF8,所以mov ecx,dword ptr [esp+4]就是将压栈的数组首地址取出来放置到寄存器ecx。

接下来sub esp,0Ch,是预留局部变量pucComputerName的空间,刚好12个字节。ESP上移12个字节,就是pucComputerName首地址。

|          …………             | 
+------------------------------+  ESP 0012FFEC
|       pucComputerName        | 
|           的空间              | 
|                              | 
+------------------------------+ 
|         0040108Ah            |
+------------------------------+
|         00405030h            |
+------------------------------+

再后lea eax,[esp],将esp的值赋值给eax,现在eax指向pucComputerName的首地址,exc指向全局变量g_aucName的首地址

  0040103B: 50                 push        eax
  0040103C: 51                 push        ecx

eax和ecx被压栈给函数GetName调用时候使用,这样栈空间图演变为:

|          …………             | 
+------------------------------+  ESP 0012FFE4
|         00405030h            |
+------------------------------+ 
|         0012FFECh            |
+------------------------------+ 
|       pucComputerName        | 
|           的空间              | 
|                              | 
+------------------------------+ 
|         0040108Ah            |
+------------------------------+
|         00405030h            |
+------------------------------+

接下来调用函数GetName,返回函数ShowComputerName的EIP指针00401042已经被压栈,ESP再次上移,栈空间图演变为:

|          …………             | 
+------------------------------+  ESP 0012FFE0
|         00401042h            |
+------------------------------+ 
|         00405030h            |
+------------------------------+ 
|         0012FFECh            |
+------------------------------+ 
|       pucComputerName        | 
|           的空间              | 
|                              | 
+------------------------------+ 
|         0040108Ah            |
+------------------------------+
|         00405030h            |
+------------------------------+

 接下来在函数GetName就是复制拷贝了,也就是将g_aucName的内容如实全部拷贝到局部变量pucComputerName所在地址,由于局部变量使用的是栈空间,这样我们main函数返回的EIP就被我们无情改写得面目全非了,欺骗了CPU,目的达到。

拷贝完成暂未返回函数ShowComputerName的栈空间状态。

|          …………             | 
+------------------------------+  ESP 0012FFE0
|         00401042h            |
+------------------------------+
|         00405030h            |
+------------------------------+
|         0012FFECh            |
+------------------------------+
|      'H','e','l','l'         |
|      'o','W','o','r'         |
|      'l','d','!','/0'        |
+------------------------------+
|         00401072h            |
+------------------------------+
|    0x6A,0x00,0x68,0x30,      |
+------------------------------+
|    0x50,0x40,0x00,0x68,      |
+------------------------------+
|    0x30,0x50,0x40,0x00,      |
+------------------------------+
|    0x6A,0x00,0xFF,0x15,      |
+------------------------------+
|    0x90,0x40,0x40,0x00,      |
+------------------------------+
|    0x6A,0x00,0xFF,0x15,      |
+------------------------------+
|    0x48,0x40,0x40,0x00,      |
+------------------------------+

由于GetName调用关系的保留的EIP指针和ESP现在指针都未被破坏,因此ret能正常返回,执行过后。

EIP被置为00401042h,ESP下移为0012FFE4

接下来我们要执行00401042所在指令了,也就是add esp,14h,为什么是14h呢,原因是我们不是push了2个函数参数么?此处也需要“弹出”,此处非pop弹出,是编译器优化结果,直接该esp最快、最直接。14h=0Ch+08h

这样我们的ESP指针就下移到0012FFF8,此时你看看此处返回地址是什么?已经被我们改写为jmp esp指令所在地址,这样执行ret后,EIP被置为00401072h,ESP下移为0012FFFC。

EIP即将执行的指令是jmp esp,而ESP 所在地址就是我们溢出攻击指令。下面发生了什么相信大家都明白了,那就是我们攻击自己溢出漏洞的程序部分被激活。

由于时间太晚了,就写到这里了。

你可能感兴趣的:(最简单的一个堆栈溢出攻击实例 (2) [by Progsoft])