南京大学 操作系统 (JYY) 学习笔记:动态链接的黑魔法与内存入侵 (Dynamic Linking)
写在前面这是本系列的第十一篇。在前几讲中我们已经知道进程从execve的初始状态开始可以通过mmap改变地址空间通过fork创建新进程。只要有 ELF 可执行文件把它的PT_LOAD数据段正确映射到内存里程序就能跑起来就像我们的 Funny Little Executable 实验。但本讲要面对一个更庞大的工程问题当开发者希望把库函数如 libc和应用程序“分离”开但又希望在运行时能随时调用库函数该怎么办课前补课静态链接 vs 动态链接静态链接库 (.a / .lib):就像是一个提前打包好的工具箱。当你写程序时编译器会把你需要的加法、减法等功能代码**直接死死地“拷贝”**到你的最终可执行文件ELF里。程序变大了但它完全独立不需要依赖外界。动态链接库 (.so / .dll):就好比是一个放在外面广场上的“共享工具箱”。程序里只留一张欠条符号表。当程序真正运行起来时操作系统再去找到这个共享库在运行时把它们拼在一起。就像租用工具随用随借。动态链接机制与为什么需要它我们熟悉的 Windows 下的.dll或者 Linux 下的.so(Shared Object)都是动态链接的产物。为什么要“拆解”应用程序早期的时候游戏如果需要用到d3dx9_xxx.dll里的功能就会把整个库静态链接进游戏本体。结果每个游戏都复制了一份庞大的图形库硬盘和内存瞬间爆炸。1. 实现运行库和应用代码分离 (应用间共享)每个 C 程序都需要glibc。如果使用动态链接整个操作系统内存里只需要一个 libc 的物理副本成百上千个进程同时共享它。运行库和应用程序可以独立升级互不干扰。2. 大型项目的工程分解比如庞大的 Android 源码改一行代码如果需要重新链接生成 2GB 的大文件程序员会疯掉的。拆分成libjvm.so,libart.so每次只编译更新那个.so文件即可。可以用ldd和file命令来观察rootLAPTOP-GT06V0GS:~# ldd /bin/lslinux-vdso.so.1(0x00007ffe950d8000)libc.so.6/lib/x86_64-linux-gnu/libc.so.6(0x00007f829a846000)... rootLAPTOP-GT06V0GS:~# file /lib/x86_64-linux-gnu/libselinux.so.1/lib/x86_64-linux-gnu/libselinux.so.1: ELF64-bit LSB shared object, dynamically linked...动态链接的阴暗面供应链攻击与依赖地狱任何技术都有代价。库的依赖本质上也是一种代码克隆甚至把风险也克隆了过来。xz-utils (liblzma) 投毒事件 (CVE-2024-3094):震惊全球的安全事件黑客 JiaT75 潜伏开源社区长达三年获取信任成为维护者后巧妙绕开 fuzz 测试向基础压缩库投毒。导致全球依赖这个.so的 Linux 系统的 SSHD 均面临后门风险。如果 Linux 全是静态链接的呢如果不用动态链接一旦libc出了安全漏洞你需要把全系统几万个应用程序全部重新编译链接一次这是不可能完成的任务。Dependency Hell (依赖地狱):A 依赖 B 的 v1 版本C 依赖 B 的 v2 版本而你的项目同时依赖 A 和 C。当这种依赖嵌套成网状时版本冲突和不兼容更新就会让人彻底崩溃。感谢动态链接库虽然有坑但如果没有它现代计算机世界早就崩塌了。揭秘底层引擎mmap 和虚拟内存一个极其狂野的实验我们构造一个包含 100MB 无用代码 (nop指令) 的巨型共享库libbloat.so。然后同时启动1000个动态链接了这个库的进程问题系统的物理内存会被消耗 100MB 还是 100GB如果是 100GB电脑瞬间死机宕机。答案只消耗 100MB共享库加载的真相 (没有魔法)你执行./a.out时第一条被执行的指令根本不在你的代码里它是操作系统通过读取 ELF 文件头的INTERP(Interpreter) 段去调用了动态链接器如/lib64/ld-linux-x86-64.so.2。链接器使用mmap系统调用把libc等共享库映射到内存中。核心魔法只读方式mmap同一个文件物理内存中只有一份真实的副本。1000 个进程的页表全都指向同一块物理内存。操作系统的内存 Tricks地址空间表面上看是“若干连续的内存段”实际全是操作系统用分页机制虚构出来的幻象延迟加载 (Lazy Allocation):不到万不得已触发 Page Fault绝不给进程分配真物理内存。写时复制 (Copy-on-Write, COW):fork()时父子进程共享内存。只有当某一方试图写入时操作系统才紧急复制一份Page fault 时写者复制一份。内存去重 (Memory Deduplication):操作系统在后台悄悄扫描内存如果发现有两页内容完全一样的只读页就悄悄合并它们内存压缩与 Swapping:扫描发现某些内存页很久没人用了Cold pages就把它压缩或者扔到硬盘交换区里。实现动态链接GOT 与 PLT 的诞生如果把动态链接交给你来设计你会怎么做方案1加载时重定位 (libc.o)把程序和库一起搬进内存加载时暴力修改所有引用了库函数的地址。缺点极慢链接器需要解析成千上万个根本不会被运行的符号并且修改代码会导致代码页无法被多个进程共享因为每个进程加载的基地址不同。方案2位置无关代码 (Position-Independent Code, PIC)这就对了我们需要映射同一个libc.so并且无论它被加载到哪个虚拟地址代码都必须能正常运行。A Layer of Indirection (引入一个间接层)难题main调用了.so库里的printf但printf在运行时的地址是未知的。我们该用什么汇编指令跳过去编译器的选择 1全部查表跳转。每次调用都多查一次内存表。对于极其高频的函数性能损失不可忍受。编译器的选择 2全部直接跳转。但 x86 的call指令只支持 4 字节的相对偏移而libc.so可能被映射到了离当前代码十万八千里远的高位地址偏移量超出了 32 位极限根本跳不过去伟大的发明PLT 与 GOT为了兼顾性能和位置无关计算机先驱们发明了组合拳GOT (Global Offset Table - 全局偏移表):这本质上是一个存放指针的数组放在.data数据段里。加载时动态链接器把真正的printf物理地址填进这个数组。PLT (Procedure Linkage Table - 过程链接表):在可执行文件的代码段里“合成”一段跳板代码蹦床printfplt: jmp *GOT_PRINTF_OFFSET(%rip)调用流程你的代码call printfplt$ \rightarrow $ 跳转到蹦床 $ \rightarrow $ 蹦床去 GOT 表里读取真正的绝对地址 $ \rightarrow $ 跳进libc.so对于全局变量extern int x怎么办不能用间接跳转指令了。编译器一旦开启了-fPIC就会强制为所有的外部变量增加一层间接寻址必须去 GOT 表里拿一遍指针再解引用。这必然带来一定的性能损耗。终极黑魔法LD_PRELOAD计算机世界里没有黑魔法所有的“外挂”都有迹可循。LD_PRELOAD是 Linux 动态链接器提供的一个环境变量。它允许你强行指定一个动态链接库使其在程序启动时被最高优先级加载。LD_PRELOAD./mylib.so ./a.out它是怎么实现拦截Hook的因为动态链接器在解析符号比如找malloc在哪时是按照顺序查找的。如果你的mylib.so里也写了一个叫malloc的函数链接器找到它之后就不会再往下找glibc里的malloc了这样你就在不修改原本a.out代码的情况下成功劫持并替换了它的系统底层函数。rootLAPTOP-GT06V0GS:/mnt/d/CSLab/osCourse/lec11/ldpreload# ./test_programTesting malloc/free hooks...# 成功的拦截程序每一次申请内存都被我们预先加载的恶意/监控代码记录了下来Allocated100bytes at 0x55ba218c86b0 Allocated200bytes at 0x55ba218c8720Take-away Messages (总结)找到正确的思路我们就能在复杂的机制中找到主干在动态链接的例子里我们理解了为了实现位置无关和代码共享先驱们创造了 ELF 中的GOT(Global Offset Table) 和PLT(Procedure Linkage Table)。而正是这种动态寻址机制赋予了我们利用LD_PRELOAD进行函数 Hook 的极客能力。

