首页
学习
活动
专区
圈层
工具
发布
21 篇文章
1
《coredump问题原理探究》Linux x86版6.3节有成员变量的类coredump例子
2
《coredump问题原理探究》Linux x86版6.5节虚函数的coredump例子
3
《coredump问题原理探究》Linux x86版6.8节多继承coredump例子
4
《coredump问题原理探究》Linux x86版7.2节vector coredump例子
5
《coredump问题原理探究》Linux x86版7.4节List coredump例子
6
《coredump问题原理探究》Linux x86版7.6节 Map coredump例子
7
《coredump问题原理探究》Linux x86版5.9节C风格数据结构内存布局之联合体
8
《coredump问题原理探究》Linux x86版6.1节C++风格数据结构内存布局之无成员变量的类
9
《coredump问题原理探究》Linux x86版6.2节C++风格数据结构内存布局之有成员变量的类
10
《coredump问题原理探究》Linux x86版4.5节函数的逆向之coredump例子
11
《coredump问题原理探究》Linux x86版5.1节C风格数据结构内存布局之引言
12
《coredump问题原理探究》Linux x86版5.2节C风格数据结构内存布局之基本数据类型
13
《coredump问题原理探究》Linux x86版5.3节C风格数据结构内存布局之数组
14
《coredump问题原理探究》Linux x86版5.4节C风格数据结构内存布局之数组coredump例子
15
《coredump问题原理探究》Linux x86版5.5节C风格数据结构内存布局之基本数据类型构成的结构体
16
《coredump问题原理探究》Linux x86版5.6节C风格数据结构内存布局之复合类型构成的结构体
17
《coredump问题原理探究》Linux x86版5.7节C风格数据结构内存布局之结构体数组
18
《coredump问题原理探究》Linux x86版5.8节C风格数据结构内存布局之结构体数组结构体coredump
19
《coredump问题原理探究》Linux x86版3.5节栈布局之-fomit-frame-pointer编译选项
20
《coredump问题原理探究》Linux x86版3.6节栈布局之gcc内嵌关键字
21
《coredump问题原理探究》Linux x86版3.8节栈布局之栈溢出coredump例子

《coredump问题原理探究》Linux x86版3.8节栈布局之栈溢出coredump例子

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1344452

现在,回到前言的栈,看一下能不能用上面的规律来恢复它的栈。前言的可执行文件是没有使用-fomit-frame-pointer编译选项的。

前言的栈是这样的:

代码语言:javascript
复制
(gdb) bt
#0  0x6f745374 in ?? ()
#1  0x57735571 in ?? ()
#2  0xbff80065 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)

先看一下ebp,esp的值:

代码语言:javascript
复制
(gdb) i r ebp esp
ebp            0x6f4e6e61       0x6f4e6e61
esp            0xbff8ef60       0xbff8ef60

毫无疑问,ebp的值是非法的。从前几节的规律可以知道,ebp >= esp。

那么,只能看一下esp所指向的栈内容,来查找函数桢指针fp。在找fp之前,先复习一下栈布局的单链表规律:

1.      fp所在地址大于esp的值

2.      fp下一个单元的内容可以用info symbol来显示出函数名称

3.      fp所指向单元也遵守1,2两个原则。

看一下esp所指向的内容:

代码语言:javascript
复制
(gdb) x /16x $esp
0xbff8ef60:     0x57735571      0xbff80065      0xb778d590      0x43647dc3
0xbff8ef70:     0xbff8ef90      0x4361d3e0      0xbff8ef98      0x08048618
0xbff8ef80:     0x00000003      0xbff8f66b      0xffffffff      0x43647dc3
0xbff8ef90:     0x43622960      0xb778dbb8      0xbff8efb8      0x08048636

由于esp的值是0xbff8ef60,里面有0xbff8ef90,0xbff8ef98,0xbff8f66b和0xbff8efb8都满足第1原则。看一下它们下一个单元满足第2原则不。

