当前位置: 首页 > 面试题库 >

获取ELF二进制文件的已加载地址,dlopen无法正常工作

微生俊材
2023-03-14
问题内容

我正在尝试获取ELF二进制文件的加载地址,但dlopen无法按预期工作:

void *elf = (char *)dlopen (0, RTLD_NOW);
printf ("%p\n", elf);
sleep (100);

它会输出0xb772d918,但根据显示的内容/proc/1510/maps,它不会指向dlfn二进制文件的已加载地址,而是ld-2.15.so

08048000-08049000 r-xp 00000000 fc:00 1379       /root/dlfn
08049000-0804a000 r--p 00000000 fc:00 1379       /root/dlfn
0804a000-0804b000 rw-p 00001000 fc:00 1379       /root/dlfn
b7550000-b7552000 rw-p 00000000 00:00 0 
b7552000-b76f5000 r-xp 00000000 fc:00 9275       /lib/i386-linux-gnu/libc-2.15.so
b76f5000-b76f7000 r--p 001a3000 fc:00 9275       /lib/i386-linux-gnu/libc-2.15.so
b76f7000-b76f8000 rw-p 001a5000 fc:00 9275       /lib/i386-linux-gnu/libc-2.15.so
b76f8000-b76fb000 rw-p 00000000 00:00 0 
b76fb000-b76fe000 r-xp 00000000 fc:00 9305       /lib/i386-linux-gnu/libdl-2.15.so
b76fe000-b76ff000 r--p 00002000 fc:00 9305       /lib/i386-linux-gnu/libdl-2.15.so
b76ff000-b7700000 rw-p 00003000 fc:00 9305       /lib/i386-linux-gnu/libdl-2.15.so
b7708000-b770b000 rw-p 00000000 00:00 0 
b770b000-b770c000 r-xp 00000000 00:00 0          [vdso]
b770c000-b772c000 r-xp 00000000 fc:00 9299       /lib/i386-linux-gnu/ld-2.15.so
b772c000-b772d000 r--p 0001f000 fc:00 9299       /lib/i386-linux-gnu/ld-2.15.so
b772d000-b772e000 rw-p 00020000 fc:00 9299       /lib/i386-linux-gnu/ld-2.15.so
bfc34000-bfc55000 rw-p 00000000 00:00 0          [stack]

因此,除了解析外/proc/pid/maps,还有没有办法检索ELF二进制文件的加载地址?(在这种情况下为0x0848000)


问题答案:

在Linux上,dlopen不返回加载ELF二进制文件的地址。而是返回struct link_map具有.l_addr成员的成员。因此,您需要以下内容:

struct link_map *lm = (struct link_map*) dlopen(0, RTLD_NOW);
printf("%p\n", lm->l_addr);

但是,尽管有评论/usr/include/link.h说,.l_addr实际上也不是加载地址。相反,这是ELF图像链接到加载位置与实际加载位置之间的_区别_ 。

对于非PIE主可执行文件,该差异始终为0。对于非预链接的共享库,该差异始终为加载地址(因为非预链接的ELF共享库已链接到地址0的加载)。

那么,如何找到主要可执行文件的基地址呢?最简单的方法是使用以下代码(链接到主要可执行文件中):

#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;
  const char *cb = (const char *)&callback;
  const char *base = (const char *)info->dlpi_addr;
  const ElfW(Phdr) *first_load = NULL;

  for (j = 0; j < info->dlpi_phnum; j++) {
    const ElfW(Phdr) *phdr = &info->dlpi_phdr[j];

    if (phdr->p_type == PT_LOAD) {
      const char *beg = base + phdr->p_vaddr;
      const char *end = beg + phdr->p_memsz;

      if (first_load == NULL) first_load = phdr;
      if (beg <= cb && cb < end) {
        // Found PT_LOAD that "covers" callback().
        printf("ELF header is at %p, image linked at 0x%zx, relocation: 0x%zx\n",
               base + first_load->p_vaddr, first_load->p_vaddr, info->dlpi_addr);
        return 1;
      }
    }
  }
  return 0;
}

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

这是在32位系统上应该看到的内容:

$ gcc -g t.c -ldl -m32  && ./a.out
ELF header is at 0x8048000, image linked at 0x8048000, relocation: 0x0
$ gcc -g t.c -ldl -m32 -pie -fPIE  && ./a.out
ELF header is at 0xf779a000, image linked at 0x0, relocation: 0xf779a000

(最后一个地址:0xf779a000如果启用了地址随机化,则每次运行都会有所不同(应如此))。



 类似资料:
  • 问题内容: 我有以下示例代码 我用-g选项将这段代码编译为gcc。 生成单独的符号文件 去除a.out中的符号 在gdb中加载此a.out gdb说“找不到调试信息”很好。然后我在gdb中使用 add-symbol-file 命令 我想知道如何找到这个 地址 ? 是否有任何命令或技巧可以找到它? 这个 地址 代表什么? 我知道gdb还有另一个命令 符号文件, 但是它会覆盖以前加载的符号。因此,我必

  • 问题内容: 请告诉我最好/最快的方法: 1)将很小的二进制文件加载到内存中。例如图标; 2)加载/读取大小为512Mb +的非常大的二进制文件。也许我必须使用内存映射的IO? 3)当您不想考虑大小/速度而只能做的事情时,通常选择:将所有字节读入内存? 谢谢!!! 问题答案: 对于内存映射文件,java有一个nio包:Memory Mapped Files 检出小文件的字节流类:字节流 检出较大文件

  • 我试着这样读: 我尝试只打印出字符串(当我创建string str=new string(allBytes,“UTF-8”)时),但我得到的只是开头的一个正方形,然后是70多个没有文本的空行。然后我尝试了int charCode=integer.parseint(str.substring(I,I+8),2);并打印出每个单独的字符,但我得到了一个NumberFormatException。我甚至

  • 问题内容: 我正在尝试将OpenCV与Hadoop一起使用。下面是我的代码。我只是在测试OpenCV库是否可以在Hadoop上正常工作,即当我在Hadoop 函数中运行OpenCV代码时 。 我在互联网上进行搜索,发现了一些在Hadoop中添加OpenCV本机库()的方法。我尝试了一些方法,但是没有用。例如本教程。 它说添加到文件。但这没有用。我收到这个错误 最后,我将OpenCV本机库()添加到

  • 问题内容: 我为dlopen编写了一个程序 但我收到“分段错误”错误,我使用.so库测试了该程序,它可以工作,但无法使其自身运行 问题答案: 您需要编写代码: 仔细阅读dlopen(3),始终检查那里的&函数是否成功,并在失败时使用。 并用编译上面的文件 不要忘记获得所有警告和标志(以便能够将自己的符号放入动态表中)。 在我的Debian / Sid / x86-64系统上(版本4.8.2和版本2

  • 问题内容: 使用带有角的ResponseEntity下载任何文件不起作用 我需要在客户端使用angular下载文件,该文件可以具有pdf或excel或image或txt的任何格式…我的方法仅适用于txt文件,但给我excel和image的失败格式,对于pdf,它会给出一个空的pdf。 所以在我的控制器中,这里是调用service方法的函数: 而我的service.js具有: 我的服务方法是这样的: