<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Elf on RatherHard の Blog</title><link>https://blog.ratherhard.com/tags/elf/</link><description>Recent content in Elf on RatherHard の Blog</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><lastBuildDate>Wed, 22 Apr 2026 20:30:59 +0800</lastBuildDate><atom:link href="https://blog.ratherhard.com/tags/elf/index.xml" rel="self" type="application/rss+xml"/><item><title>plt 和 got</title><link>https://blog.ratherhard.com/post/ctf-pwn/plt-and-got/</link><pubDate>Mon, 29 Dec 2025 14:29:00 +0000</pubDate><guid>https://blog.ratherhard.com/post/ctf-pwn/plt-and-got/</guid><description>&lt;img src="https://blog.ratherhard.com/" alt="Featured image of post plt 和 got" /&gt;&lt;p&gt;在 Linux ELF 文件和动态链接机制中，这四个段（Section）共同协作，实现了&lt;strong&gt;位置无关代码（PIC）&lt;strong&gt;和&lt;/strong&gt;延迟绑定（Lazy Binding）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;虽然它们名字很像，但功能和权限有着本质区别。我们可以将其分为两类：&lt;strong&gt;PLT 类（代码/执行）&lt;/strong&gt; 和 &lt;strong&gt;GOT 类（数据/读写）&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="1-plt-procedure-linkage-table---过程链接表"&gt;1. .plt (Procedure Linkage Table - 过程链接表)
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;属性&lt;/strong&gt;：&lt;strong&gt;代码段&lt;/strong&gt;（权限：读取+执行 &lt;code&gt;R-X&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;：它包含了一系列小的&lt;strong&gt;可执行代码片段&lt;/strong&gt;（Stub）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：当程序调用一个外部函数（如 &lt;code&gt;printf&lt;/code&gt;）时，它实际上并不是直接跳到 &lt;code&gt;printf&lt;/code&gt; 的地址（因为在编译阶段不知道地址），而是跳到 &lt;code&gt;.plt&lt;/code&gt; 段中对应的条目。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内容&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;第一项是特殊项，负责调用动态链接器的符号解析函数。&lt;/li&gt;
&lt;li&gt;后续每一项对应一个外部函数，代码逻辑通常是：&lt;code&gt;jmp *(.got.plt中的对应项)&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-got-global-offset-table---全局偏移表"&gt;2. .got (Global Offset Table - 全局偏移表)
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;属性&lt;/strong&gt;：&lt;strong&gt;数据段&lt;/strong&gt;（权限：读取+写入 &lt;code&gt;RW-&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;：用于存储&lt;strong&gt;全局变量&lt;/strong&gt;的绝对地址。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：程序在引用全局变量时，会先到 &lt;code&gt;.got&lt;/code&gt; 中查找该变量的真实地址。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;为什么需要&lt;/strong&gt;：为了实现位置无关代码（PIC），代码段不包含变量的绝对地址，只包含到 &lt;code&gt;.got&lt;/code&gt; 的相对偏移。动态链接器在程序启动时会将变量的真实地址填入 &lt;code&gt;.got&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-gotplt-got-的-plt-部分"&gt;3. .got.plt (GOT 的 PLT 部分)
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;属性&lt;/strong&gt;：&lt;strong&gt;数据段&lt;/strong&gt;（权限：读取+写入 &lt;code&gt;RW-&lt;/code&gt;，但在 Full RELRO 下为 &lt;code&gt;R--&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;：它是 &lt;code&gt;.got&lt;/code&gt; 的一个子集，专门用于存储&lt;strong&gt;外部函数&lt;/strong&gt;的绝对地址。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;与 &lt;code&gt;.plt&lt;/code&gt; 的配合&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;在&lt;strong&gt;延迟绑定&lt;/strong&gt;（Lazy Binding）模式下：&lt;code&gt;.got.plt&lt;/code&gt; 的初始内容指向 &lt;code&gt;.plt&lt;/code&gt; 中的下一条指令（即“跳回 PLT”）。当函数第一次被调用时，动态链接器解析出真实地址并覆盖掉这个值。&lt;/li&gt;
&lt;li&gt;在&lt;strong&gt;第二次调用&lt;/strong&gt;时：&lt;code&gt;.plt&lt;/code&gt; 里的 &lt;code&gt;jmp&lt;/code&gt; 就会直接跳到 &lt;code&gt;.got.plt&lt;/code&gt; 中存储的真实地址，不再进入链接器。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特殊项&lt;/strong&gt;：前三项通常预留给动态链接器的私有信息（如 &lt;code&gt;link_map&lt;/code&gt; 结构和 &lt;code&gt;_dl_runtime_resolve&lt;/code&gt; 函数地址）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4-pltgot-专门的-plt-跳转表"&gt;4. .plt.got (专门的 PLT 跳转表)
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;属性&lt;/strong&gt;：&lt;strong&gt;代码段&lt;/strong&gt;（权限：读取+执行 &lt;code&gt;R-X&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;：这是一个特殊的 &lt;code&gt;.plt&lt;/code&gt; 段，通常用于&lt;strong&gt;非延迟绑定&lt;/strong&gt;的情况，或者用于处理某些特定的重定位类型（如通过 &lt;code&gt;R_X86_64_GLOB_DAT&lt;/code&gt; 重定位的函数指针）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;区别&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;标准的 &lt;code&gt;.plt&lt;/code&gt; 条目通常包含三个动作：跳转到 GOT、压栈索引、跳到解析器。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.plt.got&lt;/code&gt; 条目通常&lt;strong&gt;直接跳转&lt;/strong&gt;到 &lt;code&gt;.got&lt;/code&gt;（而不是 &lt;code&gt;.got.plt&lt;/code&gt;）中存储的地址，不包含延迟绑定的逻辑（没有压栈和解析器的跳转）。它通常出现在启用了 &lt;code&gt;-z now&lt;/code&gt;（Full RELRO）或编译器优化后的二进制文件中。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="总结与对比"&gt;总结与对比
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th style="text-align: left"&gt;段名称&lt;/th&gt;
 &lt;th style="text-align: left"&gt;类型&lt;/th&gt;
 &lt;th style="text-align: left"&gt;权限&lt;/th&gt;
 &lt;th style="text-align: left"&gt;存储内容&lt;/th&gt;
 &lt;th style="text-align: left"&gt;核心目的&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;.plt&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: left"&gt;代码&lt;/td&gt;
 &lt;td style="text-align: left"&gt;R-X&lt;/td&gt;
 &lt;td style="text-align: left"&gt;跳转代码片段 (Stubs)&lt;/td&gt;
 &lt;td style="text-align: left"&gt;函数调用的中转站，触发延迟绑定&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;.plt.got&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: left"&gt;代码&lt;/td&gt;
 &lt;td style="text-align: left"&gt;R-X&lt;/td&gt;
 &lt;td style="text-align: left"&gt;直接跳转代码&lt;/td&gt;
 &lt;td style="text-align: left"&gt;跳过延迟绑定逻辑，直接跳转到 GOT 地址&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;.got&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: left"&gt;数据&lt;/td&gt;
 &lt;td style="text-align: left"&gt;RW-&lt;/td&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;全局变量&lt;/strong&gt;的绝对地址&lt;/td&gt;
 &lt;td style="text-align: left"&gt;变量引用的位置无关化&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;.got.plt&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: left"&gt;数据&lt;/td&gt;
 &lt;td style="text-align: left"&gt;RW-&lt;/td&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;外部函数&lt;/strong&gt;的绝对地址&lt;/td&gt;
 &lt;td style="text-align: left"&gt;配合 .plt 实现函数的延迟绑定&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="协作流程演示以延迟绑定为例"&gt;协作流程演示（以延迟绑定为例）：
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Call &lt;code&gt;printf@plt&lt;/code&gt;&lt;/strong&gt;：程序跳转到 &lt;code&gt;.plt&lt;/code&gt; 中 &lt;code&gt;printf&lt;/code&gt; 对应的条目。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jmp to &lt;code&gt;.got.plt&lt;/code&gt;&lt;/strong&gt;：&lt;code&gt;.plt&lt;/code&gt; 里的第一条指令跳转到 &lt;code&gt;.got.plt&lt;/code&gt; 记录的地址。
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;第一次调用&lt;/em&gt;：&lt;code&gt;.got.plt&lt;/code&gt; 填的是 &lt;code&gt;.plt&lt;/code&gt; 的下一行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resolve&lt;/strong&gt;：&lt;code&gt;.plt&lt;/code&gt; 剩下的代码调用动态链接器，找到 &lt;code&gt;printf&lt;/code&gt; 的真实地址。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Update&lt;/strong&gt;：动态链接器将 &lt;code&gt;printf&lt;/code&gt; 的真实地址写回 &lt;code&gt;.got.plt&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Subsequent Calls&lt;/strong&gt;：下次再调 &lt;code&gt;printf@plt&lt;/code&gt; 时，第 2 步的 &lt;code&gt;Jmp&lt;/code&gt; 会直接跳到 &lt;code&gt;printf&lt;/code&gt; 的真实地址。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="为什么现在的安全保护relro会影响这些段"&gt;为什么现在的安全保护（RELRO）会影响这些段？
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Partial RELRO&lt;/strong&gt;：&lt;code&gt;.got.plt&lt;/code&gt; 是可写的。攻击者可以利用堆栈溢出覆盖 &lt;code&gt;.got.plt&lt;/code&gt; 的条目，将 &lt;code&gt;printf&lt;/code&gt; 改为 &lt;code&gt;system&lt;/code&gt;，从而实现 &lt;strong&gt;GOT Hijacking&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Full RELRO&lt;/strong&gt;：动态链接器在程序启动时就把所有函数解析完毕，并将 &lt;code&gt;.got.plt&lt;/code&gt; 设为&lt;strong&gt;只读&lt;/strong&gt;。此时，&lt;code&gt;.plt.got&lt;/code&gt; 的作用就会变得更明显，因为不再需要延迟绑定逻辑了。&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>