代码语言:javascript
复制
(gdb) info symbol 0x4361d3e0
No symbol matches 0x4361d3e0.
(gdb) info symbol 0x08048618
wrapper2(int, char*) + 28 in section .text of /home/buckxu/work/9/1/xuzhina_dump_c1
(gdb) info symbol 0xffffffff
No symbol matches 0xffffffff.
(gdb) info symbol 0x08048636
wrapper3(int, char*) + 28 in section .text of /home/buckxu/work/9/1/xuzhina_dump_c1

可见,0xbff8ef98,0xbff8efb8满足第1,2原则。看一下0xbff8ef98是否满足第3原则不。

代码语言:javascript
复制
(gdb) x /2x 0xbff8ef98
0xbff8ef98:     0xbff8efb8      0x08048636

真是巧,0xbff8ef98指向单元的内容刚好是0xbff8efb8.也就是说,0xbff8ef98满足第3原则。

接着看0xbff8efb8是不是也满足这3个原则

代码语言:javascript
复制
(gdb) x /2x 0xbff8efb8
0xbff8efb8:     0xbff8efd8      0x08048666
(gdb) info symbol 0x08048666
main + 46 in section .text of /home/buckxu/work/9/1/xuzhina_dump_c1

可见0xbff8efb8也是满足了3个原则。

由于程序都是由main函数开始执行,可以看到,实际上已经把真实的栈还原了部分:

代码语言:javascript
复制
wrapper2(int, char*) + 28 in section .text of /home/buckxu/work/9/1/xuzhina_dump_c1
wrapper3(int, char*) + 28 in section .text of /home/buckxu/work/9/1/xuzhina_dump_c1
main + 46 in section .text of /home/buckxu/work/9/1/xuzhina_dump_c1

那么,其余的栈应该怎么还原呢?由于现在还原的栈中,wrapper2是栈顶,那么看一下wrapper2的汇编:

代码语言:javascript
复制
(gdb) disassemble wrapper2  
Dump of assembler code for function _Z8wrapper2iPc:
   0x080485fc <+0>:     push   %ebp
   0x080485fd <+1>:     mov    %esp,%ebp
   0x080485ff <+3>:     sub    $0x18,%esp
   0x08048602 <+6>:     addl   $0x1,0x8(%ebp)
   0x08048606 <+10>:    mov    0xc(%ebp),%eax
   0x08048609 <+13>:    mov    %eax,0x4(%esp)
   0x0804860d <+17>:    mov    0x8(%ebp),%eax
   0x08048610 <+20>:    mov    %eax,(%esp)
   0x08048613 <+23>:    call   0x80485de <_Z8wrapper1iPc>
   0x08048618 <+28>:    leave  
   0x08048619 <+29>:    ret    
End of assembler dump.

由于wrapper2存在栈中的返回地址是0x08048618,那么应该是由于

代码语言:javascript
复制
   0x08048613 <+23>:    call   0x80485de <_Z8wrapper1iPc>

引起的栈溢出。而_Z8wrapper1iPc则可以这样知道原型:

代码语言:javascript
复制
(gdb) shell c++filt _Z8wrapper1iPc
wrapper1(int, char*)

由于wrapper2申请了0x18个字节的局部变量空间,wrapper2返回地址0x08048618所在的单元是0xbff8ef7C,和esp的值0xbff8ef60相差0x1C,刚好是0x18局部变量地址区和1个返回地址的和。也就是说,栈刚好是在调用完wrapper1时,出现栈溢出的。

所以,栈应该如下:

代码语言:javascript
复制
wrapper1(int, char*)
wrapper2(int, char*) + 28 in section .text of /home/buckxu/work/9/1/xuzhina_dump_c1
wrapper3(int, char*) + 28 in section .text of /home/buckxu/work/9/1/xuzhina_dump_c1
main + 46 in section .text of /home/buckxu/work/9/1/xuzhina_dump_c1

如果只是还原栈,那么上面已经完成任务了,但溢出的根因是哪里呢?

而从wrapper1的汇编:

