一份简单的代码演示缓冲区溢出的危害

最近学习到《深入理解计算机系统》这边书的3.12节的缓冲区溢出,于是写了个简单的测试代码演示了一下通过缓冲区溢出是如何神不知鬼不觉的运行一段代码的。

先上代码运行后再分析:

#include <stdio.h>
void hit()
{
	unsigned char buff[ 100 ] =
	{
	0,0,0,0,	//返回地址	
	'B','O','M','B','\0',
	0x83,0xc4,0x80,             //add    $0xffffff80,%esp
	0x8b,0x44,0x24,0x0c,        //mov    0x0c(%esp),%eax
	0xff,0xf0,                  //push   %eax
	0x55,                      	//push   %ebp
	0x89,0xe5,                  //mov    %esp,%ebp
	0x83,0xec,0x18,             //sub    $0x18,%esp
	0xc7,0x04,0x24,0,0,0,0,    	//movl   $0x402080,(%esp)
	0xe8,0,0,0,0,          		//call   401278 <_puts>
	0xc9,                      	//leave
	0xc3,                      	//ret
	};
	*(unsigned int*)&buff[27] = (unsigned int)&buff[4];
	*(unsigned int*)&buff[32] = (unsigned int)puts - (unsigned int)&buff[36];
	*(unsigned int*)&buff[0] = *(unsigned int*)&buff[sizeof(buff)+12];
	/* 缓冲区溢出:buff[sizeof(buff)+12]的数据被修改 
	 * 然而编译的结果可以看到buff[sizeof(buff)+12]这个地址刚好是
	 * hit函数返回到main时所保存的main函数的下一条指令地址
	 * main函数的栈帧被破坏,结果导致错误指令的执行
	 */
	*(unsigned int*)&buff[sizeof(buff)+12] = (unsigned int)&buff[9];
}
void main()
{
	/*
	 * 如果栈帧不被破坏,则hit函数无任何意义
	 * 栈被破坏,神不知鬼不觉的运行了一个函数 puts("BOMB");
	 */
	hit();	
	printf("\n\n%08x\n",puts);
}

然后我在cygwin中编译运行这段代码:

gcc 版本 4.8.2 (GCC)

hp@hp-PC ~/c
$ gcc y.c

hp@hp-PC ~/c
$ ./a.exe
BOMB


004012e8


从代码上看,hit函数什么也没做就是往buff内填充数据,然后是输出的puts函数的地址,但是运行后却发现多了一个BOMB的输出。

再来看看hit函数的汇编代码。

hp@hp-PC ~/c
$ gcc -c y.c && objdump -d y.o

y.o:     文件格式 pe-i386


Disassembly of section .text:

