如何找到PIE二进制文件的负载重定位? [英] How to find load relocation for a PIE binary?

查看:76
本文介绍了如何找到PIE二进制文件的负载重定位?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要获取正在运行的进程内部堆栈的基地址.这将使我能够打印将由addr2line理解的原始堆栈跟踪(已剥离运行二进制文件,但addr2line可以访问符号).我通过检查 argv [0] 的elf标头成功做到了这一点:我读取了入口点,并从& _start :

I need to get base address of stack inside my running process. This would enable me to print raw stacktraces that will be understood by addr2line (running binary is stripped, but addr2line has access to symbols). I managed to do this by examining elf header of argv[0]: I read entry point and substract it from &_start:

#include <stdio.h>
#include <execinfo.h>
#include <unistd.h>
#include <elf.h>
#include <stdio.h>
#include <string.h>
void* entry_point = NULL;
void* base_addr = NULL;
extern char _start;

/// given argv[0] will populate global entry_pont
void read_elf_header(const char* elfFile) {
  // switch to Elf32_Ehdr for x86 architecture.
  Elf64_Ehdr header;
  FILE* file = fopen(elfFile, "rb");
  if(file) {
    fread(&header, 1, sizeof(header), file);
    if (memcmp(header.e_ident, ELFMAG, SELFMAG) == 0) {
        printf("Entry point from file: %p\n", (void *) header.e_entry);
        entry_point = (void*)header.e_entry;
        base_addr = (void*) ((long)&_start - (long)entry_point);
    }
    fclose(file);
  }
}

/// print stacktrace
void bt() {
    static const int MAX_STACK = 30;
    void *array[MAX_STACK];
    auto size = backtrace(array, MAX_STACK);
    for (int i = 0; i < size; ++i) {
        printf("%p ", (long)array[i]-(long)base_addr );
    }
    printf("\n");
}

int main(int argc, char* argv[])
{
    read_elf_header(argv[0]);
    printf("&_start = %p\n",&_start);
    printf("base address is: %p\n", base_addr);
    bt();

    // elf header is also in memory, but to find it I have to already have base address
    Elf64_Ehdr * ehdr_addr = (Elf64_Ehdr *) base_addr;
    printf("Entry from memory: %p\n", (void *) ehdr_addr->e_entry);

    return 0;
}

示例输出:

Entry point from file: 0x10c0
&_start = 0x5648eeb150c0
base address is: 0x5648eeb14000
0x1321 0x13ee 0x29540f8ed09b 0x10ea 
Entry from memory:  0x10c0

然后我可以

$ addr2line -e a.out 0x1321 0x13ee 0x29540f8ed09b 0x10ea
/tmp/elf2.c:30
/tmp/elf2.c:45
??:0
??:?

如何在不访问 argv 的情况下获取基址?我可能需要在 main()(全局变量的初始化)之前打印跟踪.不能选择启用ASLR或PIE.

How can I get base address without access to argv? I may need to print traces before main() (initialization of globals). Turning of ASLR or PIE is not an option.

推荐答案

如何在不访问argv的情况下获取基址?我可能需要在main()之前打印痕迹

How can I get base address without access to argv? I may need to print traces before main()

有几种方法:

  1. 如果已安装/proc (几乎总是如此),则可以从/proc/self/exe 中读取ELF标头.
  2. 您可以使用 dladdr1(),如Antti Haapala的回答所示.
  3. 您可以使用 _r_debug.r_map ,它指向已加载的ELF图像的链接列表.该列表中的第一个条目对应于 a.out ,其 l_addr 包含您要查找的重定位.此解决方案等效于 dladdr1 ,但不需要链接到 libdl .
  1. If /proc is mounted (which it almost always is), you could read the ELF header from /proc/self/exe.
  2. You could use dladdr1(), as Antti Haapala's answer shows.
  3. You could use _r_debug.r_map, which points to the linked list of loaded ELF images. The first entry in that list corresponds to a.out, and its l_addr contains the relocation you are looking for. This solution is equivalent to dladdr1, but doesn't require linking against libdl.

您能否提供3的示例代码?

Could you provide sample code for 3?

确定:

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

extern char _start;
int main()
{
  uintptr_t relocation = _r_debug.r_map->l_addr;
  printf("relocation: %p, &_start: %p, &_start - relocation: %p\n",
         (void*)relocation, &_start, &_start - relocation);
  return 0;
}

gcc -Wall -fPIE -pie t.c && ./a.out
relocation: 0x555d4995e000, &_start: 0x555d4995e5b0, &_start - relocation: 0x5b0

2和3是否都可移植?

Are both 2 and 3 equally portable?

我认为它们同样具有可移植性: dladdr1 是GLIBC扩展,也存在于Solaris上. _r_debug 早于Linux,并且也可以在Solaris上运行(我没有实际检查过,但我相信可以).它也可以在其他ELF平台上使用.

I think they are about equally portable: dladdr1 is a GLIBC extension that is also present on Solaris. _r_debug predates Linux and would also work on Solaris (I haven't actually checked, but I believe it will). It may work on other ELF platforms as well.

这篇关于如何找到PIE二进制文件的负载重定位?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