ctf-wiki 堆利用基础:深入剖析 ptmalloc2 的 unlink 宏与 malloc_printerr 错误处理机制
文档网络安全教程【免费下载链接】ctf-wikiCome and join us, we need you!项目地址https://gitcode.com/gh_mirrors/ct/ctf-wiki点击查看免费下载本文以 ctf-wiki 仓库中《ptmalloc2 实现 - 基礎操作》文档为核心结合 glibc malloc 的源码级实现free/malloc中的调用链与malloc_chunk数据结构系统讲解双向链表摘除操作unlink的使用场景、宏实现、安全校验及其在地址泄漏中的妙用并完整解析错误检测入口malloc_printerr的触发与终止流程。读完本文你将掌握 unlink 的原理与绕过思路能够理解堆溢出漏洞中常见的 corrupted double-linked list 报错根源并为后续阅读 ctf-wiki 中的 unlink 利用实战 打下坚实基础。1. unlink 是什么在 ptmalloc2 的堆管理中所有空闲 chunk会被挂载到对应的双向链表bin中。unlink的作用就是把一个已经存在于双向链表中的空闲 chunk 从链表中摘除。由于堆的分配与释放过程会频繁地插入、摘除 chunkglibc 将这一高频操作实现为一个宏unlink(AV, P, BK, FD)。1.1 使用场景根据仓库文档的归纳unlink 主要出现在以下四类路径中触发函数具体场景malloc从恰好大小合适的 large bin 中获取 chunk注意fastbin 与 small bin 的取出不使用 unlink这正是漏洞常出现在这两个 bin 的原因依次遍历处理 unsorted bin 时也不使用 unlink从比请求 chunk 所在 bin 更大的 bin 中取 chunk 时使用free后向合并合并物理相邻的低地址空闲 chunk前向合并合并物理相邻的高地址空闲 chunktop chunk 除外malloc_consolidate后向合并、前向合并除 top chunk 外将 fastbin 中的 chunk 与相邻空闲 chunk 合并realloc前向扩展合并物理相邻的高地址空闲 chunktop chunk 除外从仓库的 free 实现文档 可以看到_int_free中正是通过unlink(av, p, bck, fwd)完成后向合并通过unlink(av, nextchunk, bck, fwd)完成前向合并在 malloc 实现文档 的 large bin 分配路径中取出合适 chunk 前同样会执行unlink(av, victim, bck, fwd)。也就是说凡是涉及“空闲链表摘除”的操作都逃不开 unlink。2. unlink 宏的源码级拆解glibc 中 unlink 的实现如下即 基礎操作文档 中的原始宏/* Take a chunk off a bin list */ // unlink p #define unlink(AV, P, BK, FD) { \ // 由于 P 已经在双向链表中所以有两个地方记录其大小所以检查一下其大小是否一致。 if (__builtin_expect (chunksize(P) ! prev_size (next_chunk(P)), 0)) \ malloc_printerr (corrupted size vs. prev_size); \ FD P-fd; \ BK P-bk; \ // 防止攻击者简单篡改空闲的 chunk 的 fd 与 bk 来实现任意写的效果。 if (__builtin_expect (FD-bk ! P || BK-fd ! P, 0)) \ malloc_printerr (check_action, corrupted double-linked list, P, AV); \ else { \ FD-bk BK; \ BK-fd FD; \ // 下面主要考虑 P 对应的 nextsize 双向链表的修改 if (!in_smallbin_range (chunksize_nomask (P)) \ // 如果P-fd_nextsize为 NULL表明 P 未插入到 nextsize 链表中。 // 那么其实也就没有必要对 nextsize 字段进行修改了。 // 这里没有去判断 bk_nextsize 字段可能会出问题。 __builtin_expect (P-fd_nextsize ! NULL, 0)) { \ // 类似于小的 chunk 的检查思路 if (__builtin_expect (P-fd_nextsize-bk_nextsize ! P, 0) \ || __builtin_expect (P-bk_nextsize-fd_nextsize ! P, 0)) \ malloc_printerr (check_action, \ corrupted double-linked list (not small), \ P, AV); \ // 这里说明 P 已经在 nextsize 链表中了。 // 如果 FD 没有在 nextsize 链表中 if (FD-fd_nextsize NULL) { \ // 如果 nextsize 串起来的双链表只有 P 本身那就直接拿走 P // 令 FD 为 nextsize 串起来的 if (P-fd_nextsize P) \ FD-fd_nextsize FD-bk_nextsize FD; \ else { \ // 否则我们需要将 FD 插入到 nextsize 形成的双链表中 FD-fd_nextsize P-fd_nextsize; \ FD-bk_nextsize P-bk_nextsize; \ P-fd_nextsize-bk_nextsize FD; \ P-bk_nextsize-fd_nextsize FD; \ } \ } else { \ // 如果在的话直接拿走即可 P-fd_nextsize-bk_nextsize P-bk_nextsize; \ P-bk_nextsize-fd_nextsize P-fd_nextsize; \ } \ } \ } \ }2.1 宏的语义拆解逐段理解这个宏大小一致性检查chunksize(P)取的是 P 自身size字段记录的掩去标志位后的大小prev_size(next_chunk(P))是 P 的物理相邻后一块 chunk 中记录的“前一块大小”。由于空闲 chunk 会有两个地方记录自身大小自身size与后一 chunk 的prev_size这里检查二者是否一致防止伪造 size。取出前后指针FD P-fd、BK P-bk即 P 在链表中的后继与前驱。双向链表完整性检查FD-bk ! P || BK-fd ! P。正常情况下P 的后继的bk必然指向 PP 的前驱的fd也必然指向 P。这一检查的目的正是防止攻击者直接篡改空闲 chunk 的 fd/bk 实现任意写——如果攻击者把 fd 改成target-12、bk 改成expect那么FD-bk BK等价于*(target-1212) expect直接构成任意地址写所以 glibc 用双向一致性校验加以拦截。摘除主链FD-bk BK; BK-fd FD;完成双向链表的标准摘除。nextsize 链处理仅当 P 属于large bin 范围!in_smallbin_range且P-fd_nextsize ! NULL时才需要处理。large bin 中的 chunk 除了挂在fd/bk主链上还会按大小挂一条fd_nextsize/bk_nextsize的“跳表”链fd_nextsize指向下一个不同大小的空闲块bk_nextsize指向上一个不同大小的空闲块用于加速按大小查找。摘除 P 时需要同步维护这条链先检查P-fd_nextsize-bk_nextsize P与P-bk_nextsize-fd_nextsize P若 FD 不在 nextsize 链中FD-fd_nextsize NULL若 nextsize 链只有 P 一个节点P-fd_nextsize P则让 FD 自成环否则把 FD 插入到 nextsize 链中若 FD 已在 nextsize 链中则直接摘除 P 即可。源码结构提示宏中只判断了fd_nextsize是否为 NULL 来决定是否进入 nextsize 分支而没有判断bk_nextsize这是 glibc 源码中一个值得留意的细节在构造 large bin 场景的堆布局时可能被利用。2.2 small bin 场景的直观理解原文档以small bin 的 unlink为例给出示意图large bin 的 unlink 与之类似只是多了 nextsize 链的处理可以看到P 最后的 fd 和 bk 指针本身并不会发生变化但由于前驱后继的指针被改写遍历整个双向链表时已经无法再遍历到 P。这一特性在堆利用中非常有用——可以利用 unlink 后的残留指针来泄漏地址泄漏 libc 地址P 位于双向链表头部bk泄漏P 位于双向链表尾部fd泄漏双向链表只包含一个空闲 chunk 时P 位于链表中fd和bk均可以泄漏。泄漏堆地址双向链表包含多个空闲 chunk 时P 位于双向链表头部fd泄漏P 位于双向链表中fd和bk均可以泄漏P 位于双向链表尾部bk泄漏。注意这里的“头部”指 bin 的fd指向的 chunk即双向链表中最新加入的 chunk“尾部”指 bin 的bk指向的 chunk即双向链表中最先加入的 chunk。2.3 检查代码小结无论是fd/bk还是fd_nextsize/bk_nextsizeunlink 都会对指针的一致性进行校验对应代码如下// fd bk if (__builtin_expect (FD-bk ! P || BK-fd ! P, 0)) \ malloc_printerr (check_action, corrupted double-linked list, P, AV); \ // next_size related if (__builtin_expect (P-fd_nextsize-bk_nextsize ! P, 0) \ || __builtin_expect (P-bk_nextsize-fd_nextsize ! P, 0)) \ malloc_printerr (check_action, \ corrupted double-linked list (not small), \ P, AV);表面上看这些检查都很正常P 的 forward chunk后继的bk自然是 Pbackward chunk前驱的fd也自然是 P。但如果没有这些检查攻击者只需修改 P 的fd与bk就能轻松实现任意地址写。这正是 unlink 利用 章节中“古老的 unlink”与“当前带检查的 unlink”两种利用手法的分水岭——后者通过伪造fakeFD 12与fakeBK 8指向同一处可控内存来绕过校验。注意堆的第一个 chunk 所记录的prev_inuse位默认为 1。这一约定在 heap-structure 文档 中有详细说明size字段低三位分别是NON_MAIN_ARENA(0x4)、IS_MAPPED(0x2)、PREV_INUSE(0x1)堆中第一个被分配的内存块的 P 位被置为 1以阻止向前访问非法内存当 P 位为 0 时才能通过prev_size字段回溯上一个 chunk 的大小与地址进而进行空闲 chunk 的合并。3. malloc_printerr错误检测与终止在 glibc malloc 检测到堆元数据被破坏如 unlink 校验失败时会调用malloc_printerr函数。在 glibc 2.23 版本中其实现如下static void malloc_printerr(const char *str) { __libc_message(do_abort, %s\n, str); __builtin_unreachable(); }该函数的核心是调用__libc_message并传入do_abort动作标志最终触发abort()终止进程if ((action do_abort)) { if ((action do_backtrace)) BEFORE_ABORT(do_abort, written, fd); /* Kill the application. */ abort(); }在abort()函数内部glibc 2.23 版本会首先执行fflush(NULL)刷新所有输出流/* Flush all streams. We cannot close them now because the user might have registered a handler for SIGABRT. */ if (stage 1) { stage; fflush (NULL); }从实践角度理解malloc_printerr它不返回__builtin_unreachable()告知编译器后续代码不可达程序必然终止触发的典型报错字符串与 unlink 直接相关的有corrupted size vs. prev_size大小一致性检查失败、corrupted double-linked listfd/bk 双向链表校验失败、corrupted double-linked list (not small)large bin 的 nextsize 链校验失败。当你在调试堆漏洞时看到这些错误就说明 unlink 宏中的某一道安全防线被触碰了此时应回头检查伪造的 chunk 头size、prev_size与fd/bk以及 large bin 场景的fd_nextsize/bk_nextsize是否满足宏中的约束。4. 与相邻模块的关联unlink 并不是孤立的原语它与 ptmalloc2 的其它实现细节紧密相关这里给出仓库内的延伸阅读路径堆数据结构malloc_chunkunlink 操作的字段fd、bk、fd_nextsize、bk_nextsize只有在 chunk 空闲时才有效而prev_size的复用机制解释了为什么 unlink 需要同时检查两处记录的大小。free 实现_int_free中“后向合并 / 前向合并”两处 unlink 的调用上下文以及“只有非 fastbin 的 chunk 才会触发 unlink”的结论fastbin 中的 chunk 的 inuse 位始终为 1不与相邻空闲 chunk 合并。malloc 实现large bin 取出 chunk 时的 unlink 调用以及 unsorted bin 遍历FIFO不使用 unlink 的细节。unlink 利用实战从“古老的 unlink”无检查可直接改写 GOT到“当前 safe unlink”通过伪造fakeFD 12与fakeBK 8指向同一指针使*P P - 8、*P P - 12将指向 chunk 的指针改写为ptr - 0x18再到 2014 HITCON stkof、2016 ZCTF note2 等真实题目的完整利用链。5. 小结unlink 的本质从双向空闲链表中摘除一个节点宏内实现涉及fd/bk主链与 large bin 的fd_nextsize/bk_nextsize跳表链两套数据结构。安全校验大小一致性检查 双向链表完整性检查 large bin nextsize 链检查任何一项失败都会进入malloc_printerr并最终abort()。利用价值尽管 unlink 不再能直接实现任意写但“摘除后 P 的指针残留”可被用于泄漏 libc/堆地址配合精心构造的 fake chunk 与指针布局仍可完成指针改写如ptr → ptr - 0x18这是众多堆利用技巧house-of-* 系列、unsorted bin attack 等的基础。建议读者在掌握本文的宏语义后继续阅读 unlink 利用实战 中的完整 exp亲手验证“伪造 fakeFD/fakeBK 绕过检查”与“通过 unlink 将指针改为ptr - 0x18”这两个核心手法。赞分享文档网络安全教程【免费下载链接】ctf-wikiCome and join us, we need you!项目地址https://gitcode.com/gh_mirrors/ct/ctf-wiki点击查看免费下载相关推荐CTF-Wiki 堆利用基础深入剖析 glibc ptmalloc2 的 perturb_byte 内存填充机制CTF Wiki 堆利用基础深入剖析 glibc ptmalloc2 的 perturb_byte 内存填充机制 perturb_byte 是 glibc p文档网络安全教程ctf-wiki ptmalloc2 堆检查机制全解析malloc/free/unlink 的安全校验与报错信息对照ctf wiki ptmalloc2 堆检查机制全解析malloc/free/unlink 的安全校验与报错信息对照 导读 本文以 ctf wiki 仓库中文档网络安全教程CTF-Wiki 堆利用系列ptmalloc2 Unlink 攻击原理与实战HITCON/ZCTF 经典题目全解析CTF Wiki 堆利用系列ptmalloc2 Unlink 攻击原理与实战HITCON/ZCTF 经典题目全解析 本文以 CTF Wiki 仓库中 un文档网络安全教程上一篇5个高效技巧进阶用户如何优化LaTeX-Workshop配置下一篇MASTG 移动应用安全测试框架定制开发文档开发过程文档创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