00000000 <_hit>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   57                      push   %edi
   4:   53                      push   %ebx
   5:   83 ec 70                sub    $0x70,%esp
   8:   8d 5d 94                lea    -0x6c(%ebp),%ebx
   b:   b8 00 00 00 00          mov    $0x0,%eax
  10:   ba 19 00 00 00          mov    $0x19,%edx
  15:   89 df                   mov    %ebx,%edi
  17:   89 d1                   mov    %edx,%ecx
  19:   f3 ab                   rep stos %eax,%es:(%edi)
  1b:   c6 45 98 42             movb   $0x42,-0x68(%ebp)
  1f:   c6 45 99 4f             movb   $0x4f,-0x67(%ebp)
  23:   c6 45 9a 4d             movb   $0x4d,-0x66(%ebp)
  27:   c6 45 9b 42             movb   $0x42,-0x65(%ebp)
  2b:   c6 45 9d 83             movb   $0x83,-0x63(%ebp)
  2f:   c6 45 9e c4             movb   $0xc4,-0x62(%ebp)
  33:   c6 45 9f 80             movb   $0x80,-0x61(%ebp)
  37:   c6 45 a0 8b             movb   $0x8b,-0x60(%ebp)
  3b:   c6 45 a1 44             movb   $0x44,-0x5f(%ebp)
  3f:   c6 45 a2 24             movb   $0x24,-0x5e(%ebp)
  43:   c6 45 a3 0c             movb   $0xc,-0x5d(%ebp)
  47:   c6 45 a4 ff             movb   $0xff,-0x5c(%ebp)
  4b:   c6 45 a5 f0             movb   $0xf0,-0x5b(%ebp)
  4f:   c6 45 a6 55             movb   $0x55,-0x5a(%ebp)
  53:   c6 45 a7 89             movb   $0x89,-0x59(%ebp)
  57:   c6 45 a8 e5             movb   $0xe5,-0x58(%ebp)
  5b:   c6 45 a9 83             movb   $0x83,-0x57(%ebp)
  5f:   c6 45 aa ec             movb   $0xec,-0x56(%ebp)
  63:   c6 45 ab 18             movb   $0x18,-0x55(%ebp)
  67:   c6 45 ac c7             movb   $0xc7,-0x54(%ebp)
  6b:   c6 45 ad 04             movb   $0x4,-0x53(%ebp)
  6f:   c6 45 ae 24             movb   $0x24,-0x52(%ebp)
  73:   c6 45 b3 e8             movb   $0xe8,-0x4d(%ebp)
  77:   c6 45 b8 c9             movb   $0xc9,-0x48(%ebp)
  7b:   c6 45 b9 c3             movb   $0xc3,-0x47(%ebp)
  7f:   8d 45 94                lea    -0x6c(%ebp),%eax
  82:   83 c0 1b                add    $0x1b,%eax
  85:   8d 55 94                lea    -0x6c(%ebp),%edx
  88:   83 c2 04                add    $0x4,%edx
  8b:   89 10                   mov    %edx,(%eax)
  8d:   8d 45 94                lea    -0x6c(%ebp),%eax
  90:   83 c0 20                add    $0x20,%eax
  93:   ba 00 00 00 00          mov    $0x0,%edx
  98:   8d 4d 94                lea    -0x6c(%ebp),%ecx
  9b:   83 c1 24                add    $0x24,%ecx
  9e:   29 ca                   sub    %ecx,%edx
  a0:   89 10                   mov    %edx,(%eax)
  a2:   8d 45 94                lea    -0x6c(%ebp),%eax
  a5:   8d 55 94                lea    -0x6c(%ebp),%edx
  a8:   83 c2 70                add    $0x70,%edx
  ab:   8b 12                   mov    (%edx),%edx
  ad:   89 10                   mov    %edx,(%eax)
  af:   8d 45 94                lea    -0x6c(%ebp),%eax
  b2:   83 c0 70                add    $0x70,%eax
  b5:   8d 55 94                lea    -0x6c(%ebp),%edx
  b8:   83 c2 09                add    $0x9,%edx
  bb:   89 10                   mov    %edx,(%eax)
  bd:   83 c4 70                add    $0x70,%esp
  c0:   5b                      pop    %ebx
  c1:   5f                      pop    %edi
  c2:   5d                      pop    %ebp
  c3:   c3                      ret

000000c4 <_main>:
  c4:   55                      push   %ebp
  c5:   89 e5                   mov    %esp,%ebp
  c7:   83 e4 f0                and    $0xfffffff0,%esp
  ca:   83 ec 10                sub    $0x10,%esp
  cd:   e8 00 00 00 00          call   d2 <_main+0xe>
  d2:   e8 29 ff ff ff          call   0 <_hit>
  d7:   c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp)
  de:   00
  df:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  e6:   e8 00 00 00 00          call   eb <_main+0x27>
  eb:   c9                      leave
  ec:   c3                      ret
  ed:   90                      nop
  ee:   90                      nop
  ef:   90                      nop

hp@hp-PC ~/c
$

