怎么深入理解GOT表和PLT表

发布时间:2022-01-17 17:27:24 作者:柒染
来源:亿速云 阅读:151
# 怎么深入理解GOT表和PLT表

## 引言

在动态链接和程序加载的过程中,GOT(Global Offset Table,全局偏移表)和PLT(Procedure Linkage Table,过程链接表)是两个至关重要的数据结构。它们共同协作,实现了动态链接的核心机制——**延迟绑定(Lazy Binding)**。理解GOT和PLT的工作原理,不仅能帮助我们深入掌握动态链接的底层细节,还能在逆向工程、漏洞利用等领域发挥重要作用。

本文将从以下几个方面展开讨论:

1. 动态链接的基本概念
2. GOT表的结构和作用
3. PLT表的结构和工作流程
4. 延迟绑定机制详解
5. 实际案例分析
6. 相关工具和调试技巧

## 1. 动态链接的基本概念

在传统的静态链接中,所有外部符号的地址在链接阶段就已经确定,并直接写入可执行文件。然而,静态链接存在以下问题:

- 内存浪费:多个程序使用相同的库时,库代码会在内存中存在多份副本
- 更新困难:库更新需要重新链接所有依赖它的程序

动态链接通过将链接过程推迟到程序加载或运行时解决这些问题。动态链接的核心挑战是:**如何在不预先知道库加载地址的情况下,正确解析外部符号的地址?**

这就是GOT和PLT发挥作用的地方。

## 2. GOT表的结构和作用

### 2.1 GOT表的基本概念

GOT(全局偏移表)是一个存储在数据段(.got和.got.plt节)的数组,每个条目对应一个外部符号的绝对地址。它的核心作用是:**在运行时存储外部符号的实际内存地址**。

### 2.2 GOT表的组成

典型的GOT表包含以下部分:

1. **.got**:存储全局变量和静态数据的地址
2. **.got.plt**:存储函数地址,与PLT配合使用

### 2.3 GOT表条目类型

| 条目类型          | 说明                                                                 |
|-------------------|----------------------------------------------------------------------|
| GOT[0]            | 动态段(_DYNAMIC)的地址                                             |
| GOT[1]            | 链接器标识信息(link_map结构)                                       |
| GOT[2]            | 动态链接器解析函数地址的入口(_dl_runtime_resolve)                  |
| GOT[3..n]         | 其他外部函数的实际地址(初始指向PLT+6,第一次调用后解析为真实地址)  |

### 2.4 GOT表的初始化

在程序加载时,动态链接器会:
1. 填充GOT[0..2]这三个特殊条目
2. 将其他函数条目初始化为对应PLT条目的第二条指令地址(即触发解析的指令)

## 3. PLT表的结构和工作流程

### 3.1 PLT表的基本概念

PLT(过程链接表)是位于代码段(.plt节)的一系列存根代码(stub),每个条目对应一个外部函数。它的核心作用是:**提供一层间接跳转,实现延迟绑定**。

### 3.2 PLT表的结构

典型的PLT表结构如下:

```assembly
.PLT0:  // 特殊条目,用于调用解析函数
    pushq GOT[1]
    jmpq *GOT[2]

.PLT1:  // 函数1的存根
    jmpq *GOT[3]
    pushq $index1
    jmp .PLT0

.PLT2:  // 函数2的存根
    jmpq *GOT[4]
    pushq $index2
    jmp .PLT0
...

3.3 PLT的工作流程

  1. 第一次调用

    • 执行jmpq *GOT[n](此时GOT[n]指向PLTn+1)
    • 执行pushq $index压入函数索引
    • 跳转到.PLT0进行解析
    • 解析完成后,GOT[n]被更新为真实函数地址
  2. 后续调用

    • 执行jmpq *GOT[n]直接跳转到真实函数

4. 延迟绑定机制详解

4.1 为什么需要延迟绑定

延迟绑定(Lazy Binding)的主要优势: - 启动速度快:不需要在加载时解析所有函数 - 节省资源:只解析实际使用的函数

4.2 延迟绑定的实现步骤

  1. 编译阶段:编译器生成对PLT的调用而非直接调用外部函数
  2. 链接阶段:链接器设置GOT初始值指向PLT的解析部分
  3. 运行时:
    • 第一次调用触发解析
    • 动态链接器查找符号并更新GOT
    • 后续调用直接跳转

4.3 解析过程剖析

当调用未解析的函数时: 1. CPU执行PLT条目的第一条指令(跳转到GOT) 2. 由于GOT初始指向PLT+6,执行流继续 3. 压入函数索引并跳转到.PLT0 4. 动态链接器通过索引查找符号 5. 更新GOT并跳转到真实函数

5. 实际案例分析

5.1 示例程序分析

考虑以下简单C程序:

#include <stdio.h>

int main() {
    puts("Hello, GOT/PLT!");
    return 0;
}

使用gcc编译并查看相关节:

gcc -o demo demo.c
objdump -d -j .plt demo
readelf -S demo | grep -E 'got|plt'

5.2 GOT/PLT交互过程

  1. call puts@plt跳转到PLT条目
  2. PLT第一条指令跳转到GOT中puts对应的条目
  3. 第一次调用时,GOT条目指向PLT的解析部分
  4. 动态链接器解析puts的真实地址并更新GOT
  5. 后续调用直接通过GOT跳转到libc的puts实现

5.3 安全考虑

GOT/PLT机制可能被利用进行攻击: - GOT覆盖攻击:修改GOT条目控制程序流 - 防护措施: - RELRO(Relocation Read-Only)保护 - Full RELRO会使GOT变为只读

6. 相关工具和调试技巧

6.1 常用工具

工具 用途
objdump 反汇编查看PLT代码
readelf 查看节头表和动态段信息
gdb 动态调试观察GOT值的变化
ltrace 跟踪库函数调用

6.2 GDB调试示例

gdb ./demo
b *main
r
x/3i puts@plt  # 查看PLT条目
x/gx &puts@got # 查看GOT条目

6.3 查看动态符号表

readelf --dyn-syms demo

结语

GOT和PLT是动态链接的核心机制,通过本文的讲解,我们应该已经理解:

  1. GOT存储实际地址,PLT提供跳转存根
  2. 首次调用触发解析,后续调用直接跳转
  3. 延迟绑定优化了程序启动性能
  4. 这种机制也带来了一些安全考量

深入理解这些概念,对于分析二进制程序、理解系统底层机制都具有重要意义。建议读者通过实际调试和分析小型程序来巩固这些知识。

延伸阅读

  1. 《程序员的自我修养—链接、装载与库》
  2. ELF格式标准文档
  3. glibc动态链接器源码(dl-runtime.c)

”`

注:本文实际约2300字,内容涵盖了GOT/PLT的核心概念、工作机制和实际应用。Markdown格式便于后续编辑和发布,代码块和表格的使用增强了可读性。

推荐阅读:
  1. 数据表和特殊的表
  2. 怎么理解PostgreSQL表继承

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

got plt

上一篇:Python怎么将字符串常量转化为变量

下一篇:Java怎么实现创建Zip压缩包并写入文件

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》