oh-my-opencode-slim 领域文档消费协议:Agent 探索代码库前必须遵循的术语、ADR 与 codemap 纪律

oh-my-opencode-slim 领域文档消费协议:Agent 探索代码库前必须遵循的术语、ADR 与 codemap 纪律

人工智能AI AgentAgent 编排AI 技能 【免费下载链接】oh-my-opencode-slim Lean, fine tuned Opencode multi agent suite Mix any models Auto delegate tasks 项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-opencode-slim 点击查看 免费下载 导读 本文系…

2026/9/25 3:13:39 阅读更多 →
使用 Terraform 与 Ansible 在 AWS 上部署 Apache Pulsar 集群

使用 Terraform 与 Ansible 在 AWS 上部署 Apache Pulsar 集群

消息队列后端流处理 【免费下载链接】pulsar Apache Pulsar - distributed pub-sub messaging system 项目地址: https://gitcode.com/gh_mirrors/pulsar28/pulsar 点击查看 免费下载 本篇技术指南以 Apache Pulsar 官方部署文档为主体,完整讲解如何借助…

2026/9/25 3:13:39 阅读更多 →
预测市场里谁在赚taker的钱?prediction-market-analysis深度解析Maker-Taker微观结构

预测市场里谁在赚taker的钱?prediction-market-analysis深度解析Maker-Taker微观结构

