linux – 为什么ELF部分之间没有未使用的空白空间?

linux – 为什么ELF部分之间没有未使用的空白空间?,第1张

概述似乎在 Linux上使用gcc 4.9.2创建的二进制文件(Ubuntu 15.04,32位)在.eh_frame和.init_array节之间有几千个未使用的字节. objdump -h为简单可执行文件输出的示例: Sections:Idx Name Size VMA LMA File off Algn[...] 16 .eh_fram 似乎在 Linux上使用gcc 4.9.2创建的二进制文件(Ubuntu 15.04,32位)在.eh_frame和.init_array节之间有几千个未使用的字节. objdump -h为简单可执行文件输出的示例:
Sections:IDx name          Size      VMA       LMA       file off  Algn[...] 16 .eh_frame     000000c0  080484ac  080484ac  000004ac  2**2                  CONTENTS,ALLOC,LOAD,Readonly,DATA 17 .init_array   00000004  08049f08  08049f08  00000f08  2**2                  CONTENTS,DATA[...]

.eh_frame以文件偏移量0x56c结束,但.init_array从0xf08开始,留下一个0x99c = 2460字节的漏洞.所有其他部分在上一部分结束后立即开始.

未使用空间的大小会有所不同,因此很难观察到某些更改如何影响代码大小.

这个洞来自哪里?有没有办法避免它?

更新:输出ld –verbose:

$cat so.cint main() {    return 0;}$gcc so.c -Wl,--verbose -o soGNU ld (GNU Binutils for Ubuntu) 2.25  Supported emulations:   elf_i386   i386linux   elf32_x86_64   elf_x86_64   elf_l1om   elf_k1om   i386pep   i386peusing internal linker script:==================================================/* Script for -z combreloc: combine and sort reloc sections *//* copyright (C) 2014 Free Software Foundation,Inc.   copying and distribution of this script,with or without modification,are permitted in any medium without royalty provIDed the copyright   notice and this notice are preserved.  */OUTPUT_FORMAT("elf32-i386","elf32-i386","elf32-i386")OUTPUT_ARCH(i386)ENTRY(_start)SEARCH_DIR("=/usr/i686-linux-gnu/lib32"); SEARCH_DIR("=/usr/local/lib32"); SEARCH_DIR("=/lib32"); SEARCH_DIR("=/usr/lib32"); SEARCH_DIR("=/usr/i686-linux-gnu/lib"); SEARCH_DIR("=/usr/local/lib/i386-linux-gnu"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib/i386-linux-gnu"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib/i386-linux-gnu"); SEARCH_DIR("=/usr/lib");SECTIONS{  /* Read-only sections,merged into text segment: */  PROVIDE (__executable_start = SEGMENT_START("text-segment",0x08048000)); . = SEGMENT_START("text-segment",0x08048000) + SIZEOF_headerS;  .interp         : { *(.interp) }  .note.gnu.build-ID : { *(.note.gnu.build-ID) }  .hash           : { *(.hash) }  .gnu.hash       : { *(.gnu.hash) }  .dynsym         : { *(.dynsym) }  .dynstr         : { *(.dynstr) }  .gnu.version    : { *(.gnu.version) }  .gnu.version_d  : { *(.gnu.version_d) }  .gnu.version_r  : { *(.gnu.version_r) }  .rel.dyn        :    {      *(.rel.init)      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)      *(.rel.fini)      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)      *(.rel.data.rel.ro .rel.data.rel.ro.* .rel.gnu.linkonce.d.rel.ro.*)      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)      *(.rel.ctors)      *(.rel.dtors)      *(.rel.got)      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)      *(.rel.ifunc)    }  .rel.plt        :    {      *(.rel.plt)      PROVIDE_HIDDEN (__rel_iplt_start = .);      *(.rel.iplt)      PROVIDE_HIDDEN (__rel_iplt_end = .);    }  .init           :  {    KEEP (*(SORT_NONE(.init)))  }  .plt            : { *(.plt) *(.iplt) }  .text           :  {    *(.text.unlikely .text.*_unlikely .text.unlikely.*)    *(.text.exit .text.exit.*)    *(.text.startup .text.startup.*)    *(.text.hot .text.hot.*)    *(.text .stub .text.* .gnu.linkonce.t.*)    /* .gnu.warning sections are handled specially by elf32.em.  */    *(.gnu.warning)  }  .fini           :  {    KEEP (*(SORT_NONE(.fini)))  }  PROVIDE (__etext = .);  PROVIDE (_etext = .);  PROVIDE (etext = .);  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }  .rodata1        : { *(.rodata1) }  .eh_frame_hdr : { *(.eh_frame_hdr) }  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table  .gcc_except_table.*) }  /* These sections are generated by the Sun/Oracle C++ compiler.  */  .exception_ranges   : ONLY_IF_RO { *(.exception_ranges  .exception_ranges*) }  /* Adjust the address for the data segment.  We want to adjust up to     the same address within the page on the next page up.  */  . = AliGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_AliGN (CONSTANT (MAXPAGESIZE),CONSTANT (COMMONPAGESIZE));  /* Exception handling  */  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }  .exception_ranges   : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }  /* Thread Local Storage sections  */  .tdata          : { *(.tdata .tdata.* .gnu.linkonce.td.*) }  .tbss           : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }  .preinit_array     :  {    PROVIDE_HIDDEN (__preinit_array_start = .);    KEEP (*(.preinit_array))    PROVIDE_HIDDEN (__preinit_array_end = .);  }  .init_array     :  {    PROVIDE_HIDDEN (__init_array_start = .);    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))    KEEP (*(.init_array EXCLUDE_file (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))    PROVIDE_HIDDEN (__init_array_end = .);  }  .fini_array     :  {    PROVIDE_HIDDEN (__fini_array_start = .);    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))    KEEP (*(.fini_array EXCLUDE_file (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))    PROVIDE_HIDDEN (__fini_array_end = .);  }  .ctors          :  {    /* gcc uses crtbegin.o to find the start of       the constructors,so we make sure it is       first.  Because this is a wildcard,it       doesn't matter if the user does not       actually link against crtbegin.o; the       linker won't look for a file to match a       wildcard.  The wildcard also means that it       doesn't matter which directory crtbegin.o       is in.  */    KEEP (*crtbegin.o(.ctors))    KEEP (*crtbegin?.o(.ctors))    /* We don't want to include the .ctor section from       the crtend.o file until after the sorted ctors.       The .ctor section from the crtend file contains the       end of ctors marker and it must be last */    KEEP (*(EXCLUDE_file (*crtend.o *crtend?.o ) .ctors))    KEEP (*(SORT(.ctors.*)))    KEEP (*(.ctors))  }  .dtors          :  {    KEEP (*crtbegin.o(.dtors))    KEEP (*crtbegin?.o(.dtors))    KEEP (*(EXCLUDE_file (*crtend.o *crtend?.o ) .dtors))    KEEP (*(SORT(.dtors.*)))    KEEP (*(.dtors))  }  .jcr            : { KEEP (*(.jcr)) }  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }  .dynamic        : { *(.dynamic) }  .got            : { *(.got) *(.igot) }  . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 12 ? 12 : 0,.);  .got.plt        : { *(.got.plt)  *(.igot.plt) }  .data           :  {    *(.data .data.* .gnu.linkonce.d.*)    SORT(CONSTRUCTORS)  }  .data1          : { *(.data1) }  _edata = .; PROVIDE (edata = .);  . = .;  __bss_start = .;  .bss            :  {   *(.dynbss)   *(.bss .bss.* .gnu.linkonce.b.*)   *(COMMON)   /* Align here to ensure that the .bss section occupIEs space up to      _end.  Align after .bss to ensure correct alignment even if the      .bss section disappears because there are no input sections.      FIXME: Why do we need it? When there is no .bss section,we don't      pad the .data section.  */   . = AliGN(. != 0 ? 32 / 8 : 1);  }  . = AliGN(32 / 8);  . = SEGMENT_START("ldata-segment",.);  . = AliGN(32 / 8);  _end = .; PROVIDE (end = .);  . = DATA_SEGMENT_END (.);  /* Stabs deBUGging sections.  */  .stab          0 : { *(.stab) }  .stabstr       0 : { *(.stabstr) }  .stab.excl     0 : { *(.stab.excl) }  .stab.exclstr  0 : { *(.stab.exclstr) }  .stab.index    0 : { *(.stab.index) }  .stab.indexstr 0 : { *(.stab.indexstr) }  .comment       0 : { *(.comment) }  /* DWARF deBUG sections.     Symbols in the DWARF deBUGging sections are relative to the beginning     of the section so we begin them at 0.  */  /* DWARF 1 */  .deBUG          0 : { *(.deBUG) }  .line           0 : { *(.line) }  /* GNU DWARF 1 extensions */  .deBUG_srcinfo  0 : { *(.deBUG_srcinfo) }  .deBUG_sfnames  0 : { *(.deBUG_sfnames) }  /* DWARF 1.1 and DWARF 2 */  .deBUG_aranges  0 : { *(.deBUG_aranges) }  .deBUG_pubnames 0 : { *(.deBUG_pubnames) }  /* DWARF 2 */  .deBUG_info     0 : { *(.deBUG_info .gnu.linkonce.wi.*) }  .deBUG_abbrev   0 : { *(.deBUG_abbrev) }  .deBUG_line     0 : { *(.deBUG_line .deBUG_line.* .deBUG_line_end ) }  .deBUG_frame    0 : { *(.deBUG_frame) }  .deBUG_str      0 : { *(.deBUG_str) }  .deBUG_loc      0 : { *(.deBUG_loc) }  .deBUG_macinfo  0 : { *(.deBUG_macinfo) }  /* sgi/MIPS DWARF 2 extensions */  .deBUG_weaknames 0 : { *(.deBUG_weaknames) }  .deBUG_funcnames 0 : { *(.deBUG_funcnames) }  .deBUG_typenames 0 : { *(.deBUG_typenames) }  .deBUG_varnames  0 : { *(.deBUG_varnames) }  /* DWARF 3 */  .deBUG_pubtypes 0 : { *(.deBUG_pubtypes) }  .deBUG_ranges   0 : { *(.deBUG_ranges) }  /* DWARF Extension.  */  .deBUG_macro    0 : { *(.deBUG_macro) }  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }  /disCARD/ : { *(.note.GNU-stack) *(.gnu_deBUGlink) *(.gnu.lto_*) }}==================================================attempt to open /usr/lib/gcc/i686-linux-gnu/4.9/../../../i386-linux-gnu/crt1.o succeeded/usr/lib/gcc/i686-linux-gnu/4.9/../../../i386-linux-gnu/crt1.oattempt to open /usr/lib/gcc/i686-linux-gnu/4.9/../../../i386-linux-gnu/crti.o succeeded/usr/lib/gcc/i686-linux-gnu/4.9/../../../i386-linux-gnu/crti.oattempt to open /usr/lib/gcc/i686-linux-gnu/4.9/crtbegin.o succeeded/usr/lib/gcc/i686-linux-gnu/4.9/crtbegin.oattempt to open /tmp/ccQ0fTTK.o succeeded/tmp/ccQ0fTTK.oattempt to open /usr/lib/gcc/i686-linux-gnu/4.9/libgcc.so Failedattempt to open /usr/lib/gcc/i686-linux-gnu/4.9/libgcc.a succeededattempt to open /usr/lib/gcc/i686-linux-gnu/4.9/libgcc_s.so succeeded-lgcc_s (/usr/lib/gcc/i686-linux-gnu/4.9/libgcc_s.so)attempt to open /usr/lib/gcc/i686-linux-gnu/4.9/libc.so Failedattempt to open /usr/lib/gcc/i686-linux-gnu/4.9/libc.a Failedattempt to open /usr/lib/gcc/i686-linux-gnu/4.9/../../../i386-linux-gnu/libc.so succeededopened script file /usr/lib/gcc/i686-linux-gnu/4.9/../../../i386-linux-gnu/libc.soopened script file /usr/lib/gcc/i686-linux-gnu/4.9/../../../i386-linux-gnu/libc.soattempt to open /lib/i386-linux-gnu/libc.so.6 succeeded/lib/i386-linux-gnu/libc.so.6attempt to open /usr/lib/i386-linux-gnu/libc_nonshared.a succeeded(/usr/lib/i386-linux-gnu/libc_nonshared.a)elf-init.oSattempt to open /lib/i386-linux-gnu/ld-linux.so.2 succeeded/lib/i386-linux-gnu/ld-linux.so.2/lib/i386-linux-gnu/ld-linux.so.2attempt to open /usr/lib/gcc/i686-linux-gnu/4.9/libgcc.so Failedattempt to open /usr/lib/gcc/i686-linux-gnu/4.9/libgcc.a succeededattempt to open /usr/lib/gcc/i686-linux-gnu/4.9/libgcc_s.so succeeded-lgcc_s (/usr/lib/gcc/i686-linux-gnu/4.9/libgcc_s.so)attempt to open /usr/lib/gcc/i686-linux-gnu/4.9/crtend.o succeeded/usr/lib/gcc/i686-linux-gnu/4.9/crtend.oattempt to open /usr/lib/gcc/i686-linux-gnu/4.9/../../../i386-linux-gnu/crtn.o succeeded/usr/lib/gcc/i686-linux-gnu/4.9/../../../i386-linux-gnu/crtn.old-linux.so.2 needed by /lib/i386-linux-gnu/libc.so.6found ld-linux.so.2 at /lib/i386-linux-gnu/ld-linux.so.2
解决方法 那里有三个内存区域:

>只读数据.
>可以在加载时修复的非延迟重定位.
>数据.

现在,.eh_frame部分标记为Readonly,因此它进入第一部分.

.init_array是一个初始化函数的函数指针数组,在加载程序/库时可以解析为它们的绝对地址,然后标记为只读(写入函数指针是利用漏洞的常用方法),所以它去了进入第二个地区.

链接描述文件的相关部分是:

[...].eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }[...]/* Adjust the address for the data segment.  We want to adjust up to   the same address within the page on the next page up.  */. = AliGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1));. = DATA_SEGMENT_AliGN (CONSTANT (MAXPAGESIZE),CONSTANT (COMMONPAGESIZE));[...].init_array     :[...].got            : { *(.got) *(.igot) }. = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 12 ? 12 : 0,.);.got.plt        : { *(.got.plt)  *(.igot.plt) }.data           :[...]. = DATA_SEGMENT_END (.);

您可以在https://sourceware.org/binutils/docs/ld/Builtin-Functions.html查阅GNU ld链接器脚本内置函数的文档.但要注意DATA_SEGMENT_AliGN文档不正确,正如Stephen Kell在binutils bug #19203: “DATA_SEGMENT_ALIGN documentation is not consistent with behaviour”报道的那样,显然是因为Jakub Jelinek的[PATCH] Fix DATA_SEGMENT_ALIGN.DATA_SEGMENT_AliGN本身是在binutils的邮件列表线程中引入的[RFC PATCH] Smarter aligning of data segment.

不知何故,以下内容:

. = AliGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1));. = DATA_SEGMENT_AliGN (CONSTANT (MAXPAGESIZE),CONSTANT (COMMONPAGESIZE));

导致1页跳转,在您的示例中将您从0x0804856c移动到0x0804956c.

当使用链接器选项-z relro时,请求在加载时修复的重定位被标记为只读,DATA_SEGMENT_RELRO_END会使先前的DATA_SEGMENT_AliGN添加足够的填充,以使DATA_SEGMENT_RELRO_END的两个参数的总和与新页面对齐.

因此,假设.got.plt至少有三个指针,前三个指针(由加载器立即使用)将位于第二个区域,其余的.got.plt位于第三个区域.

DATA_SEGMENT_AliGN添加的填充将您从0x0804956c移动到0x08049f08.如果在发生修复后发送的所有可以被保护的内容都是只读的,那么在新页面中,您将保持在0x0804a000,这将保持读写.

总结

以上是内存溢出为你收集整理的linux – 为什么ELF部分之间没有未使用的空白空间?全部内容,希望文章能够帮你解决linux – 为什么ELF部分之间没有未使用的空白空间?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/yw/1049860.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)

保存