如何在加载了dlopen的库中覆盖全局符号?

时间:2020-06-18 21:04:14

标签: c linux hook dlopen

涉及3个组成部分

  • main:主程序,加载loader.so
  • loader.so:用-Bsymbolic编译,覆盖puts并加载other.so
  • other.so:调用puts,并且无法修改

如何让other.so使用puts中覆盖的loader.so

请注意,我希望putsloader.so开始(包括other.so)被仅覆盖,主程序应该不受影响

跟随示例代码

main.c

#include <stdio.h>
#include <dlfcn.h>

int main(int argc, char *argv[]){
    dlopen("./loader.so", RTLD_NOW | RTLD_GLOBAL | RTLD_DEEPBIND);
    puts("Normal");
    return 0;
}

loader.c

#include <stdio.h>
#include <dlfcn.h>

extern int puts(const char *s){
    fputs("Hooked: ", stdout);
    fputs(s, stdout);
    fputc('\n', stdout);
    return 0;
}

__attribute__((constructor))
void ctor(void) {
    puts("Something");
    void *other = dlopen("./other.so", RTLD_NOW);
}

other.c

#include <stdio.h>

__attribute__((constructor))
void ctor(void) {
    puts("Hello!");
}

make.sh

#!/bin/bash
gcc main.c -o main -ldl
gcc loader.c -fPIC -shared -Wl,-Bsymbolic -o loader.so
gcc other.c -fPIC -shared -o other.so

所需的输出

Hooked: Something
Hooked: Hello!
Normal

实际输出

Hooked: Something
Hello!
Normal

2 个答案:

答案 0 :(得分:1)

在解决了更多问题之后,我有一个解决方案,需要patchelf的一些外部帮助,因此,如果有其他解决方法,我将等待接受该解决方案。 / p>

此解决方案的工作原理是,使用经过修改的shared.so制作一个新的共享对象puts,如下所示

int puts(const char *s){
    fputs("Hooked: ", stdout);
    fputs(s, stdout);
    fputc('\n', stdout);
    return 0;
}

然后我们需要强制other.so依赖于这个新的共享对象,我们可以使用patchelf --add-needed shared.so other.so

确实涉及对other.so的修改,但是不需要从源头重新编译(这使该方法更可行)。

现在,当我们加载other.so时,我们需要像这样在RTLD_DEEPBIND内指定loader.c

void *other = dlopen("./libother.so", RTLD_NOW | RTLD_GLOBAL | RTLD_DEEPBIND);

,这样搜索顺序就不会从全局上下文开始,而是从库本身开始。 由于other.so未定义puts,因此将查找直接依赖项,并且将在puts中找到shared.so

RTLD_DEEPBIND的属性可确保即使最终的LD_PRELOAD对象也被压倒了。

因此,如果在预加载的共享库中禁用了puts,我们可以解决该问题并从glibc调用真正的,未经修改的puts(并且仅适用于源自other.so的调用)。 / p>

如果我们想要的只是恢复原始行为,我们就不需要patchelfshared.so

答案 1 :(得分:0)

尝试添加标志-Wl,--no-as-needed

gcc loader.c -fPIC -shared -Wl,-Bsymbolic -Wl,--no-as-needed -o loader.so

我成功地从time-machine的C库中挂钩了与时间相关的函数。

相关问题