Linux下无需重编译,如何覆盖静态库链接的可执行文件中的方法?
可行方案及实现步骤
针对你在Linux平台下的需求,无需重编译App1即可替换静态链接的DoSmth()函数是可行的,以下是几种实操方案:
方案1:符号重定向+动态库预加载
静态链接的可执行文件中,全局符号默认对外可见(除非编译时指定-fvisibility=hidden),我们可以通过修改符号表实现劫持:
- 第一步:用
objcopy修改App1的符号表,将原DoSmth重命名为备用名:objcopy --redefine-sym DoSmth=DoSmth_original App1 - 第二步:将Lib2编译为动态库,确保导出
DoSmth函数:gcc -fPIC -shared lib2.c -o libLib2.so - 第三步:创建App2,通过预加载Lib2启动修改后的App1。App2的代码示例(C语言):
编译App2后运行,App1会自动调用Lib2中的#include <unistd.h> #include <stdlib.h> int main() { setenv("LD_PRELOAD", "./libLib2.so", 1); execl("./App1", "App1", NULL); return 0; }DoSmth。
注意:如果App1编译时使用了
-fvisibility=hidden或符号被优化为局部,此方案失效。
方案2:Ptrace运行时指令劫持
通过ptrace系统调用跟踪App1进程,修改其指令流实现函数替换,适合无法修改App1二进制的场景:
- 核心思路:App2作为父进程启动App1,附加到目标进程后,找到
DoSmth的内存地址,将该地址的开头指令修改为跳转指令,指向Lib2中DoSmth的入口(需先将Lib2注入到App1的进程空间)。 - 关键步骤:
- 编写Lib2的注入逻辑:通过远程调用
dlopen将Lib2加载到App1进程(可借助ptrace写入系统调用参数并执行)。 - 用
objdump -d App1定位DoSmth的起始地址。 - 替换该地址的指令为跳转指令(比如x86_64平台的
jmp指令),指向Lib2中DoSmth的地址。
- 编写Lib2的注入逻辑:通过远程调用
注意:此方案依赖CPU架构,需要处理内存权限、指令长度匹配等问题,实现复杂度较高。
方案3:二进制文件直接替换函数段
直接修改App1的二进制内容,替换原DoSmth的指令:
- 第一步:用
objdump -d App1找到DoSmth函数的二进制指令在文件中的偏移量和长度。 - 第二步:将Lib2的
DoSmth编译为位置无关代码,提取其原始二进制指令:gcc -c -fPIC lib2.c -o lib2.o objcopy -O binary --only-section=.text lib2.o do_smth.bin - 第三步:用
dd命令将do_smth.bin的内容写入App1的对应偏移位置:dd if=do_smth.bin of=App1 bs=1 seek=<原DoSmth的文件偏移> conv=notrunc
注意:必须保证Lib2的
DoSmth指令长度不超过原函数的长度,否则会破坏相邻代码导致崩溃;若原函数存在重定位信息,此方案无法直接使用。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

