前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《coredump问题原理探究》Linux x86版6.3节有成员变量的类coredump例子

《coredump问题原理探究》Linux x86版6.3节有成员变量的类coredump例子

作者头像
血狼debugeeker
发布2018-09-20 14:46:16
1.6K0
发布2018-09-20 14:46:16
举报
文章被收录于专栏:debugeeker的专栏

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

在探究完类成员变量分布后,来定位一个coredump例子来实践一把:

代码语言:javascript
复制
(gdb) bt
#0  0x0804863c in xuzhina_dump_c06_s2_ex::print() ()
#1  0x08048713 in main ()

看一下xuzhina_dump_c06_s2_ex::print的汇编:

代码语言:javascript
复制
(gdb) disassemble 0x0804863c                   
Dump of assembler code for function _ZN22xuzhina_dump_c06_s2_ex5printEv:
   0x08048610 <+0>:     push   %ebp
   0x08048611 <+1>:     mov    %esp,%ebp
   0x08048613 <+3>:     sub    $0x28,%esp
   0x08048616 <+6>:     movl   $0x0,-0xc(%ebp)
   0x0804861d <+13>:    jmp 0x804869b <_ZN22xuzhina_dump_c06_s2_ex5printEv+139>
   0x0804861f <+15>:    mov    0x8(%ebp),%eax
   0x08048622 <+18>:    movzwl (%eax),%eax
   0x08048625 <+21>:    cwtl   
   0x08048626 <+22>:    test   %eax,%eax
   0x08048628 <+24>:    je     0x8048631 <_ZN22xuzhina_dump_c06_s2_ex5printEv+33>
   0x0804862a <+26>:    cmp    $0x1,%eax
   0x0804862d <+29>:    je     0x8048654 <_ZN22xuzhina_dump_c06_s2_ex5printEv+68>
   0x0804862f <+31>:    jmp 0x8048676 <_ZN22xuzhina_dump_c06_s2_ex5printEv+102>
   0x08048631 <+33>:    mov    0x8(%ebp),%eax
   0x08048634 <+36>:    mov    0x14(%eax),%edx
   0x08048637 <+39>:    mov    -0xc(%ebp),%eax
   0x0804863a <+42>:    add    %edx,%eax
=> 0x0804863c <+44>:    movzbl (%eax),%eax
   0x0804863f <+47>:    movsbl %al,%eax
   0x08048642 <+50>:    mov    %eax,0x4(%esp)
   0x08048646 <+54>:    movl   $0x80487c4,(%esp)
   0x0804864d <+61>:    call   0x80484a0 <printf@plt>
   0x08048652 <+66>:    jmp 0x8048697 <_ZN22xuzhina_dump_c06_s2_ex5printEv+135>
   0x08048654 <+68>:    mov    0x8(%ebp),%eax
   0x08048657 <+71>:    mov    0x14(%eax),%eax
   0x0804865a <+74>:    mov    -0xc(%ebp),%edx
   0x0804865d <+77>:    shl    $0x2,%edx
   0x08048660 <+80>:    add    %edx,%eax
   0x08048662 <+82>:    flds   (%eax)
   0x08048664 <+84>:    fstpl  0x4(%esp)
   0x08048668 <+88>:    movl   $0x80487c8,(%esp)
   0x0804866f <+95>:    call   0x80484a0 <printf@plt>
   0x08048674 <+100>:   jmp 0x8048697 <_ZN22xuzhina_dump_c06_s2_ex5printEv+135>
   0x08048676 <+102>:   mov    0x8(%ebp),%eax
   0x08048679 <+105>:   mov    0x14(%eax),%eax
   0x0804867c <+108>:   mov    -0xc(%ebp),%edx
   0x0804867f <+111>:   shl    $0x2,%edx
   0x08048682 <+114>:   add    %edx,%eax
   0x08048684 <+116>:   mov    (%eax),%eax
   0x08048686 <+118>:   mov    %eax,0x4(%esp)
   0x0804868a <+122>:   movl   $0x80487cc,(%esp)
   0x08048691 <+129>:   call   0x80484a0 <printf@plt>
   0x08048696 <+134>:   nop
   0x08048697 <+135>:   addl   $0x1,-0xc(%ebp)
   0x0804869b <+139>:   mov    0x8(%ebp),%eax
   0x0804869e <+142>:   mov    0x18(%eax),%eax
   0x080486a1 <+145>:   cmp    -0xc(%ebp),%eax
   0x080486a4 <+148>:   seta   %al
   0x080486a7 <+151>:   test   %al,%al
   0x080486a9 <+153>:   jne    0x804861f <_ZN22xuzhina_dump_c06_s2_ex5printEv+15>
   0x080486af <+159>:   leave  
   0x080486b0 <+160>:   ret    
