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

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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuzhina/article/details/8846866

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

(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的汇编:

(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指令是由

   0x0804859f <+95>:    call   *%ecx

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

(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这个函数指针是由下面的指令来获得:

   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函数指针的第一个参数的获得:

   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函数指针的第二个参数的获得:

   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

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

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

   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在每次循环比较结束条件时,都会:

   0x080485a4 <+100>:   addl   $0x1,-0x10(%ebp)

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

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

   0x080485ab <+107>:   cmp    0xc(%ebp),%eax

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

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

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

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

(gdb) frame 
#1  0x080485a1 in result(xuzhina_dump_c05_s3_ex*, int) ()
(gdb)  x /x $ebp-0x10
0xbfac0268:     0x00000003

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

看一下代码:

 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的这一块代码所导致的

54	             default:
 55	                 test[i].a = i;
 56	                 test[i].b = i%4;
 57	                 test[i].oper = 0;
 58	                 break;

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 《coredump问题原理探究》Linux x86版7.2节vector coredump例子

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuzhina/article/detai...

    血狼
  • 《coredump问题原理探究》Linux x86版7.8节vector相关的iterator对象

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuzhina/article/detai...

    血狼
  • 《coredump问题原理探究》Linux x86版3.7节栈布局之返回值

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuzhina/article/detai...

    血狼
  • C语言与汇编的嵌入式编程:统计字符串中各字符出现的次数

    墨文
  • 新型武器Xerosploit制霸内网

    前言 今天在逛i春秋论坛的时候,看到有位大神写的一篇文章 脆弱的内网安全之Xerosploit的使用 这里面稍微带过一个各个模块的使用,我这里拿其中一个模块来详...

    用户1467662
  • Docker进阶与实战(容器技术系列)

    本书由一个真正钻研容器技术的团队写作,他们不仅仅是在使用Docker,更多的是在探索容器的未来之路,希望把“代码与产品,理论与实践”完美结合。本书内容从Dock...

    用户3157710
  • 即将到来的以太坊伊斯坦布尔有哪些更新

    以太坊网络计划将于9069000区块号进行代号为:伊斯坦布尔(Istanbul)的升级。该块预计将于 2019 年 12 月 7 日,周六挖出。

    Tiny熊
  • 实战篇 | 用Xpath,bs4,正则三种方式爬51job

    最近整理一个爬虫系列方面的文章,不管大家的基础如何,我从头开始整一个爬虫系列方面的文章,让大家循序渐进的学习爬虫,小白也没有学习障碍.

    叫我龙总
  • QEMU 1: 使用QEMU创建虚拟机

    一、QEMU简介# QEMU是一款开源的模拟器及虚拟机监管器(Virtual Machine Monitor, VMM)。QEMU主要提供两种功能给用户使用。一...

    用户1214695
  • 快来算一算你写了多少行代码

    前面我们一直在介绍图形界面,这次换个口味,在命令行实现代码行数统计程序。本程序共 135 行,其中 18 行空行、110 行有效代码、7行注释,大小为 7.71...

    用户2870857

扫码关注云+社区

领取腾讯云代金券