代码语言:javascript
复制
(gdb) disassemble wrapper1
Dump of assembler code for function _Z8wrapper1iPc:
   0x080485de <+0>:     push   %ebp
   0x080485df <+1>:     mov    %esp,%ebp
   0x080485e1 <+3>:     sub    $0x18,%esp
   0x080485e4 <+6>:     addl   $0x1,0x8(%ebp)
   0x080485e8 <+10>:    mov    0xc(%ebp),%eax
   0x080485eb <+13>:    mov    %eax,0x4(%esp)
   0x080485ef <+17>:    mov    0x8(%ebp),%eax
   0x080485f2 <+20>:    mov    %eax,(%esp)
   0x080485f5 <+23>:    call   0x80485a0 <_Z8overflowiPc>
   0x080485fa <+28>:    leave  
   0x080485fb <+29>:    ret    
End of assembler dump.

由于在wrapper1里,所有的内存地址操作都由ebp,esp来指定,而ebp是由esp来获取。所以wrapper1运行时,esp的值是应该是wrapper2减去0x18(wrapper1的局部变量空间大小)和返回地址,即0xbff8ef60 - 0x18 - 4 = 0xbff8ef44。

代码语言:javascript
复制
(gdb) x 0xbff8ef44
0xbff8ef44:     0x6d754865

可知,在wrapper1时,esp值所指向的地址是合法的。也就是说,在wrapper1里运行,是不会出现内存越界的。那么应该是由于

代码语言:javascript
复制
   0x080485f5 <+23>:    call   0x80485a0 <_Z8overflowiPc>

这一条指令引起的。

研究一下_Z8overflowiPc:

代码语言:javascript
复制
(gdb) shell c++filt _Z8overflowiPc
overflow(int, char*)
(gdb) disassemble overflow
Dump of assembler code for function _Z8overflowiPc:
   0x080485a0 <+0>:     push   %ebp
   0x080485a1 <+1>:     mov    %esp,%ebp
   0x080485a3 <+3>:     sub    $0x28,%esp
   0x080485a6 <+6>:     mov    0xc(%ebp),%eax
   0x080485a9 <+9>:     mov    %eax,0x4(%esp)
   0x080485ad <+13>:    lea    -0x18(%ebp),%eax
   0x080485b0 <+16>:    mov    %eax,(%esp)
   0x080485b3 <+19>:    call   0x8048460 <strcpy@plt>
   0x080485b8 <+24>:    lea    -0x18(%ebp),%eax
   0x080485bb <+27>:    mov    %eax,0x4(%esp)
   0x080485bf <+31>:    movl   $0x8048704,(%esp)
   0x080485c6 <+38>:    call   0x8048470 <printf@plt>
   0x080485cb <+43>:    addl   $0x1,0x8(%ebp)
   0x080485cf <+47>:    mov    0x8(%ebp),%eax
   0x080485d2 <+50>:    jmp    0x80485dc <_Z8overflowiPc+60>
   0x080485d4 <+52>:    mov    %eax,(%esp)
   0x080485d7 <+55>:    call   0x8048490 <_Unwind_Resume@plt>
   0x080485dc <+60>:    leave  
   0x080485dd <+61>:    ret    
End of assembler dump.

由于overflow调用strcpy,而strcpy是有名的不安全的函数,它有可能是这次coredump的根因。

到这里,可以贴一下这个程序的源码来看一下,上面的栈还原是不是准确:

代码语言:javascript
复制
#include <string.h>
#include <stdio.h>
int overflow( int level, char* str )
{
     char buff[16];
     strcpy( buff, str );
     printf( "buffer:%s", buff );
     return ++level;
}
 
int wrapper1( int level, char* str )
{
     return overflow( ++level, str );
}
 
int wrapper2( int level, char* str )
{
     return wrapper1( ++level, str );
}
 
int wrapper3( int level, char* str )
{
     return wrapper2( ++level, str );
}
 
int main( int argc, char* argv[] )
{
    if ( argc < 2 )
     {
         return -1;
     }
     return wrapper3( 0, argv[1] ); 
}

执行时的这样的:

代码语言:javascript
复制
[buckxu@xuzhina 1]$ ./xuzhina_dump_c1 WeAreHumanBeingsNothingCanNotStopUsWe                           
Segmentation fault (core dumped)
举报
领券