gpt4 book ai didi

c - 如何找到 PIE 二进制文件的加载重定位?

转载 作者:IT王子 更新时间:2023-10-29 01:00:00 26 4
gpt4 key购买 nike

我需要获取正在运行的进程中堆栈的基地址。这将使我能够打印 addr2line 可以理解的原始堆栈跟踪(运行二进制文件被剥离,但 addr2line 可以访问符号)。我设法通过检查 argv[0] 的 elf header 来做到这一点:我读取入口点并将其从 &_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()

有几种方式:

  1. 如果安装了 /proc(几乎总是这样),您可以从 /proc/self/exe 读取 ELF header 。
  2. 您可以使用 dladdr1(),如 Antti Haapala 的回答所示。
  3. 您可以使用_r_debug.r_map,它指向加载的ELF 图像的链表。该列表中的第一个条目对应于 a.out,其 l_addr 包含您要查找的重定位。此解决方案等效于 dladdr1,但不需要针对 libdl 进行链接。

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

Are both 2 and 3 equally portable?

我认为它们的可移植性差不多:dladdr1 是 GLIBC 扩展,也存在于 Solaris 上。 _r_debug 早于 Linux,也可以在 Solaris 上运行(我还没有实际检查过,但我相信它会)。它也可以在其他 ELF 平台上运行。

关于c - 如何找到 PIE 二进制文件的加载重定位?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55066749/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com