【发布时间】:2017-06-29 23:40:48
【问题描述】:
我编写了一些代码来搜索共享库的 ELF 标头中的符号。如果我解析存储在磁盘上的共享对象文件,该代码就可以工作。
现在,我想使用此代码来解析已加载共享库的 ELF 标头。例如,将 libdl 库映射到当前进程:
b7735000-b7738000 r-xp 00000000 08:01 315560 /lib/i386-linux-gnu/libdl.so.2
b7738000-b7739000 r--p 00002000 08:01 315560 /lib/i386-linux-gnu/libdl.so.2
b7739000-b773a000 rw-p 00003000 08:01 315560 /lib/i386-linux-gnu/libdl.so.2
地址的(第一个)映射包含 ELF 标头。我试图阅读此标题并在 .dynsym 部分中提取 dlopen 符号。但是,标头与磁盘上的“普通”.so 文件略有不同。例如 .shstrtab 版本的偏移量为 0。因此,无法获取节的名称。
我想问一下为什么在加载库的过程中会更改 ELF 标头以及在哪里可以找到“缺失”部分。加载库后是否甚至可以解析 ELF 标头? 有人知道任何文章解释共享库/它的 ELF 标头映射到进程时的布局吗?
目前我正在使用以下函数来迭代 ELF 标头。如果 libdl_start 指向内存映射的 libdl.so.2 文件,则代码可以正常工作。但是,如果它指向链接器映射的区域,get_dynstr_section 不会找到 dynstr 部分。
int get_libdl_functions()
{
Elf32_Ehdr *ehdr = libdl_start;
Elf32_Shdr *shdr, *shdrs_start = (Elf32_Shdr *)(((char *)ehdr) + ehdr->e_shoff);
Elf32_Sym *symbol, *symbols_start;
char *strtab = get_dynstr_section();
int sec_it = 0, sym_it = 0;
rt_info->dlopen = NULL;
rt_info->dlsym = NULL;
if(strtab == NULL)
return -1;
for(sec_it = 0; sec_it < ehdr->e_shnum; ++sec_it) {
// Iterate over all sections to find .dynsym
shdr = shdrs_start + sec_it;
if(shdr->sh_type == SHT_DYNSYM)
{
// Ok we found the right section
symbols_start = (Elf32_Sym *)(((char *)ehdr) + shdr->sh_offset);
for(sym_it = 0; sym_it < shdr->sh_size / sizeof(Elf32_Sym); ++sym_it) {
symbol = symbols_start + sym_it;
if(ELF32_ST_TYPE(symbol->st_info) != STT_FUNC)
continue;
if(strncmp(strtab + symbol->st_name, DL_OPEN_NAME, sizeof DL_OPEN_NAME) && !rt_info->dlopen) {
//printf("Offset of dlopen: 0x%x\n", symbol->st_value);
dlopen = ((char *)ehdr) + symbol->st_value;
} else if(strncmp(strtab + symbol->st_name, DL_SYM_NAME, sizeof DL_SYM_NAME) && !rt_info->dlsym) {
//printf("Offset of dlsym: 0x%x\n", symbol->st_value);
dlsym = ((char *)ehdr) + symbol->st_value;
}
if(dlopen != 0 && dlsym != 0)
return 0;
}
}
}
return -1;
}
void *get_dynstr_section()
{
Elf32_Ehdr *ehdr = libdl_start;
Elf32_Shdr *shdr, *shdrs_start = (Elf32_Shdr *)(((char *)ehdr) + ehdr->e_shoff);
char *strtab = ((char *)ehdr) + ((shdrs_start + ehdr->e_shstrndx))->sh_offset;
int sec_it = 0;
for(sec_it = 0; sec_it < ehdr->e_shnum; ++sec_it) {
// Iterate over all sections to find .dynstr section
shdr = shdrs_start + sec_it;
if(shdr->sh_type == SHT_STRTAB && strncmp(strtab + shdr->sh_name, DYNSTR_NAME, sizeof DYNSTR_NAME))
return ((char *)ehdr) + shdr->sh_offset;
}
return NULL;
}
【问题讨论】:
标签: linux process mapping shared-libraries elf