You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LD_PRELOAD能否替换通过dlopen()/dlsym()解析的函数?

关于LD_PRELOAD拦截dlsym解析函数的问题解答

你的猜测完全正确——默认情况下,LD_PRELOAD确实无法拦截通过dlsym()直接从指定共享库句柄中解析的函数。下面我会详细解释原因,以及给出可行的解决办法:

为什么默认情况下拦截失败?

LD_PRELOAD的工作原理是让动态链接器优先加载预加载库中的符号,覆盖其他库中的同名符号,但这个优先级只作用于常规的全局符号解析流程(比如程序直接调用未定义的符号,或者动态链接器自动解析依赖库的符号)。

而dlsym(handle, "func1")是直接从你传入的liba.so句柄对应的符号表中查找符号,完全跳过了全局符号的查找逻辑。动态链接器不会因为LD_PRELOAD有同名符号就替换这个直接查找的结果,所以你的libb.so里的func1()不会被调用。

可行的解决方案

方案1:修改liba.so,将func1设为弱符号

如果你有权限修改liba.so的源代码或编译流程,可以把func1()定义为弱符号,这样LD_PRELOAD中的强符号就能覆盖它。

具体操作:

  • 在liba.so的func1()定义前添加__attribute__((weak))属性:
    __attribute__((weak)) void func1() {
        // 原函数实现
    }
    
  • 重新编译liba.so。

此时,当程序通过dlsym()查找func1时,动态链接器会优先使用全局符号表中LD_PRELOAD提供的强符号(即libb.so的func1()),而不是liba.so里的弱符号。

方案2:拦截dlopen()和dlsym()本身(无需修改原程序或liba.so)

如果无法修改liba.so或原程序,最通用的方法是在libb.so中重新实现dlopen()和dlsym(),拦截对liba.so中func1()的查找请求,替换成自己的函数实现。

实现步骤:

  1. 编写libb.c代码,拦截两个函数并保存原函数指针:

    #define _GNU_SOURCE
    #include <dlfcn.h>
    #include <stdio.h>
    #include <string.h>
    
    // 保存原函数的指针,避免递归调用
    void* (*original_dlopen)(const char*, int) = NULL;
    void* (*original_dlsym)(void*, const char*) = NULL;
    
    // 我们要替换的func1实现
    void func1() {
        printf("[HOOKED] This is func1 from libb.so\n");
    }
    
    // 拦截dlopen
    void* dlopen(const char* filename, int flags) {
        // 初始化原函数指针(只执行一次)
        if (!original_dlopen) {
            original_dlopen = dlsym(RTLD_NEXT, "dlopen");
            if (!original_dlopen) {
                fprintf(stderr, "Failed to get original dlopen: %s\n", dlerror());
                return NULL;
            }
        }
        // 可选:打印调试信息
        printf("Intercepted dlopen for: %s\n", filename);
        return original_dlopen(filename, flags);
    }
    
    // 拦截dlsym
    void* dlsym(void* handle, const char* symbol) {
        if (!original_dlsym) {
            original_dlsym = dlsym(RTLD_NEXT, "dlsym");
            if (!original_dlsym) {
                fprintf(stderr, "Failed to get original dlsym: %s\n", dlerror());
                return NULL;
            }
        }
        // 判断是否是查找liba.so中的func1
        if (symbol && strcmp(symbol, "func1") == 0) {
            printf("Hooking func1 request\n");
            return (void*)func1;
        }
        // 其他符号请求转发给原dlsym
        return original_dlsym(handle, symbol);
    }
    
  2. 编译libb.so:

    gcc -shared -fPIC -ldl libb.c -o libb.so
    
  3. 运行程序时预加载libb.so:

    LD_PRELOAD=./libb.so ./your_target_program
    

注意事项:

  • RTLD_NEXT是GNU扩展,需要定义_GNU_SOURCE才能使用,它用于获取当前库之后加载的原函数指针,避免递归调用。
  • 如果程序使用liba.so的绝对路径调用dlopen(),你可能需要在dlopen拦截逻辑中匹配完整路径(比如/usr/lib/liba.so)。
  • 这种方法可以拦截所有通过dlsym()查找func1的请求,无论目标库是谁,如果你需要精准匹配liba.so的句柄,可以额外通过dladdr()等函数判断句柄对应的库路径。

内容的提问来源于stack exchange,提问作者Cillian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 06:34:27