Skip to main content

Linux下的动态链接机制

· 6 min read
Muel - Nova

摸了一个月,终于开始整PWN辣


动态链接和静态链接

在生成可执行文件1时,通常要经过编译、链接两个阶段。动态链接静态链接即在链接阶段使用不同方法的两种机制。

静态链接

在多个源文件中,由于每个源文件独立编译(意即每个.c文件会生成一个.o文件),则需要将这些目标文件进行链接,从而形成一个可执行文件。这个过程便称为静态链接。

the process of static linking

链接完成后,这些静态库2的内容就以整合到了可执行文件当中,又或者以链接过程中确定的静态内存偏移量加载到该可执行文件的地址空间中。这也导致通常情况下静态链接生成的可执行文件会比动态链接生成的文件更大。

当程序(可执行文件或库)被装载到内存中时,静态变量便会被存储在程序地址空间的数据段(已初始化)3或bss段4(未初始化)中。

优点

  • 避免了依赖问题
  • 允许应用程序包含在单个可执行文件中,简化分发与安装
  • 运行速度快

缺点

  • 更新维护困难(因为每次更新或维护都需要重新链接、用户获取更新也需要整个程序重新下载)
  • 浪费空间(每个可执行文件中都会有其所需要的函数的副本)

动态链接

动态链接主要便是为了解决静态链接的两个缺点而出现的

动态链接的思想就是在程序运行时才将程序模块链接到一起形成一个完整的程序。在链接阶段,它只对未引用的符号做了标识,并生成额外的代码片段(即PLT表),直到程序运行时才进行符号重定向。不同系统对动态链接的实现不同,你可以在维基百科下的Dynamic Linker处找到更多,目前我们将更多关注Unix-like System下的动态链接。

对于动态链接的详细过程,你可以阅读参考中的聊聊Linux动态链接中的PLT和GOT(1)——何谓PLT与GOT

优点

  • 更新维护较容易
  • 节省空间

缺点

  • ​ 运行性能略低于静态链接

GOT & PLT

GOT

全局偏移表5,它将符号映射到相应的绝对内存地址

PLT

过程链接表6,它将函数映射到相应的绝对内存地址

原文如下

The global offset table converts position-independent address calculations to absolute locations.

Similarly the procedure linkage table converts position-independent function calls to absolute locations.

简要的说,PLT处的代码是这样工作的:跳转到对应的GOT表寻找所要执行函数的实际地址。若*.got.plt节中没有所需要的地址时,则通过链接器找到该函数并填充其地址至.got.plt*节中,再跳转到该处执行。

the process of PLT and GOT

这是简化的示意图。

当执行function@plt时,程序首先执行jmp [function@got.plt]

function@got.pltfunction未被调用前的内容是[function@plt+4],也就是说,在未执行该函数前,jmp [function@got.plt]实际上只是跳转到了下一行push 0xX

这里的0xX其实就是GOT表中的下标位置,例如,我们假设functionplt[1],那它所对应的X就是3,即push 0x3

之后它执行jmp plt[0]

这里的plt[0]的原理我们不在细挖,只需要知道,它找到了链接器,利用GOT[1]GOT[2]将函数function的实际地址存到了对应的function@got.plt处,并执行该函数返回。

则第二次执行function@plt时,jmp [function@got.plt]即跳转到了函数的实际地址

这也是我们能够通过GOT泄露而获取libc偏移的理论依据

参考

聊聊Linux动态链接中的PLT和GOT(1)——何谓PLT与GOT

深入浅出静态链接和动态链接

彻底搞清楚GOT和PLT

GOT表和PLT表知识详解

注释

Footnotes

  1. Executable File

  2. Static Library

  3. Data Segment

  4. .BSS

  5. Global Offset Table

  6. Procedure Linkage Table

Loading Comments...