前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《coredump问题原理探究》Linux x86版5.8节C风格数据结构内存布局之结构体数组结构体coredump

《coredump问题原理探究》Linux x86版5.8节C风格数据结构内存布局之结构体数组结构体coredump

作者头像
血狼debugeeker
发布2018-09-20 14:37:55
3660
发布2018-09-20 14:37:55
举报
文章被收录于专栏:debugeeker的专栏debugeeker的专栏

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

上一节已经阐述完毕结构体的特征了,需要一个coredump例子:

代码语言:javascript
复制
(gdb) bt
#0  0x00000000 in ?? ()
#1  0x080485a1 in result(xuzhina_dump_c05_s3_ex*, int) ()
#2  0x080487f8 in main ()

从栈顶来看,顶层的地址竟然是0,eip的值也是0.会出现eip为0的情况,只可能是调用了函数指针,且函数指针为0.看一下result的汇编:

代码语言:javascript
复制
(gdb) disassemble result
Dump of assembler code for function _Z6resultP22xuzhina_dump_c05_s3_exi:
   0x08048540 <+0>:     push   %ebp
   0x08048541 <+1>:     mov    %esp,%ebp
   0x08048543 <+3>:     push   %ebx
   0x08048544 <+4>:     sub    $0x24,%esp
   0x08048547 <+7>:     movl   $0x0,-0xc(%ebp)
   0x0804854e <+14>:    movl   $0x0,-0x10(%ebp)
   0x08048555 <+21>:    jmp    0x80485a8 <_Z6resultP22xuzhina_dump_c05_s3_exi+104>
   0x08048557 <+23>:    mov    -0x10(%ebp),%edx
   0x0804855a <+26>:    mov    %edx,%eax
   0x0804855c <+28>:    add    %eax,%eax
   0x0804855e <+30>:    add    %edx,%eax
   0x08048560 <+32>:    shl    $0x2,%eax
   0x08048563 <+35>:    mov    %eax,%edx
   0x08048565 <+37>:    mov    0x8(%ebp),%eax
   0x08048568 <+40>:    add    %edx,%eax
   0x0804856a <+42>:    mov    0x8(%eax),%ecx
   0x0804856d <+45>:    mov    -0x10(%ebp),%edx
   0x08048570 <+48>:    mov    %edx,%eax
   0x08048572 <+50>:    add    %eax,%eax
   0x08048574 <+52>:    add    %edx,%eax
   0x08048576 <+54>:    shl    $0x2,%eax
   0x08048579 <+57>:    mov    %eax,%edx
   0x0804857b <+59>:    mov    0x8(%ebp),%eax
   0x0804857e <+62>:    add    %edx,%eax
   0x08048580 <+64>:    mov    0x4(%eax),%ebx
   0x08048583 <+67>:    mov    -0x10(%ebp),%edx
   0x08048586 <+70>:    mov    %edx,%eax
   0x08048588 <+72>:    add    %eax,%eax
   0x0804858a <+74>:    add    %edx,%eax
   0x0804858c <+76>:    shl    $0x2,%eax
   0x0804858f <+79>:    mov    %eax,%edx
   0x08048591 <+81>:    mov    0x8(%ebp),%eax
   0x08048594 <+84>:    add    %edx,%eax
   0x08048596 <+86>:    mov    (%eax),%eax
   0x08048598 <+88>:    mov    %ebx,0x4(%esp)
   0x0804859c <+92>:    mov    %eax,(%esp)

   0x0804859f <+95>:    call   *%ecx
   0x080485a1 <+97>:    add    %eax,-0xc(%ebp)

   0x080485a4 <+100>:   addl   $0x1,-0x10(%ebp)
   0x080485a8 <+104>:   mov    -0x10(%ebp),%eax
   0x080485ab <+107>:   cmp    0xc(%ebp),%eax
   0x080485ae <+110>:   setl   %al
   0x080485b1 <+113>:   test   %al,%al
   0x080485b3 <+115>:   jne    0x8048557 <_Z6resultP22xuzhina_dump_c05_s3_exi+23>
   0x080485b5 <+117>:   mov    -0xc(%ebp),%eax
   0x080485b8 <+120>:   add    $0x24,%esp
   0x080485bb <+123>:   pop    %ebx
   0x080485bc <+124>:   pop    %ebp
   0x080485bd <+125>:   ret    
End of assembler dump.

在上面汇编,指令地址0x08048557-0x080485b3是一个循环,循环条件是判断ebp-0x10单元的值是不是与函数第三个参数(ebp+0xc)相等,由此可知,ebp-0x10存放着循环计数,取名为cnt。而栈顶的返回值0x080485a1也位于这个循环里,

那么coredump指令是由

代码语言:javascript
复制
   0x0804859f <+95>:    call   *%ecx

引起,也就是说ecx为0,如下图。