hit函数什么也没做,但是buff的缓冲区溢出了,在sizeof(buff) + 12的位置上写入了一个地址。然而这个位置却恰好是,main函数中hit();调用后面的一条指令地址,在汇编语句上就是  d7:   c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp)  这条指令的地址。

代码进入hit函数后的栈帧如下:

	|
	|
	| 返回地址 |
	|   %ebp   |          <- %ebp
	|   %edi   |
	|   %ebx   |
	|
	|   ...
	|   
	| 0xc3,                  ret
	| c9                     leave
	| e8 0 0 0 0           	 call   <_puts>
	| c7 04 24 0 0 0 0     	 movl   $0,(%esp)
	| 83 ec 18               sub    $0x18,%esp
	| 89 e5                  mov    %esp,%ebp
	| 55                     push   %ebp
	| ff f0                  push   %eax
	| 8b 44 24 0c            mov    0x0c(%esp),%eax
	| 83 c4 80               add    $0xffffff80,%esp 
	| B  O  M  B \0
	| 返回地址 |         <-  buff  将原始的返回地址存储在buff中
	|
	|          |        <-  %esp

main函数的call 0<_hit> 指令调用后,就进入_hit函数,首先将 %ebp  %edi  %ebx 顺序压入堆栈,然后将 %esp指针进去0x70,在栈上为buff数组分配空间。buff数组中填入了危险的数据,这些数据是病毒指令,注意栈图上列出来的指令是倒置的。

当hit函数的ret指令开始运行时,栈帧如下:

	|          | 
	|    ...
	|
	| 病毒地址 | 
	|   %ebp   |          
	|   %edi   |
	|   %ebx   |
	|
	|   ...
	|   
	| 0xc3,                  ret
	| c9                     leave
	| e8 0 0 0 0           	 call   401278 <_puts>
	| c7 04 24 0 0 0 0     	 movl   $0,(%esp)        <- %eip
	| 83 ec 18               sub    $0x18,%esp
	| 89 e5                  mov    %esp,%ebp
	| 55                     push   %ebp
	| ff f0                  push   %eax
	| 8b 44 24 0c            mov    0x0c(%esp),%eax
	| 83 c4 80               add    $0xffffff80,%esp 
	| B  O  M  B \0
	| 返回地址 |              将原始的返回地址存储在buff中
	|
	|
将原始的返回地址存储在buff中将原始的返回地址存储在buff中
虽然hit函数的栈帧被释放了,为buff分配的栈空间已经收回去了,%esp的指针已经指向了返回地址处,但栈上存储的病毒数据并未被清空。接着ret指令执行完成后,就要跳转到第一个病毒代码运行,病毒代码是在栈上运行的。病毒地址就指向了 buff+9的地址处,这就是一条 add    $0xffffff80,%esp 指令。
ret运行完,并执行完病毒的6条指令后的栈空间如下:
 
	|          | 
	|    ...
	|
	| 病毒地址 | 
	|   %ebp   |          
	|   %edi   |
	|   %ebx   |
	|
	|   ...
	|   
	| 0xc3,                  ret
	| c9                     leave
	| e8 0 0 0 0           	 call   401278 <_puts>
	| c7 04 24 0 0 0 0     	 movl   $0,(%esp)        <- %eip
	| 83 ec 18               sub    $0x18,%esp
	| 89 e5                  mov    %esp,%ebp
	| 55                     push   %ebp
	| ff f0                  push   %eax
	| 8b 44 24 0c            mov    0x0c(%esp),%eax
	| 83 c4 80               add    $0xffffff80,%esp     
	| B  O  M  B \0
	| 返回地址 |             将原始的返回地址存储在buff中
	|
	| 返回地址 |             把返回地址再搬回到栈上
	|   %ebp   |         <- %ebp
	|          |
	|    ..    |
	|          |         <- %esp 

在栈上的病毒指令调用了puts(BOMB)函数后,就又返回到原先的地址开始运行,神不知鬼不觉的运行了一个函数。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值