为什么动态加载PIE在glibc中不再起作用? 展览1 图2

问题描述

注意::声称的dupe不会以任何方式回答此问题;特别是,可以通过使用-fPIC进行编译来轻松地修复线程局部示例,就像下面已经提到的bugzilla示例一样。其余只是未征求的意见和毫无根据的主张。


在最近的Linux系统(如Debian 10,RHEL 8等)中,您可以创建一个ELF文件,该文件既是位置独立的可执行文件(PIE),又是动态加载的库/共享对象。

这非常有用,并且有很多应用程序,例如创建包装器程序,这些程序预加载自身,然后执行另一个程序(请参见下面的图2 ),或者将虚拟机/语言环境作为单个对象,可以嵌入或嵌入作为独立的解释器运行。所有这些都无需处理固定的安装目录,symlink漏洞,$ORIGIN或其他此类安全性和文件系统策略的噩梦。

但是,在glibc中更改2c75b54会破坏它:

elf:拒绝dlopen PIE对象[BZ#24323]

另一个可执行文件已经被映射,因此动态链接器 无法为第二个可执行文件正确执行重定位

还在discussion中声称导致该事实:

也没有办法正确执行第二个可执行文件的ELF构造器

但这似乎是伪造的。如下面的图1 所示,构造函数,重定位和线程局部变量似乎可以正常工作。

复制重定位不起作用,但是是否有任何理由还要中断那些使用复制重定位的程序,如使用-fPIC? (特别是,只需使用-fPIC进行编译,即可轻松修复testcase中的discussion。)

此更改在FreeBSD 12.2中也为picked,给出的主要原因是“ glibc也这样做”。它仍然可以在NetBSD 9.1和OpenBSD 6.8中使用(尽管构造函数在OpenBSD中不能使用)。

那么,以这种方式使用PIE不起作用的技术原因是什么?清除这种情况的清晰场景(请参阅图1和图2中的挑战)会很好。

展览1

直接执行 a) b) dl加载为共享库并通过其{{1 }}通过libexe来实现。

面临的挑战是展示main可以使用的功能,这些功能会阻止 a) b)或使其工作方式不同,无法轻易修复。

loader

图2

这个小程序会自动加载,然后运行另一个可执行文件。与典型的libexe不同,它在Linux上也可以通过fexecve / execveat(AT_EMPTY_PATH)正常运行,就像在执行memfd_create d文件时一样。经过测试可在Debian> = 9,Centos / RHEL> = 7等上运行。

面临的挑战是演示一个可执行文件,当以这种方式执行链时,该可执行文件将失败,但是当预加载的代码位于单独的库中时(如stdbuf/libstdbuf.so),该可执行文件将正常工作。

cat <<'EOT' > libexe.c
#include <stdio.h>
#include <errno.h>
#include <err.h>

__thread int var;
void set_errno(int e){ errno = e; }

__attribute__((weak))
int main(void){
        set_errno(EPIPE); warn("%s var=%d",__FILE__,var);
}
__attribute__((constructor))
static void init(void){
        var = 33;
        fprintf(stderr,"%s's constructor\n",__FILE__);
}
EOT

cat <<'EOT' > loader.c
#include <dlfcn.h>
#include <stdio.h>
#include <err.h>
#include <errno.h>

int main(void){
        void *dl; char *lib = "./libexe";

        if(!(dl = dlopen(lib,RTLD_LAZY)))
                errx(1,"dlopen: %s",dlerror());
        printf("var=%d in %s\n",*(int*)dlsym(dl,"var"),__FILE__);
        ((void(*)(int))dlsym(dl,"set_errno"))(EBADF); warn("%s",__FILE__);
        return ((int(*)(void))dlsym(dl,"main"))();
}
__attribute__((constructor))
static void init(void){
        fprintf(stderr,__FILE__);
}
EOT

cc -pie -fPIC -Wl,-E libexe.c -o libexe
cc loader.c -o loader -ldl

############
$ ./loader
loader.c's constructor
libexe.c's constructor
var=33 in loader.c
loader: loader.c: Bad file descriptor
loader: libexe.c var=33: Broken pipe

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)