必须确认.dynamic段所在PT_LOAD段具有可写权限(RW标志)且绕过RELRO保护,否则直接修改会失败;推荐用patchelf安全增删DT_NEEDED条目,它自动处理字符串表扩展、动态数组更新和段对齐。

用 readelf 和 objdump 确认当前 DT_NEEDED 条目是否可写
ELF 文件的动态依赖列表存在 .dynamic 段中,关键项是 DT_NEEDED。但直接修改二进制文件前必须确认:该段是否在只读段(PT_LOAD 的 PF_R 但无 PF_W)里?否则写入会失败或被内核拒绝加载。
实操建议:
- 运行
readelf -l your_binary | grep -A2 "LOAD.*RW",找带RW标志的PT_LOAD段 ——.dynamic必须落在其中,否则不能安全 patch - 用
readelf -d your_binary | grep NEEDED查看原始依赖,记下每个DT_NEEDED字符串在.dynstr中的偏移 - 注意:某些发行版(如 Alpine)默认启用
PT_GNU_RELRO,即使段有写权限,运行时也会被 mmap 设为只读 —— 此时必须先patchelf --remove-needed再重加,不能手改
用 patchelf 安全增删 DT_NEEDED 条目
patchelf 是目前最可靠的用户态 ELF 依赖修改工具,它自动处理字符串表增长、重定位、段对齐和 RELRO 适配,避免手动计算偏移出错。
常见错误现象:直接用 hex 编辑器改 .dynstr 后程序启动报 cannot open shared object file 或 Segmentation fault —— 多半是字符串末尾没填 \0、.dynamic 数组项没同步更新、或新字符串超出原 .dynstr 段大小。
实操建议:
- 删依赖:
patchelf --remove-needed libold.so your_binary - 加依赖(追加到末尾):
patchelf --add-needed libnew.so your_binary - 替换依赖(先删后加,顺序不能反):
patchelf --remove-needed libfoo.so --add-needed libbar.so your_binary - 不支持“原地替换字符串”,因为
libfoo.so和libbar.so长度不同,必须重分配空间 ——patchelf内部会挪动.dynstr并修正所有引用
手写 C++ 修改需绕过 PT_GNU_RELRO 且重算 .dynamic 数组
如果非得用 C++ 自己读写 ELF(比如嵌入式环境无 patchelf),核心难点不在改字符串,而在维护 .dynamic 段结构一致性:每个 DT_NEEDED 对应一个 Elf64_Dyn 结构体,数组长度、d_tag 值、d_un.d_ptr 指向的字符串偏移都必须精确匹配。
性能与兼容性影响:
- 修改后若未调用
elf_update(elf, ELF_C_NULL)(libelf)或未重写e_shoff/e_phoff,glibc 的ld-linux可能因校验失败拒绝加载 - 64 位 ELF 的
DT_NEEDED条目占 16 字节,字符串存于.dynstr;若新增依赖名比原字符串长,必须扩展.dynstr段并更新其sh_size,否则越界读取 - 几乎所有现代 Linux 发行版默认开启 RELRO,所以 C++ 程序必须先用
mmap以PROT_READ|PROT_WRITE映射整个文件,再调用mprotect临时解除 RELRO 保护(仅限调试,生产环境极不推荐)
修改后必须验证 ldd 输出与实际加载行为一致
ldd your_binary 只解析 DT_NEEDED,不模拟真实加载路径;而 LD_DEBUG=libs 才反映运行时行为。两者不一致说明修改未生效或被缓存。
容易踩的坑:
-
ldd在容器内可能显示宿主机的库路径,误判修改结果 —— 应在目标环境中跑LD_DEBUG=libs ./your_binary 2>&1 | grep "trying file" - 修改后的二进制若被 strip 过,
.dynstr段可能被删,导致DT_NEEDED的d_un.d_ptr指向非法地址 ——readelf -d会报corrupt,此时无法修复,只能重链接 - Android 的 bionic 不支持部分
DT_*标签,改完在 Android 上跑logcat | grep linker可能发现unknown DT tag—— 说明工具链版本不匹配
真正麻烦的从来不是改几个字节,而是让 loader 相信你改得既合法又自洽。哪怕只动一个 DT_NEEDED,也要同步更新至少三处元数据:字符串表内容、.dynamic 数组项、以及段头里的尺寸字段。漏任何一环,程序就停在 _dl_start 上。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











