获取主可执行文件的ELF头

小太郎*_*小太郎 11 c c++ linux elf

出于各种目的,我试图在不解析的情况下获取主可执行文件的ELF头的地址/proc/self/maps.我尝试解析/ functions link_list给出的链,但它们不包含指向主可执行文件基址的条目.有没有办法做到这一点(标准与否)没有解析?dlopendlinfol_addr/proc/self/maps

我正在尝试做的一个例子:

#include <stdio.h>
#include <elf.h>
int main()
{
    Elf32_Ehdr* header = /* Somehow obtain the address of the ELF header of this program */;
    printf("%p\n", header);
    /* Read the header and do stuff, etc */
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Emp*_*ian 18

void *返回的指针dlopen(0, RTLD_LAZY)给你一个struct link_map *,对应于主可执行文件.

调用dl_iterate_phdr还会在第一次执行回调时返回主可执行文件的条目.

您可能会对.l_addr == 0链接映射中的事实以及dlpi_addr == 0使用时的事实感到困惑dl_iterate_phdr.

这种情况正在发生,因为l_addr(和dlpi_addr)实际上并没有记录ELF图像的加载地址.相反,它们记录已应用于该图像的重定位.

通常,主可执行文件构建为加载0x400000(对于x86_64 Linux)或0x08048000(对于ix86 Linux),并加载到同一地址(即它们不重定位).

但是,如果您将可执行文件与-pieflag 链接起来,那么它将被链接到0x0,并且它将被重定位到其他地址.

那你怎么得到ELF标题?简单:

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <link.h>
#include <stdio.h>
#include <stdlib.h>

static int
callback(struct dl_phdr_info *info, size_t size, void *data)
{
  int j;
  static int once = 0;

  if (once) return 0;
  once = 1;

  printf("relocation: 0x%lx\n", (long)info->dlpi_addr);

  for (j = 0; j < info->dlpi_phnum; j++) {
    if (info->dlpi_phdr[j].p_type == PT_LOAD) {
      printf("a.out loaded at %p\n",
             (void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr));
      break;
    }
  }
  return 0;
}

int
main(int argc, char *argv[])
{
  dl_iterate_phdr(callback, NULL);
  exit(EXIT_SUCCESS);
}


$ gcc -m32 t.c && ./a.out
relocation: 0x0
a.out loaded at 0x8048000

$ gcc -m64 t.c && ./a.out
relocation: 0x0
a.out loaded at 0x400000

$ gcc -m32 -pie -fPIC t.c && ./a.out
relocation: 0xf7789000
a.out loaded at 0xf7789000

$ gcc -m64 -pie -fPIC t.c && ./a.out
relocation: 0x7f3824964000
a.out loaded at 0x7f3824964000
Run Code Online (Sandbox Code Playgroud)

更新:

为什么手册页会说"基地址"而不是重定位?

这是一个错误;-)

我猜测,这名男子页的很久以前写的prelinkpie,和ASLR存在.如果没有预链接,共享库始终链接在地址加载0x0,然后relocationbase address成为一个和相同的.

当info引用主可执行文件时,dlpi_name如何指向空字符串?

这是实施的意外.

它的工作方式是内核open(2)是可执行文件,并将打开的文件描述符传递给加载器(在auxv[]向量中,如AT_EXECFD).一切装载机知道它会通过读取文件描述符的可执行文件.

在UNIX上没有简单的方法将文件描述符映射回它打开的名称.首先,UNIX支持硬链接,并且可能有多个引用同一文件的文件名.

较新的Linux内核也传递用于execve(2)可执行文件的名称(也在auxv[]as中AT_EXECFN).但这是可选的,即使传入它,glibc也不会将其放入.l_name/ dlpi_name以便不破坏依赖于名称为空的现有程序.

相反,glibc将该名称保存在__progname和中__progname_full.

装载机coud readlink(2)从名称/proc/self/exe上未使用的系统AT_EXECFN,但该/proc文件系统,但不保证任何安装,这样仍然有一个空的名字离开它的时候.

  • 难道不是一个更简单的方法(如果依赖未记录的细节),只需在“struct link_map”中的“l_ld”地址上调用“dladdr”,然后使用“dli_fbase”? (2认同)