预测市场里谁在赚taker的钱?prediction-market-analysis深度解析Maker-Taker微观结构 【免费下载链接】prediction-market-analysis A framework for collecting and analyzing prediction market data, including the largest publicly available dataset of Polym…

2026/9/25 3:12:38 阅读更多 →

最新新闻

Skia PathKit:将 Skia PathOps 编译为 WASM 并在浏览器中使用的完整实战指南

Skia PathKit:将 Skia PathOps 编译为 WASM 并在浏览器中使用的完整实战指南

图形学图像处理 【免费下载链接】skia Skia is a complete 2D graphic library for drawing Text, Geometries, and Images. 项目地址: https://gitcode.com/gh_mirrors/skia1/skia 点击查看 免费下载 本文以 Skia 仓库中 modules/pathkit/README.md 的核心内容为骨…

2026/9/25 5:26:24 阅读更多 →
ng-zorro-antd 日期选择器进阶指南:用 nzRenderExtraFooter 在面板中渲染自定义页脚

ng-zorro-antd 日期选择器进阶指南:用 nzRenderExtraFooter 在面板中渲染自定义页脚

UI组件前端 【免费下载链接】ng-zorro-antd Angular UI Component Library based on Ant Design 项目地址: https://gitcode.com/gh_mirrors/ng/ng-zorro-antd 点击查看 免费下载 nzRenderExtraFooter 是 ng-zorro-antd 日期选择器组件族(nz-date-picke…