代码语言:javascript
复制
(gdb) frame 1
#1  0x080485a1 in result(xuzhina_dump_c05_s3_ex*, int) ()
(gdb) i r
eax            0x3      3
ecx            0x0      0
edx            0x24     36
ebx            0x3      3
esp            0xbfac0250       0xbfac0250
ebp            0xbfac0278       0xbfac0278
esi            0x0      0
edi            0x0      0
eip            0x80485a1        0x80485a1 <result(xuzhina_dump_c05_s3_ex*, int)+97>
eflags         0x10296  [ PF AF SF IF RF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

ecx这个函数指针是由下面的指令来获得:

代码语言:javascript
复制
   0x08048557 <+23>:    mov    -0x10(%ebp),%edx
   0x0804855a <+26>:    mov    %edx,%eax
   0x0804855c <+28>:    add    %eax,%eax
   0x0804855e <+30>:    add    %edx,%eax
   0x08048560 <+32>:    shl    $0x2,%eax
   0x08048563 <+35>:    mov    %eax,%edx
   0x08048565 <+37>:    mov    0x8(%ebp),%eax
   0x08048568 <+40>:    add    %edx,%eax
   0x0804856a <+42>:    mov    0x8(%eax),%ecx

ecx函数指针的第一个参数的获得:

代码语言:javascript
复制
   0x08048583 <+67>:    mov    -0x10(%ebp),%edx
   0x08048586 <+70>:    mov    %edx,%eax
   0x08048588 <+72>:    add    %eax,%eax
   0x0804858a <+74>:    add    %edx,%eax
   0x0804858c <+76>:    shl    $0x2,%eax
   0x0804858f <+79>:    mov    %eax,%edx
   0x08048591 <+81>:    mov    0x8(%ebp),%eax
   0x08048594 <+84>:    add    %edx,%eax
   0x08048596 <+86>:    mov    (%eax),%eax

ecx函数指针的第二个参数的获得:

代码语言:javascript
复制
   0x0804856d <+45>:    mov    -0x10(%ebp),%edx
   0x08048570 <+48>:    mov    %edx,%eax
   0x08048572 <+50>:    add    %eax,%eax
   0x08048574 <+52>:    add    %edx,%eax
   0x08048576 <+54>:    shl    $0x2,%eax
   0x08048579 <+57>:    mov    %eax,%edx
   0x0804857b <+59>:    mov    0x8(%ebp),%eax
   0x0804857e <+62>:    add    %edx,%eax
   0x08048580 <+64>:    mov    0x4(%eax),%ebx

由上面可以看到,这三个值的获取只有最后一条指令是不一样,其它都一样。

先分析一下前面相同的指令:

代码语言:javascript
复制
   0x0804856d <+45>:    mov    -0x10(%ebp),%edx		;把cnt(ebp-0x10)的内容放入edx
   0x08048570 <+48>:    mov    %edx,%eax			;eax = edx
   0x08048572 <+50>:    add    %eax,%eax				;eax *= 2
   0x08048574 <+52>:    add    %edx,%eax				;eax += edx = 3*edx
   0x08048576 <+54>:    shl    $0x2,%eax				;eax <<= 2. eax = 12*cnt
   0x08048579 <+57>:    mov    %eax,%edx			;edx = eax
   0x0804857b <+59>:    mov    0x8(%ebp),%eax		;result函数的第一个参数
   0x0804857e <+62>:    add    %edx,%eax				;eax += edx

由上面的汇编可以看出,取得ecx所指向的函数及它两个参数,都是在result函数的第一参数加一定偏移值来得到,而这个偏移值则是12*cnt。

而cnt在每次循环比较结束条件时,都会:

代码语言:javascript
复制
   0x080485a4 <+100>:   addl   $0x1,-0x10(%ebp)

即cnt++。也就是说,每次循环获取函数指针放入ecx及函数指针两个参数时,计算的偏移值都会比上一次循环的偏移值多上12,是一个固定值。根据数组的特征,可见result函数的第一个参数是一个数组,它每个元素的大小是12

且cnt在循环比较结束条件时,都会

代码语言:javascript
复制
   0x080485ab <+107>:   cmp    0xc(%ebp),%eax

可见,result函数的第二个参数是数组要遍历的个数。

再者,由于函数指针和它两个参数的获得只是最后一条指令不一样,且它们是以数组的元素为基地址的。可以推断它们是结构体。

所以,现在就是要看一下究竟是数组哪个元素的第三个成员为空。

由于cnt是放在ebp-0x10,看一下ebp-0x10的内容就知道:

代码语言:javascript
复制
(gdb) frame 
#1  0x080485a1 in result(xuzhina_dump_c05_s3_ex*, int) ()
(gdb)  x /x $ebp-0x10
0xbfac0268:     0x00000003

可见是数组的第四个元素的第三个成员为空。

看一下代码:

代码语言:javascript
复制
 typedef int (*operation)( int a, int b );
 struct xuzhina_dump_c05_s3_ex
 {
     int a;
     int b;
     operation oper;
 };
 
 int result( struct xuzhina_dump_c05_s3_ex test[], int num )
 {
     int res = 0;
     for ( int i = 0; i < num; i++ )
     {
         res += test[i].oper( test[i].a, test[i].b );
     }
	 
     return res;
 }
 
 int add( int a, int b )
 {
     return a+b;
 }
 int sub( int a, int b )
 {
     return a-b;
 }
 int mul( int a, int b )
 {
     return a*b;
 }
 
 void init( struct xuzhina_dump_c05_s3_ex test[], int num )
 {
      for( int i = 0; i < num; i++ )
      {
          switch( i % 4 )
          {
              case 0:
                  test[i].a = i/4;
                  test[i].b = 0;
                  test[i].oper = add;
                 break;
             case 1:
                 test[i].a = i/4;
                 test[i].b = i%4;
                 test[i].oper = mul;
                 break;
             case 2:
                 test[i].a = i%4;
                 test[i].b = i/4;
                 test[i].oper = sub;
                 break;
             default:
                 test[i].a = i;
                 test[i].b = i%4;
                 test[i].oper = 0;
                 break;
         }
     }
 }
 
 int main()
 {
     struct xuzhina_dump_c05_s3_ex test[15];
     init( test, 15 );
 
     return result( test, 15 );
 }

可见是由于init的这一块代码所导致的

代码语言:javascript
复制
54	             default:
 55	                 test[i].a = i;
 56	                 test[i].b = i%4;
 57	                 test[i].oper = 0;
 58	                 break;
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2013年04月24日,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

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