我正在尝试获取 ELF 二进制文件的加载地址,但dlopen
无法按预期工作:
void *elf = (char *)dlopen (0, RTLD_NOW);
printf ("%pn", 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("%pn", 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%zxn",
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
因运行而异(您应该这样做))。