《GDB —— 数组越界导致内存被破坏》

1.例程

test.c
#include <string.h> char name[] = "book cat dog building vegetable curry"; void func(void) { char buf[5]; strcpy(buf, names); } int main(void) { func(); return 0; }

  编译程序

gcc -fno-stack-protector -g test.c

  其中 -fno-stack-protector禁止用栈保护。如果开启栈保护,会使用canary机制。

  不指定默认百度说gcc是禁止用栈保护,但是我自己编译出来是使能栈保护。

运行程序:

Segmentation fault (core dumped)

  如果使能栈保护的话,运行后的结果是:

*** stack smashing detected ***: ./a.out terminated
Aborted (core dumped)

  具体可以查看canary机制。我们首先用禁止栈保护来调试程序。

 

2.gdb调试

(gdb) r
Starting program: /home/zhuang/test/a.out1 

Program received signal SIGSEGV, Segmentation fault.
0x676e6964 in ?? ()
(gdb) bt 
#0  0x676e6964 in ?? ()
#1  0x67657620 in ?? ()
#2  0x62617465 in ?? ()
#3  0x6320656c in ?? ()
#4  0x79727275 in ?? ()
#5  0x00000000 in ?? ()

  首先可以看到,当程序停止后,bt看栈的时候,都是??。

  这种情况可能是代码跳转到或者调用了错误的地址0x676e6964。

(gdb) x/i 0x676e6964
=> 0x676e6964:    Cannot access memory at address 0x676e6964

  可以看到无法访问该地址。

 

运行地址的改变

  那么是从哪里跳转到或调用了不存在的地址?

1.直接指定地址并调用(函数调用)。

2.指定一块内存区域,其中保存了跳转地址(GOT/PLT调用函数的原理)

3.执行ret命令,用于函数结束时返回调用者函数。ret命令将栈指针指向的位置的值作为跳转(返回)地址使用。

 

分析:

1.函数调用的地址比较难破坏,一般都保存在只读空间。所以一般尝试写操作就会产生segmentation fault。此时,core文件会记录PC的值,比较好分析。

2.上述的第二种和第三种使用的地址保存在GOT或栈中,即使数据被破坏,也不会保存。等到访问已经被破坏的地址,才会报错。

 

解决思路:

  确定破坏地址值的位置。就是将错误地址当作数据,寻找是在哪里将这个数据写到这个地方的。所以我们寻找写入0x676e6964这个数据的地方。

(gdb) x/c $esp
0xbffff050:    32 ' '

(38条消息) EAX、ESP、EBP等寄存器的作用_桂花鱼_的博客-CSDN博客_esp寄存器

  当前的esp的地址是0xbffff050,可以看到是个空格。

(gdb) x/30c $esp-15
0xbffff041:    97 'a'    116 't'    32 ' '    100 'd'    111 'o'    103 'g'    32 ' '    98 'b'
0xbffff049:    117 'u'    105 'i'    108 'l'    100 'd'    105 'i'    110 'n'    103 'g'    32 ' '
0xbffff051:    118 'v'    101 'e'    103 'g'    101 'e'    116 't'    97 'a'    98 'b'    108 'l'
0xbffff059:    101 'e'    32 ' '    99 'c'    117 'u'    114 'r'    114 'r'

  $esp-15的意思就是0xbffff050-15(15为十进制),也就是0xbffff041。然后30c表示读取从0xbffff041开始的30个字符。

  可以看出来,esp的前后都是字符串。因此考虑是不是字符串复制导致的越界。

(gdb) p (char *)$esp
$1 = 0xbffff050 " vegetable curry"
(gdb) p (char *)$esp-20
$2 = 0xbffff03c "ook cat dog building vegetable curry"
(gdb) p (char *)$esp-25
$3 = 0xbffff037 "\277\374\360\377book cat dog building vegetable curry"
(gdb) p (char *)$esp-21
$4 = 0xbffff03b "book cat dog building vegetable curry"

  可以看到$esp-21开始是一个完整字符串。

   因为调查的重点是寻找写入0x676e6964这个数据的地方。可以发现这个数据的ASCII是gnid。考虑到i386架构采用小端模式,所以数据应该是ding。

  所以确定是字符串复制的时候越界导致的栈返回地址被改写。

 

  考虑一下确认栈的返回地址位置。watch查看

 

 

 

 

 

 

 

 

 

 

__stack_chk_fail栈检查失败 - smartch - 博客园 (cnblogs.com)

(38条消息) 【GDB】__stack_chk_fail 栈溢出问题定位_pcj_888的博客-CSDN博客_如何定位栈溢出

(38条消息) C语言函数栈帧详解_CDQ0818的博客-CSDN博客_c语言栈帧

记录一次完整的Canary保护 - eur1ka - 博客园 (cnblogs.com)

 

一:canary(栈保护)

  栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来让shellcode能够得到执行。当启用栈保护后,函数开始执行的时候会先往栈里插入cookie信息,当函数真正返回的时候会验证cookie信息是否合法,如果不合法就停止程序运行。攻击者在覆盖返回地址的时候往往也会将cookie信息给覆盖掉,导致栈保护检查失败而阻止shellcode的执行。在Linux中我们将cookie信息称为canary。

gcc在4.2版本中添加了-fstack-protector和-fstack-protector-all编译参数以支持栈保护功能,

因此在编译时可以控制是否开启栈保护以及程度,例如:

1、gcc -o test test.c  // 默认情况下,不开启Canary保护

2、gcc -fno-stack-protector -o test test.c //禁用栈保护

3、gcc -fstack-protector -o test test.c //启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码

4、gcc -fstack-protector-all -o test test.c //启用堆栈保护,为所有函数插入保护代码

posted @ 2022-11-10 12:15  一个不知道干嘛的小萌新  阅读(718)  评论(0编辑  收藏  举报