相关新闻

Windows RTMP直播服务器终极指南:5分钟快速搭建专业流媒体平台

Windows RTMP直播服务器终极指南:5分钟快速搭建专业流媒体平台

Windows RTMP直播服务器终极指南:5分钟快速搭建专业流媒体平台 【免费下载链接】nginx-rtmp-win32 Nginx-rtmp-module Windows builds. 项目地址: https://gitcode.com/gh_mirrors/ng/nginx-rtmp-win32 想在Windows系统上快速搭建一个专业的RTMP直播服务器吗…

2026/10/14 22:56:07 阅读更多 →
消息队列选型对比:RabbitMQ、Kafka 与 Redis Stream 的适用边界

消息队列选型对比:RabbitMQ、Kafka 与 Redis Stream 的适用边界

消息队列选型对比:RabbitMQ、Kafka 与 Redis Stream 的适用边界 一、深度引言与场景痛点:一条消息延迟了 10 秒,用户以为系统坏了 7 月在刷题系统中实现了一个"AI 题解生成"的异步功能:用户提交题目后,系统将…

2026/10/3 2:19:13 阅读更多 →
宠物商城托运一体化搭建,宠物档案绑定商品物流全流程

宠物商城托运一体化搭建,宠物档案绑定商品物流全流程

宠物商城托运一体化搭建,宠物档案绑定商品物流全流程当下宠物服务类小程序与平台逐步从单一用品售卖、单一托运服务,转向商城购物、活体托运、宠物档案管理一体化的综合运营模式。很多宠物平台在实际搭建中,普遍存在宠物档案、商城订单、托运…

