ELF解析中读取.symtab段符号时所有st_name字段均为0的问题
异常原因定位
核心错误1:数组长度计算完全错误
你定义段头表数组时,混淆了「总字节数」和「数组元素个数」:
- 你计算的
shdr_tab_size是段头表的总字节数(每个段头大小 × 段数量) - 但
Elf64_Shdr shdr_tab[shdr_tab_size]要求的参数是元素个数,也就是段数量本身e_shnum - 这会导致栈上分配的数组大小是预期的
sizeof(Elf64_Shdr)倍,轻则内存浪费,重则直接栈溢出,后续所有段头、符号表的读取结果都是内存垃圾,直接导致st_name全为0。
核心错误2:变量类型选择溢出
你用16位无符号类型Elf64_Half存储段头表总大小,一旦总大小超过65535(普通ELF文件很容易达到),就会发生截断,得到错误的数组长度,进一步破坏内存布局。
其他次要错误
- 所有
fread调用未检查返回值,无法确认是否真的读取到了预期数量的字节 - 符号表条目数写死为10,未通过规范要求的
sh_size / sh_entsize计算 - 除
shdr_names外,其余malloc分配的内存未做空指针检查,分配失败时会直接访问非法内存 - 段头表用栈上可变长度数组(VLA)分配,栈空间通常仅几MB,极易触发栈溢出
修正后的代码实现
// 前置要求:已正确读取并校验ELF头ehdr的合法性 Elf64_Xword shdr_total_size = (Elf64_Xword)ehdr->e_shentsize * ehdr->e_shnum; // 用堆分配替代栈VLA,避免栈溢出 Elf64_Shdr *shdr_tab = malloc(shdr_total_size); if (!shdr_tab) { fclose(infos.fp); exit(EXIT_FAILURE); } // 读取段头表 rewind(infos.fp); fseek(infos.fp, ehdr->e_shoff, SEEK_SET); size_t read_cnt = fread(shdr_tab, 1, shdr_total_size, infos.fp); if (read_cnt != shdr_total_size) { free(shdr_tab); fclose(infos.fp); exit(EXIT_FAILURE); } // 读取段字符串表(兼容段数超限的边缘规范场景) Elf64_Word shstrtab_idx = ehdr->e_shstrndx; if (shstrtab_idx == SHN_XINDEX) { shstrtab_idx = shdr_tab[0].sh_link; } Elf64_Shdr *shstrtab_shdr = &shdr_tab[shstrtab_idx]; char *shdr_names = malloc(shstrtab_shdr->sh_size); if (!shdr_names) { free(shdr_tab); fclose(infos.fp); exit(EXIT_FAILURE); } fseek(infos.fp, shstrtab_shdr->sh_offset, SEEK_SET); read_cnt = fread(shdr_names, 1, shstrtab_shdr->sh_size, infos.fp); if (read_cnt != shstrtab_shdr->sh_size) { free(shdr_names); free(shdr_tab); fclose(infos.fp); exit(EXIT_FAILURE); } // 定位符号表段及关联的字符串表 Elf64_Shdr *shdr_symtab = NULL, *shdr_strtab = NULL; for (Elf64_Word i = 0; i < ehdr->e_shnum; i++) { if (shdr_tab[i].sh_type == SHT_SYMTAB) { shdr_symtab = &shdr_tab[i]; // 直接通过sh_link获取关联strtab,避免多strtab场景下查找错误 shdr_strtab = &shdr_tab[shdr_symtab->sh_link]; break; } } if (!shdr_symtab || shdr_symtab->sh_size == 0) { free(shdr_names); free(shdr_tab); fclose(infos.fp); exit(EXIT_SUCCESS); } // 读取符号表 Elf64_Sym *symtab = malloc(shdr_symtab->sh_size); if (!symtab) { free(shdr_names); free(shdr_tab); fclose(infos.fp); exit(EXIT_FAILURE); } fseek(infos.fp, shdr_symtab->sh_offset, SEEK_SET); read_cnt = fread(symtab, 1, shdr_symtab->sh_size, infos.fp); if (read_cnt != shdr_symtab->sh_size) { free(symtab); free(shdr_names); free(shdr_tab); fclose(infos.fp); exit(EXIT_FAILURE); } // 读取关联的字符串表 char *strtab = malloc(shdr_strtab->sh_size); if (!strtab) { free(symtab); free(shdr_names); free(shdr_tab); fclose(infos.fp); exit(EXIT_FAILURE); } fseek(infos.fp, shdr_strtab->sh_offset, SEEK_SET); read_cnt = fread(strtab, 1, shdr_strtab->sh_size, infos.fp); if (read_cnt != shdr_strtab->sh_size) { free(strtab); free(symtab); free(shdr_names); free(shdr_tab); fclose(infos.fp); exit(EXIT_FAILURE); } // 按规范计算符号条目数,替代硬编码的10 size_t sym_count = shdr_symtab->sh_size / shdr_symtab->sh_entsize; for (size_t i = 0; i < sym_count; i++) { // st_name为0对应空符号,直接跳过 if (symtab[i].st_name == 0) continue; printf("%s\n", strtab + symtab[i].st_name); } // 释放所有资源 free(strtab); free(symtab); free(shdr_names); free(shdr_tab); fclose(infos.fp);
内容的提问来源于stack exchange,提问作者Sad Adl
相关产品推荐
相关产品推荐

