直接用 fread 修改 .strtab 会破坏 ELF 文件,因其字符串表被其他节区通过偏移量引用,长度变化或越界写入将导致偏移错位、数据覆盖及符号解析失败。
为什么直接用
修改
会破坏 ELF 文件
ELF 的字符串表(
或
)不是独立文件块,它被其他节区(如
、
)通过偏移量(
、
等字段)引用。直接覆盖字符串内容会导致:长度变化 → 后续字符串偏移错位 → 符号解析失败;或者越界写入 → 覆盖相邻节区数据(比如把
的第一个符号名写爆到下一个符号的
字段里)。这不是“改文本”,是动内存布局。
字符串表必须保持「所有字符串以
结尾 + 整体连续 + 无空洞」,新增字符串只能追加到末尾
修改已有字符串只能等长替换(包括结尾
),否则必须重写整个
并更新所有引用它的节区头和字段
显示的
和
是磁盘映像位置,不是内存加载地址,操作时必须按文件 offset 计算,不能按
后的虚拟地址硬算
如何安全地在 ELF 中新增一个字符串并获取其偏移
新增字符串必须追加到
末尾,并更新节头中
,同时确保文件后续内容(如下一节区)不被覆盖。关键不是“写进去”,而是“让整个 ELF 结构还认得它”。
用
定位到原
末尾(
),再
新字符串(含结尾
)
用
读取原始 ELF 头和节头表,找到
对应的
,将其
增加新字符串长度(含
),再
回原位置
如果新增后导致节头表本身被推移(例如文件末尾扩展触发重排),必须同步更新 ELF 头中
—— 这种情况极少见,但真实存在,尤其当原文件无足够 padding 时
不要用
扩展文件后再写:某些系统对只读 mmap 区域或签名验证过的二进制(如 Android 的 AVB)会拒绝加载已扩容的 ELF
修改符号名时,必须同步更新哪些字段
改符号名不只是动
,符号表(
)里的每个
都有
字段,它是相对于
起始的字节偏移。改名即改这个偏移值,而偏移值取决于你把新字符串放在
的哪个位置。
先确定新字符串在
中的绝对文件偏移:
遍历
中所有
,找到要修改的符号(比如
)
将该符号的
改为新字符串在
中的偏移(不是索引,是文件内 byte offset)
注意:
本身可能被
掉,此时只能改
,逻辑相同,但需确认
是否可写(有些发行版设为
)
用
时最容易忽略的三个细节
封装了大部分结构解析,但底层仍是文件操作,它不会自动帮你处理“改了字符串表就要重算所有引用”的逻辑,更不会保证你写的偏移合法。
C知道
CSDN推出的一款AI技术问答工具
下载
立即学习
“
C++免费学习笔记(深入)
”;
调用
前,必须手动调用
更新每个改过的符号,否则
内存缓存不变,写回文件还是旧的
返回的
指向的是内存副本,直接
写字符串进去没用——得用
/
操作符号,用
+ 偏移操作字符串表原始字节
不等于“可以随便写”:若原 ELF 文件以只读方式打开(
),即使传
,
也会失败并返回
—— 必须用
打开文件描述符
真正难的从来不是“怎么找到字符串表”,而是改完之后,所有依赖它的地方是否还指向有效位置。一个偏移算错,整个符号解析链就断了,loader 会静默跳过符号或直接报
。
fread.strtab.strtab.dynstr.symtab.dynamicst_named_tag.symtabst_value\0\0.strtabreadelf -S.strtabsh_offsetsh_sizemmap.strtabsh_sizelseek.strtabsh_offset + sh_sizewrite\0read.strtabElf64_Shdrsh_size\0pwritee_shoffftruncate.strtab.symtabElf64_Symst_name.strtab.strtab.strtabold_strtab_offset + old_strtab_size.symtabElf64_Symst_name != 0 && strcmp(get_string_at(strtab, sym->st_name), "old_name") == 0st_name.strtab.symtabstrip.dynsym.dynstrPROT_READlibelflibelfelf_update(elf, ELF_C_WRITE)gelf_update_sym.symtabelf_getdatadata->d_bufmemcpygelf_getsymgelf_update_symdata->d_bufelf_begin(..., ELF_C_RDWR, ...)open(path, O_RDONLY)ELF_C_RDWRelf_updateNULLO_RDWRInvalid ELF image