加载时链接期间的符号地址与Linux中的运行时链接

Apo*_*vaJ 13 c c++ linux gcc dynamic-linking

我试图了解Linux中动态库的加载时间链接(使用gcc -l)与运行时链接(使用dlopen(), dlsym())的机制差异,以及这些机制如何影响库的状态及其符号的地址.

本实验

我有三个简单的文件:

libhello.c:

int var;
int func() {
    return 7;
}
Run Code Online (Sandbox Code Playgroud)

libhello.h:

extern int var;
int func();
Run Code Online (Sandbox Code Playgroud)

main.c中:

#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
#include "libhello.h"

int main() {
    void* h = dlopen("libhello.so", RTLD_NOW);
    printf("Address  Load-time linking    Run-time linking\n");
    printf("-------  -----------------    ----------------\n");
    printf("&var     0x%016" PRIxPTR "   0x%016" PRIxPTR "\n", (uintptr_t)&var , (uintptr_t)dlsym(h, "var" ));
    printf("&func    0x%016" PRIxPTR "   0x%016" PRIxPTR "\n", (uintptr_t)&func, (uintptr_t)dlsym(h, "func"));
}
Run Code Online (Sandbox Code Playgroud)

我用命令编译libhello.c gcc -shared -o libhello.so -fPIC libhello.c

我用命令编译main.c. gcc main.c -L. -lhello -ldl

观察

运行main.c可执行文件打印如下:

Address  Load-time linking    Run-time linking
-------  -----------------    ----------------
&var     0x0000000000601060   0x00007fdb4acb1034
&func    0x0000000000400700   0x00007fdb4aab0695
Run Code Online (Sandbox Code Playgroud)

加载时链接地址保持不变,但运行时链接地址会在每次运行时发生变化.

问题

  1. 为什么运行时地址每次运行都会改变?它们是否因地址空间布局随机化而改变?
  2. 如果是这种情况,为什么地址不会改变加载时链接?加载时链接是否容易受到针对随机化旨在防范的相同攻击?
  3. 在上面的程序中,相同的库被加载两次 - 一次在加载时,然后在运行时使用dlopen().第二个加载不会复制第一个加载的状态.即,如果var之前更改了值,则dlopen()此值不会反映在已var加载的via 的版本中dlsym().有没有办法在第二次加载时保持这种状态?

Art*_*Art 13

  1. 是的,这是ASLR.

  2. 因为PIE(位置无关可执行文件)非常昂贵(性能).如此多的系统在权衡库中进行权衡,因为它们无论如何都必须是位置独立的,但不要随机化可执行文件,因为它会花费太多的性能.是的,这种方式更容易受到攻击,但大多数安全性都是一种权衡.

  3. 是的,不要通过手柄搜索符号,而是使用RTLD_DEFAULT.像这样加载相同动态库的两个实例通常是个坏主意.如果某些系统dlopen知道已加载了相同的库,并且动态链接器认为"同一个库"可​​能会根据库路径发生变化,则可以跳过加载库.你在这里的行为非常严重/弱定义,这些行为多年来已经发展到更多来处理错误和问题,而不是通过深思熟虑的设计.

请注意,RTLD_DEFAULT将返回主可执行文件中的符号地址或第一个(加载时间)加载的动态库,并且将忽略动态加载的库.

此外,值得记住的另一件事是,如果您var在libhello中引用它将始终从库的加载时版本解析符号,即使在dlopen:ed版本中也是如此.我修改func为返回var并将此代码添加到您的示例代码中:

int (*fn)(void) = dlsym(h, "func");
int *vp;

var = 17;
printf("%d %d %d %p\n", var, func(), fn(), vp);

vp = dlsym(h, "var");
*vp = 4711;
printf("%d %d %d %p\n", var, func(), fn(), vp);

vp = dlsym(RTLD_DEFAULT, "var");
*vp = 42;
printf("%d %d %d %p\n", var, func(), fn(), vp);
Run Code Online (Sandbox Code Playgroud)

得到这个输出:

$ gcc main.c -L. -lhello -ldl && LD_LIBRARY_PATH=. ./a.out
17 17 17 0x7f2e11bec02c
17 17 17 0x7f2e11bec02c
42 42 42 0x601054
Address  Load-time linking    Run-time linking
-------  -----------------    ----------------
&var     0x0000000000601054   0x0000000000601054
&func    0x0000000000400700   0x0000000000400700
Run Code Online (Sandbox Code Playgroud)