2026/10/13 23:36:46 阅读更多 →

最新新闻

Phaser多任务协同实战:场景调度、事件通信与插件扩展解析

Phaser多任务协同实战:场景调度、事件通信与插件扩展解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 8:37:38 阅读更多 →
Helm 3 部署 Kubernetes 应用实战:从安装到回滚

Helm 3 部署 Kubernetes 应用实战:从安装到回滚

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 8:37:38 阅读更多 →
JavaWeb毕设选题:SpringBoot+Vue知识竞赛平台设计与实现

JavaWeb毕设选题:SpringBoot+Vue知识竞赛平台设计与实现

每年三四月都是毕设最焦灼的时候。我身边不少学生做Java Web方向的毕业设计,用得最多的技术搭配就是SpringBootVue,题目翻来覆去也不过是商城、管理系统、预约平台,做多了答辩老师一眼就能看出哪些是赶工拼凑的。相比之下,信息知识…

2026/10/16 8:37:38 阅读更多 →
用Rust解析SOR文件:OTDR数据格式与工程实践

用Rust解析SOR文件:OTDR数据格式与工程实践

简介:面向光纤网络运维与测试工程师,这款由Rust编写的OTDR数据解析工具,依据Bellcore Telcordia SR-4731标准设计,用于读取、解析并分析光时域反射仪产生的SOR轨迹文件,帮助定位光纤断点、接头损耗与链路质量异常。压缩…

2026/10/16 8:37:38 阅读更多 →
NTP 时钟服务器双机部署误区与无跳变冗余方案

NTP 时钟服务器双机部署误区与无跳变冗余方案

关键词:ntp网络时钟服务器,网络时钟同步系统,ntp协议时钟服务器,ntp网络同步时钟服务器一、前言:时间同步的单点故障陷阱在电力变电站、工业自控、核心业务机房的时间同步系统建设中,几乎所有设计都会考虑规…

2026/10/16 8:37:38 阅读更多 →
CentOS单机伪分布式MongoDB副本集搭建与故障转移实战

CentOS单机伪分布式MongoDB副本集搭建与故障转移实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 8:36:37 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →