是否跳过/删除ELF文件中的PHDR程序标头以获取可执行文件?如果是这样,为什么?

问题描述 投票:1回答:2

我正在为这个简单的C ++程序进行二进制编程,以了解ELF的程序头:

int main(){ }

编译:

❯ make
g++ -O0 -fverbose-asm -no-pie -o main main.cpp

我使用readelf -l main获得以下信息:

Elf file type is EXEC (Executable file)
Entry point 0x401020
There are 11 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x0000000000000268 0x0000000000000268  R      0x8
  INTERP         0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000004c0 0x00000000000004c0  R      0x1000
...

我在本文档中看到:PHDR的http://man7.org/linux/man-pages/man5/elf.5.html

数组元素,如果存在,则指定局部程序头表本身的大小和大小,pro的文件和存储映像中公克。此细分类型可能不会出现超过一次写入文件。此外,只有在程序头表是内存映像的一部分该程序。如果存在,则必须在任何可加载的细分条目。

if present的出现让我想知道如果我跳过PHDR标头会发生什么。我使用vim的十六进制编辑器使用main来更改:%!xxd的二进制布局(是请务必在保存之前运行:%!xxd -r,否则它将不再是二进制文件):

00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000  .ELF............
00000010: 0200 3e00 0100 0000 2010 4000 0000 0000  ..>..... .@.....
00000020: 4000 0000 0000 0000 1839 0000 0000 0000  @........9......

至:

00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000  .ELF............
00000010: 0200 3e00 0100 0000 2010 4000 0000 0000  ..>..... .@.....
00000020: 7800 0000 0000 0000 1839 0000 0000 0000  @........9......

(仅更改第20个字节),以跳过PHDR标头的长度。我再次运行readelf以确认它仍然是有效的ELF文件:

❯ readelf -l main

Elf file type is EXEC (Executable file)
Entry point 0x401020
There are 11 program headers, starting at offset 120

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  INTERP         0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  ...

令人惊讶的是,程序仍然可以完美执行。为什么我们甚至需要PHDR标头?对链接和/或其他情况有用吗?似乎在运行时根本没有使用过,所以为什么要放这个呢?

c++ c elf readelf
2个回答
1
投票

如果主程序的类型为ET_EXEC(非PIE),则可以在没有PT_PHDR的情况下运行。 PT_PHDR的主要用途是将标头中的(未重定位)地址与程序标头的实际运行时地址(由动态链接器通过aux向量中的AT_PHDR获得)进行比较,以确定偏移量PIE可执行文件已加载。

我不确定glibc对具有PT_PHDR的动态链接器的要求是什么,但是在musl libc中,我们仅需要它来计算此负载偏移量,否则根本就不会使用它。


0
投票

我再次运行readelf以确认它仍然是有效的ELF文件:

请注意,尽管它是有效的ELF,但它现在已经破坏了程序头表中的第11个条目(因为您没有减少程序头的数量)。

而且令人惊讶的是,程序仍然可以完美执行。

此程序不使用动态链接器的any功能,因此,您破坏了其结构的事实并不明显。

现在尝试从libc.so.6调用某些例程,或调用dlopendlsym,看看是否仍然有效。

[查看GLIBC加载程序源(rtld.c),它确实非常关心PT_PHDR,所以如果没有它,事情仍然可以工作,我会感到惊讶。

© www.soinside.com 2019 - 2024. All rights reserved.