End of assembler dump.

由于从符号表可以看到xuzhina_dump_c06_s2_ex::print是类xuzhina_dump_c06_s2_ex的成员函数,那么由调用类成员函数时,this指针作为第一个参数传递的规则,知道ebp+8存放着this指针。

再由崩溃指令地址是0x0804863c,可以把这个指令地址附近的汇编分析如下:

代码语言:javascript
复制
   0x0804862f <+31>:    jmp 0x8048676 <_ZN22xuzhina_dump_c06_s2_ex5printEv+102>
   0x08048631 <+33>:    mov    0x8(%ebp),%eax			//this指针
   0x08048634 <+36>:    mov    0x14(%eax),%edx			//偏移this指针20个字节的成员变量
   0x08048637 <+39>:    mov    -0xc(%ebp),%eax			//某个局部变量
   0x0804863a <+42>:    add    %edx,%eax				//该成员变量为数组
=> 0x0804863c <+44>:    movzbl (%eax),%eax				//该成员变量是char数组

而0x0804862f是无条件跳转,也就是说,0x08048631这条指令应该由其它地方跳转过来的。

代码语言:javascript
复制
   0x08048628 <+24>:    je     0x8048631 <_ZN22xuzhina_dump_c06_s2_ex5printEv+33>

查看一下0x00401073附近的汇编,可分析如下

代码语言:javascript
复制
   0x0804861f <+15>:    mov    0x8(%ebp),%eax		//this指针
   0x08048622 <+18>:    movzwl (%eax),%eax			//第一个成员变量,类型是short
   0x08048625 <+21>:    cwtl   
   0x08048626 <+22>:    test   %eax,%eax				//第一个成员变量是否为0
   0x08048628 <+24>:    je     0x8048631 <_ZN22xuzhina_dump_c06_s2_ex5printEv+33>
   0x0804862a <+26>:    cmp    $0x1,%eax				//第一个成员变量是否为1
   0x0804862d <+29>:    je     0x8048654 <_ZN22xuzhina_dump_c06_s2_ex5printEv+68>
   0x0804862f <+31>:    jmp 0x8048676 <_ZN22xuzhina_dump_c06_s2_ex5printEv+102>

由此可见,当第一个成员变量(命名为flag)为0时,才会执行崩溃指令所在的那个分支。也就是说,这一段汇编的结构如下

代码语言:javascript
复制
if ( flag == 0 )
{
…
}
else if ( flag == 1 )
{
….
}
else
{
…
}

现在尝试着找出ebp-0xc这个局部变量是处于什么作用。

代码语言:javascript
复制
   0x08048616 <+6>:     movl   $0x0,-0xc(%ebp)
   0x0804861d <+13>:    jmp  x804869b <_ZN22xuzhina_dump_c06_s2_ex5printEv+139>
   0x0804861f <+15>:    mov    0x8(%ebp),%eax

   0x08048697 <+135>:   addl   $0x1,-0xc(%ebp)
   0x0804869b <+139>:   mov    0x8(%ebp),%eax
   0x0804869e <+142>:   mov    0x18(%eax),%eax
   0x080486a1 <+145>:   cmp    -0xc(%ebp),%eax
   0x080486a4 <+148>:   seta   %al
   0x080486a7 <+151>:   test   %al,%al
   0x080486a9 <+153>:   jne    0x804861f <_ZN22xuzhina_dump_c06_s2_ex5printEv+15>