2026/9/25 5:26:24 阅读更多 →
Fission 统一 OpenTelemetry 可观测性落地:RFC-0019 指标迁移、OTLP 原生推送与依赖裁剪实战

Fission 统一 OpenTelemetry 可观测性落地:RFC-0019 指标迁移、OTLP 原生推送与依赖裁剪实战

云原生后端 【免费下载链接】fission Fast and Simple Serverless Functions for Kubernetes 项目地址: https://gitcode.com/gh_mirrors/fi/fission 点击查看 免费下载 导读 Fission 的 tracing 与 logging 早已原生使用 OpenTelemetry,唯独指标&…

2026/9/25 5:26:23 阅读更多 →
从实验室到整机落地:集成化生物信号采集与处理系统全记录

从实验室到整机落地:集成化生物信号采集与处理系统全记录

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

2026/9/25 5:26:23 阅读更多 →
代码热更新原理与实践:从DevTools到Nacos配置中心

代码热更新原理与实践:从DevTools到Nacos配置中心

改代码最烦什么?不是需求复杂,不是报错看不懂,而是改一行前端样式要刷新整个页面重新走一遍流程,改一个Java方法要重启服务然后重新登录、重新点进那个页面。如果每天在这种“改代码-重启-验证”的循环里折腾几十次,你…

2026/9/25 5:26:23 阅读更多 →
使用 AWS SDK for Java 2.x 操作 Amazon Connect:实例、联系人、队列与历史指标实战指南

使用 AWS SDK for Java 2.x 操作 Amazon Connect:实例、联系人、队列与历史指标实战指南

示例工程教程后端 【免费下载链接】aws-doc-sdk-examples Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below. 项目地…

2026/9/25 5:25:23 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →