如何在 C++ 中找到 ELF 二进制文件所需的动态库?

问题描述 投票:0回答:4

如何使用 C++ 获取 Linux 中 elf 二进制文件所需的所有动态库的列表?

一旦我成功地从二进制文件中提取信息(文件名?),我就可以通过搜索

PATH
找到实际的文件,但我无法找到有关从 ELF 二进制文件中提取未损坏信息的任何信息.

想法?

c++ linux binary elf decompiling
4个回答
13
投票

您可以调用“readelf -d”程序并解析输出:

readelf -d /usr/bin/readelf | grep NEEDED
 0x0000000000000001 (NEEDED)             Shared library: [libz.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

12
投票

所需共享对象的列表存储在可执行文件的所谓“动态部分”中。获取必要信息的粗略算法如下:

解析
    ELF 标头
  1. ,检查该文件是否是动态可执行文件(ET_EXEC
    ET_DYN
    )。
    获取程序头的偏移量和计数 (
  2. e_phoff/e_phnum/e_phentsize
  3. ),检查它们是否非零且有效。
    解析
  4. 程序头
  5. ,寻找PT_DYNAMIC。还要记住
    PT_LOAD
    段的虚拟地址 -> 文件偏移映射。
    找到后,解析
  6. 动态部分
  7. 。查找 DT_NEEDED
    DT_STRTAB
    条目。
    
    
    
d_val

条目的

DT_NEEDED
字段是
DT_STRTAB
字符串表的偏移量,这将是所需库的SONAME。请注意,由于
DT_STRTAB
条目是运行时
address
而不是字符串表的 offset,因此您需要使用步骤 3 中存储的信息将其映射回文件偏移量。


0
投票

libelf

 来执行此操作。请注意,
libelf 有 C API。
从他们的教程

这里

,查看第 4.2 节(或这里)中有关如何获取程序头表的示例。找到 DT_DYNAMIC 部分并从字符串表中读取依赖关系,如第 5.4 节中的示例(或

此处
)。 -- 编辑 --

我实际上有机会编写代码。这是我所做的:

#include <assert.h> #include <fcntl.h> #include <gelf.h> #include <stdio.h> #include <unistd.h> void print_dt_needed(const char *elf_path) { assert(elf_version(EV_CURRENT) != EV_NONE); int fd = open(elf_path, O_RDWR, 0); assert(fd >= 0); Elf *elf = elf_begin(fd, ELF_C_READ, NULL); assert(elf != NULL); assert(elf_kind(elf) == ELF_K_ELF); Elf_Scn *scn = NULL; while ((scn = elf_nextscn(elf, scn)) != NULL) { GElf_Shdr shdr = {}; assert(gelf_getshdr(scn, &shdr) == &shdr); if (shdr.sh_type == SHT_DYNAMIC) { Elf_Data *data = NULL; data = elf_getdata(scn, data); assert(data != NULL); size_t sh_entsize = gelf_fsize(elf, ELF_T_DYN, 1, EV_CURRENT); for (size_t i = 0; i < shdr.sh_size / sh_entsize; i++) { GElf_Dyn dyn = {}; assert(gelf_getdyn(data, i, &dyn) == &dyn); if (dyn.d_tag == DT_NEEDED) { printf("DT_NEEDED detected: %s\n", elf_strptr(elf, shdr.sh_link, dyn.d_un.d_val)); } } } } assert(elf_end(elf) == 0); assert(close(fd) == 0); } int main(int argc, char const *argv[]) { print_dt_needed(argv[1]); return 0; }



0
投票

#include <stdio.h> #include <string.h> #include <elf.h> int print_dependencies(const char *file_name) { Elf64_Ehdr ehdr; Elf64_Shdr shdr, shdr_shstrtab, shdr_dynstr; Elf64_Phdr phdr; Elf64_Dyn dyn; long int oldpos, dynpos; int dyncount; char sname[1000]; FILE *f = fopen(file_name, "r"); if(!f) { return 1; } if(fseek(f, 0, SEEK_SET) != 0) { fclose(f); return 1; } if(fread(&ehdr, sizeof(ehdr), 1, f) <= 0) { fclose(f); return 1; } if(memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { fclose(f); return 1; } if(fseek(f, ehdr.e_shoff + (ehdr.e_shentsize * ehdr.e_shstrndx), SEEK_SET) != 0) { fclose(f); return 1; } if(fread(&shdr_shstrtab, sizeof(shdr_shstrtab), 1, f) <= 0) { fclose(f); return 1; } if(fseek(f, ehdr.e_shoff, SEEK_SET) != 0) { fclose(f); return 1; } for(int i = 0; i < ehdr.e_shnum; i++) { if(fread(&shdr_dynstr, sizeof(shdr_dynstr), 1, f) <= 0) { shdr_dynstr.sh_type = SHT_NULL; break; } if(shdr_dynstr.sh_type == SHT_STRTAB) { oldpos = ftell(f); sname[8] = 0; if(fseek(f, shdr_shstrtab.sh_offset + shdr_dynstr.sh_name, SEEK_SET) == 0) { fgets(sname, 8, f); } if(strcmp(sname, ".dynstr") == 0) { break; } else { shdr_dynstr.sh_type = SHT_NULL; } if(fseek(f, oldpos, SEEK_SET) != 0) { break; } } } if(fseek(f, ehdr.e_shoff, SEEK_SET) == 0) { for(int i = 0; i < ehdr.e_shnum; i++) { if(fread(&shdr, sizeof(shdr), 1, f) <= 0) { break; } if(shdr.sh_type == SHT_DYNAMIC) { oldpos = ftell(f); dyncount = shdr.sh_size / shdr.sh_entsize; if(fseek(f, shdr.sh_offset, SEEK_SET) == 0) { for(int i = 0; i < dyncount; i++) { if(fread(&dyn, sizeof(dyn), 1, f) <= 0 || dyn.d_tag == DT_NULL) { break; } if(dyn.d_tag == DT_NEEDED) { dynpos = ftell(f); if(fseek(f, shdr_dynstr.sh_offset + dyn.d_un.d_val, SEEK_SET) == 0) { sname[sizeof(sname) - 1] = 0; fgets(sname, sizeof(sname) - 1, f); printf("shdr.sh_type is SHT_DYNAMIC, dyn.d_tag is DT_NEEDED: %s\n", sname); } if(fseek(f, dynpos, SEEK_SET) != 0) { break; } } } } if(fseek(f, oldpos, SEEK_SET) != 0) { break; } } } } if(fseek(f, ehdr.e_phoff, SEEK_SET) == 0) { for(int i = 0; i < ehdr.e_phnum; i++) { if(fread(&phdr, sizeof(phdr), 1, f) <= 0) { break; } if(phdr.p_type == PT_DYNAMIC) { oldpos = ftell(f); dyncount = phdr.p_filesz / sizeof(dyn); if(fseek(f, phdr.p_offset, SEEK_SET) == 0) { for(int i = 0; i < dyncount; i++) { if(fread(&dyn, sizeof(dyn), 1, f) <= 0 || dyn.d_tag == DT_NULL) { break; } if(dyn.d_tag == DT_NEEDED) { dynpos = ftell(f); if(fseek(f, shdr_dynstr.sh_offset + dyn.d_un.d_val, SEEK_SET) == 0) { sname[sizeof(sname) - 1] = 0; fgets(sname, sizeof(sname) - 1, f); printf("phdr.p_type is PT_DYNAMIC, dyn.d_tag is DT_NEEDED: %s\n", sname); } if(fseek(f, dynpos, SEEK_SET) != 0) { break; } } } } if(fseek(f, oldpos, SEEK_SET) != 0) { break; } } } } fclose(f); return 0; } int main(int argc, char* argv[]) { if(argc > 1) { print_dependencies(argv[1]); } return 0; }

这是一个C代码,我试图让它处理所有错误并且不会导致任何内存泄漏,fgets可以被替换为逐字节读取并仅分配所需字节数以节省一些内存的东西。您可以使用基于 malloc 和 realloc 的数组来存储调用函数要使用的 sname。如果需要的话也可以使用C++,那么你可以使用C++向量以方便的方式存储sname。此代码广泛使用 fseek,如果您不想多次查找和读取文件,可以将文件缓存到内存中并从内存中处理它,或者只缓存文件的相关部分。请注意,如果您需要知道运行应用程序所需的所有文件(如 ldd 那样),则需要在其内部递归地使用 print_dependency ,获取依赖项的依赖项等。

© www.soinside.com 2019 - 2024. All rights reserved.