由上面一段,可以看出,ebp-0xc存放着是一个计数变量,也就是一个索引,且处于一个指令地址由0x804861f-0x080486a9范围组成的循环。那么

代码语言:javascript
复制
   0x08048631 <+33>:    mov    0x8(%ebp),%eax			//this指针
   0x08048634 <+36>:    mov    0x14(%eax),%edx			//偏移this指针20个字节的成员变量
   0x08048637 <+39>:    mov    -0xc(%ebp),%eax			//某个局部变量
   0x0804863a <+42>:    add    %edx,%eax				//该成员变量为数组
=> 0x0804863c <+44>:    movzbl (%eax),%eax				//该成员变量是char数组

可以看出eax+14h即this指针偏移20个字节的成员变量是char指针,命名为ptr。由

代码语言:javascript
复制
   0x0804869b <+139>:   mov    0x8(%ebp),%eax
   0x0804869e <+142>:   mov    0x18(%eax),%eax
   0x080486a1 <+145>:   cmp    -0xc(%ebp),%eax
   0x080486a4 <+148>:   seta   %al
   0x080486a7 <+151>:   test   %al,%al
   0x080486a9 <+153>:   jne    0x804861f <_ZN22xuzhina_dump_c06_s2_ex5printEv+15>

可推知this指针偏移0x18即24个字节的成员变量是一个最大整数,命名为num。

从上面可以知道,类xuzhina_dump_c06_s2_ex大概如下:

代码语言:javascript
复制
class xuzhina_dump_c06_s2_ex
{
private:
   short flag;   //第一个成员变量
   char unknown[n];  //未知数目成员变量,只以字节来算。n = 16或n=18
   char* ptr;        
   int num;         //指定上面ptr的元素个数
public:
   void print();
 ………….
};

代码语言:javascript
复制
(gdb) x /x $ebp+8
0xbfe09690:     0xbfe096b4
(gdb) x /x 0xbfe096b4+0x14
0xbfe096c8:     0x69766544
(gdb) x /x 0x69766544
0x69766544:     Cannot access memory at address 0x69766544

可知ptr这个类成员变量被修改为0x69766544,所以才会coredump。现在看一下这个对象的数值:

代码语言:javascript
复制
(gdb) x /8x 0xbfe096b4
0xbfe096b4:     0x68540000      0x73497369      0x726f5741      0x7546646c
0xbfe096c4:     0x664f6c6c      0x69766544      0x4f6f4e6c      0x6143656e

从这些数值来看,它们大多处于ASCII码的范围,就是0-127(0x7f)。试一下看是不是。

由于第一个成员变量flag占两个字节,那么查看的起始地址应该是0xbfe096b6.

代码语言:javascript
复制
(gdb) x /s 0xbfe096b6
0xbfe096b6:      "ThisIsAWorldFullOfDevilNoOneCanSurvie"

它的长度是37。也就是说,它把ptr,num的值都覆盖了。

那么它是从哪里来的,是在构造时就已经设置还是在别的地方。为了弄清楚这个,先看一下main函数的汇编:

代码语言:javascript
复制
(gdb) disassemble main
Dump of assembler code for function main:
   0x080486b1 <+0>:     push   %ebp
   0x080486b2 <+1>:     mov    %esp,%ebp
   0x080486b4 <+3>:     and    $0xfffffff0,%esp
   0x080486b7 <+6>:     sub    $0x40,%esp
   0x080486ba <+9>:     cmpl   $0x2,0x8(%ebp)
   0x080486be <+13>:    jg     0x80486c7 <main+22>
   0x080486c0 <+15>:    mov    $0xffffffff,%eax
   0x080486c5 <+20>:    jmp    0x8048718 <main+103>
   0x080486c7 <+22>:    mov    0xc(%ebp),%eax
   0x080486ca <+25>:    add    $0x8,%eax
   0x080486cd <+28>:    mov    (%eax),%eax
   0x080486cf <+30>:    mov    %eax,(%esp)
   0x080486d2 <+33>:    call   0x8048480 <strlen@plt>
   0x080486d7 <+38>:    mov    0xc(%ebp),%edx
   0x080486da <+41>:    add    $0x8,%edx
   0x080486dd <+44>:    mov    (%edx),%ecx
   0x080486df <+46>:    mov    0xc(%ebp),%edx
   0x080486e2 <+49>:    add    $0x4,%edx
   0x080486e5 <+52>:    mov    (%edx),%edx
   0x080486e7 <+54>:    mov    %eax,0x10(%esp)
   0x080486eb <+58>:    mov    %ecx,0xc(%esp)
   0x080486ef <+62>:    movl   $0x0,0x8(%esp)
   0x080486f7 <+70>:    mov    %edx,0x4(%esp)
   0x080486fb <+74>:    lea    0x24(%esp),%eax
   0x080486ff <+78>:    mov    %eax,(%esp)
   0x08048702 <+81>:    call   0x80485d0 <_ZN22xuzhina_dump_c06_s2_exC2EPcsPvj>
   0x08048707 <+86>:    lea    0x24(%esp),%eax
   0x0804870b <+90>:    mov    %eax,(%esp)
   0x0804870e <+93>:    call   0x8048610 <_ZN22xuzhina_dump_c06_s2_ex5printEv>
   0x08048713 <+98>:    mov    $0x0,%eax
   0x08048718 <+103>:   jmp    0x8048722 <main+113>
   0x0804871a <+105>:   mov    %eax,(%esp)
   0x0804871d <+108>:   call   0x80484c0 <_Unwind_Resume@plt>
   0x08048722 <+113>:   leave  
   0x08048723 <+114>:   ret    
End of assembler dump.

通过结合上面汇编和

代码语言:javascript
复制
(gdb) shell c++filt _ZN22xuzhina_dump_c06_s2_exC2EPcsPvj
xuzhina_dump_c06_s2_ex::xuzhina_dump_c06_s2_ex(char*, short, void*, unsigned int)
(gdb) shell c++filt _ZN22xuzhina_dump_c06_s2_ex5printEv 
xuzhina_dump_c06_s2_ex::print()

可知,main函数只是调用了四个函数,strlen,类xuzhina_dump_c06_s2_ex的构造函数和成员函数print,及_Unwind_Resume(说明可见http://refspecs.linuxfoundation.org/LSB_1.3.0/gLSB/gLSB/baselib--unwind-resume.html)

可见,main函数只是调用了类xuzhina_dump_c06_s2_ex两个对外接口,一个构造函数,一个print成员函数。那么,类xuzhina_dump_c06_s2_ex的成员变量ptr,num应该是在构造函数里初始化的。

由下面

代码语言:javascript
复制
   0x080486e7 <+54>:    mov    %eax,0x10(%esp)
   0x080486eb <+58>:    mov    %ecx,0xc(%esp)
   0x080486ef <+62>:    movl   $0x0,0x8(%esp)
   0x080486f7 <+70>:    mov    %edx,0x4(%esp)
   0x080486fb <+74>:    lea    0x24(%esp),%eax
   0x080486ff <+78>:    mov    %eax,(%esp)
   0x08048702 <+81>:    call   0x80485d0 <_ZN22xuzhina_dump_c06_s2_exC2EPcsPvj>

从四个push指令,可以猜测,类xuzhina_dump_c06_s2_ex构造函数有四个参数(五个参数减去默认的this指针这个参数)。

看一下构造函数:

代码语言:javascript
复制
(gdb) disassemble _ZN22xuzhina_dump_c06_s2_exC2EPcsPvj
Dump of assembler code for function _ZN22xuzhina_dump_c06_s2_exC2EPcsPvj:
   0x080485d0 <+0>:     push   %ebp
   0x080485d1 <+1>:     mov    %esp,%ebp
   0x080485d3 <+3>:     sub    $0x28,%esp
   0x080485d6 <+6>:     mov    0x10(%ebp),%eax
   0x080485d9 <+9>:     mov    %ax,-0xc(%ebp)
   0x080485dd <+13>:    mov    0x8(%ebp),%eax
   0x080485e0 <+16>:    mov    0x14(%ebp),%edx
   0x080485e3 <+19>:    mov    %edx,0x14(%eax)
   0x080485e6 <+22>:    mov    0x8(%ebp),%eax
   0x080485e9 <+25>:    mov    0x18(%ebp),%edx
   0x080485ec <+28>:    mov    %edx,0x18(%eax)
   0x080485ef <+31>:    mov    0x8(%ebp),%eax
   0x080485f2 <+34>:    lea    0x2(%eax),%edx
   0x080485f5 <+37>:    mov    0xc(%ebp),%eax
   0x080485f8 <+40>:    mov    %eax,0x4(%esp)
   0x080485fc <+44>:    mov    %edx,(%esp)
   0x080485ff <+47>:    call   0x8048490 <strcpy@plt>
   0x08048604 <+52>:    mov    0x8(%ebp),%eax
   0x08048607 <+55>:    movzwl -0xc(%ebp),%edx
   0x0804860b <+59>:    mov    %dx,(%eax)
   0x0804860e <+62>:    leave  
   0x0804860f <+63>:    ret    
End of assembler dump.

代码语言:javascript
复制
   0x080485d6 <+6>:     mov    0x10(%ebp),%eax

   0x080485dd <+13>:    mov    0x8(%ebp),%eax

   0x080485e0 <+16>:    mov    0x14(%ebp),%edx

   0x080485e6 <+22>:    mov    0x8(%ebp),%eax

   0x080485f5 <+37>:    mov    0xc(%ebp),%eax

这五条指令来看,类xuzhina_dump_c06_s2_ex构造函数的参数确实是四个。

再由ebp+8放着this指针和下面四段代码

代码语言:javascript
复制
   0x080485dd <+13>:    mov    0x8(%ebp),%eax
   0x080485e0 <+16>:    mov    0x14(%ebp),%edx
   0x080485e3 <+19>:    mov    %edx,0x14(%eax)

   0x080485e6 <+22>:    mov    0x8(%ebp),%eax
   0x080485e9 <+25>:    mov    0x18(%ebp),%edx
   0x080485ec <+28>:    mov    %edx,0x18(%eax)

   0x080485ef <+31>:    mov    0x8(%ebp),%eax
   0x080485f2 <+34>:    lea    0x2(%eax),%edx
   0x080485f5 <+37>:    mov    0xc(%ebp),%eax

   0x08048604 <+52>:    mov    0x8(%ebp),%eax
   0x08048607 <+55>:    movzwl -0xc(%ebp),%edx
   0x0804860b <+59>:    mov    %dx,(%eax)

可知,类xuzhina_dump_c06_s2_ex只有四个成员变量。且由

代码语言:javascript
复制
   0x080485ef <+31>:    mov    0x8(%ebp),%eax
   0x080485f2 <+34>:    lea    0x2(%eax),%edx		//只是偏移2个字节
   0x080485f5 <+37>:    mov    0xc(%ebp),%eax
   0x080485f8 <+40>:    mov    %eax,0x4(%esp)
   0x080485fc <+44>:    mov    %edx,(%esp)
   0x080485ff <+47>:    call   0x8048490 <strcpy@plt>

可知,第二个成员变量是char数组,大小是18的字节。也就是说,这个类的定义大概如下:

代码语言:javascript
复制
class xuzhina_dump_c06_s2_ex
{
private:
   short flag;   //第一个成员变量
   char str[18];  //暂命名为str
   char* ptr;     
   int num;         //指定ptr的元素个数
public:
   void print();
 ………….
};

由于ptr(偏移值在0x14h)这个成员变量是在str后面,且

代码语言:javascript
复制
   0x080485ef <+31>:    mov    0x8(%ebp),%eax
   0x080485f2 <+34>:    lea    0x2(%eax),%edx
   0x080485f5 <+37>:    mov    0xc(%ebp),%eax
   0x080485f8 <+40>:    mov    %eax,0x4(%esp)
   0x080485fc <+44>:    mov    %edx,(%esp)
   0x080485ff <+47>:    call   0x8048490 <strcpy@plt>

所以,ptr的值有可能是在strcpy时被覆盖的。由strcpy的原型

char *strcpy(char *dest, const char *src);

  0x080485f5 <+37>:   mov    0xc(%ebp),%eax

  0x080485f8 <+40>:    mov    %eax,0x4(%esp)

可知,src是类xuzhina_dump_c06_s2_ex构造函数的第一个参数(不是指默认this指针)。那么跳到main函数桢看一下,传递给构造函数的第一个参数是从哪里来的。

由main函数这一段汇编

代码语言:javascript
复制
   0x080486df <+46>:    mov    0xc(%ebp),%edx	//第二个参数argv
   0x080486e2 <+49>:    add    $0x4,%edx			// argv[1]的地址
   0x080486e5 <+52>:    mov    (%edx),%edx
   0x080486e7 <+54>:    mov    %eax,0x10(%esp)
   0x080486eb <+58>:    mov    %ecx,0xc(%esp)
   0x080486ef <+62>:    movl   $0x0,0x8(%esp)
   0x080486f7 <+70>:    mov    %edx,0x4(%esp)
   0x080486fb <+74>:    lea    0x24(%esp),%eax
   0x080486ff <+78>:    mov    %eax,(%esp)
   0x08048702 <+81>:    call   0x80485d0 <_ZN22xuzhina_dump_c06_s2_exC2EPcsPvj>

可知,类xuzhina_dump_c06_s2_ex构造函数的第一个参数是由main函数的第二个参数argv1传入的。看一下argv1是多少

代码语言:javascript
复制
(gdb) x $ebp+0xc
0xbfe096e4:     0xbfe09774
(gdb) x 0xbfe09774+4
0xbfe09778:     0xbfe0a6a3
(gdb) x /s 0xbfe0a6a3
0xbfe0a6a3:      "ThisIsAWorldFullOfDevilNoOneCanSurvie"

和上面分析出来的字符串正好是一样的。也就是说,由于不正确使用了strcpy,导致改写了ptr,导致了coredump。

例子的源代码:

代码语言:javascript
复制
  1	 #include <string.h>
  2	 #include <stdio.h>
  3	 class xuzhina_dump_c06_s2_ex
  4	 {
  5	     private:
  6	         short m_type;
  7	         char m_name[16];
  8	         void* m_ptr;
  9	         unsigned int m_len;
 10	     public:
 11	         xuzhina_dump_c06_s2_ex( char* name, short type,
 12	                 void* data, unsigned int len );
 13	         void print();
 14	 };
 15 
 16	 xuzhina_dump_c06_s2_ex::xuzhina_dump_c06_s2_ex( char* name, short type,
 17	         void* data, unsigned int len )
 18	 {
 19	     m_ptr = data;
 20	     m_len = len;
 21	     strcpy( m_name, name );
 22	     m_type = type;
 23	 
 24	 }
 25 
 26	 void xuzhina_dump_c06_s2_ex::print()
 27	 {
 28	     for ( unsigned int i = 0; i < m_len; i++ )
 29	     {
 30	         switch( m_type )
 31	         {
 32	             case 0:
 33	                 printf( "%c ", *((char*)m_ptr + i ) );
 34	                 break;
 35	             case 1:
 36	                 printf( "%f ", *((float*)m_ptr + i ) );
 37	                 break;
 38	             default:
 39	                 printf( "%d ", *((int*)m_ptr + i ) );
 40	                 break;
 41	         }
 42	     }
43	 }
 44	 
 45	 int main(int argc, char* argv[] )
 46	 {
 47	     if ( argc < 3 )
 48	     {
 49	         return -1;
 50	     }
 51	     xuzhina_dump_c06_s2_ex test( argv[1], 0,
 52	             argv[2], strlen( argv[2] ) );
 53	     test.print();
 54 
 55	     return 0;
 56	 }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015年01月13日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档