ELF节表中sh_name是索引而非字符串,需通过.shstrtab查表获取真实节名;修改节表必须重写整个文件并修正所有相关偏移,否则破坏ELF结构。

不能直接用标准 C++ 读写 ELF 节表——得靠 libelf 或手撸解析,否则会破坏二进制结构或读错字段。
为什么 fread 读出的 sh_name 是乱码或 0
ELF 节表项(Elf64_Shdr)里的 sh_name 不是字符串,而是指向节头字符串表(.shstrtab)的索引。直接打印它,等于在内存里瞎指一个地址。
- 先定位
.shstrtab节:查节表中sh_type == SHT_STRTAB且sh_flags & SHF_STRINGS,通常它是节表里第一个或最后一个字符串表 - 再用
sh_name偏移去读.shstrtab数据区,才能拿到真实节名 - 手撸时注意:
sh_name是uint32_t(即使在 64 位 ELF 中),别用size_t强转
修改节表必须重写整个 ELF 文件,不能只改节表段
ELF 文件不是“节表 + 内容”的松散拼接。节表位置、大小、对齐、各节 sh_offset 都相互依赖;改一个 sh_size 或挪动节表位置,所有后续节的 sh_offset、程序头(Program Header)里的 p_offset 都可能失效。
- 安全做法:用
libelf的elf_update(elf, ELF_C_WRITE),它会自动重排布局、修正偏移 - 手撸风险极高:哪怕只把
.data节的sh_size加 4,也得确保不踩到下一节、不越界文件末尾、不破坏shstrtab字符串结尾的\0 - 不要尝试用
mmap+ 原地修改:ELF 校验(如readelf -h)会失败,ld和loader可能拒绝加载
libelf 修改节标志(sh_flags)的最小可行步骤
比如想让 .rodata 可写(仅用于调试/注入场景),核心是改 sh_flags 并触发重写。
立即学习“C++免费学习笔记(深入)”;
- 用
elf_begin(fd, ELF_C_RDWR, NULL)打开,别用ELF_C_READ - 遍历节表:
elf_getscn(elf, i)→gelf_getshdr(scn, &shdr)→ 检查shdr.sh_name对应节名是否为".rodata" - 改
shdr.sh_flags |= SHF_WRITE,再用gelf_update_shdr(scn, &shdr)提交 - 最后必须调
elf_update(elf, ELF_C_WRITE),否则磁盘文件不变 - 注意:改完后
readelf -S显示的Flags列会多一个W,但运行时是否生效还取决于程序头(PT_LOAD)的p_flags—— 这俩不联动,得单独处理
真正难的不是读节名,而是改完之后整套结构还能被内核 loader 正确映射。哪怕 sh_flags 改对了,如果对应 PT_LOAD 段没开 PF_W,mmap 时仍会报 Permission denied。这种跨层约束,文档里很少提,但一踩就崩。



















