CTF-PWN glibc源码阅读[1]: 寻找libc中堆结构的定义(2.31-0ubuntu9.16)

源代码在这里下载

来到malloc/malloc.c

在980行发现这段代码

// 定义最大 mmap 值为 -4
#define M_MMAP_MAX             -4

// 如果没有定义 DEFAULT_MMAP_MAX,则将其定义为 65536
#ifndef DEFAULT_MMAP_MAX
#define DEFAULT_MMAP_MAX       (65536)
#endif

// 引入 malloc.h 头文件,通常包含内存分配和释放相关的函数声明
#include <malloc.h>

// 如果没有定义 RETURN_ADDRESS 宏,定义为一个空操作,返回 NULL
#ifndef RETURN_ADDRESS
#define RETURN_ADDRESS(X_) (NULL)
#endif

/* 结构体和类型的前向声明 */

// 定义 malloc_chunk 结构体(实际定义可能在代码的其他地方)
struct malloc_chunk;

// 定义 mchunkptr 为指向 malloc_chunk 结构体的指针类型
typedef struct malloc_chunk* mchunkptr;

/* 内部函数声明 */

// 内部函数:分配内存
static void*  _int_malloc(mstate, size_t);

// 内部函数:释放内存
static void     _int_free(mstate, mchunkptr, int);

// 内部函数:调整内存块大小
static void*  _int_realloc(mstate, mchunkptr, INTERNAL_SIZE_T, INTERNAL_SIZE_T);

// 内部函数:内存对齐分配
static void*  _int_memalign(mstate, size_t, size_t);

// 内部函数:内存对齐分配的中间实现
static void*  _mid_memalign(size_t, size_t, void *);

// 内部函数:打印内存分配错误信息,且该函数不会返回
static void malloc_printerr(const char *str) __attribute__ ((noreturn));

// 内部函数:检查内存块的有效性
static void* mem2mem_check(void *p, size_t sz);

// 内部函数:检查堆的顶端是否正常
static void top_check(void);

// 内部函数:通过 munmap 释放内存块
static void munmap_chunk(mchunkptr p);

// 如果系统支持 mremap,则声明 mremap_chunk 函数,用于调整内存映射
#if HAVE_MREMAP
static mchunkptr mremap_chunk(mchunkptr p, size_t new_size);
#endif

// 内部函数:检查 malloc 操作的合法性
static void*   malloc_check(size_t sz, const void *caller);

// 内部函数:检查 free 操作的合法性
static void      free_check(void* mem, const void *caller);

// 内部函数:检查 realloc 操作的合法性
static void*   realloc_check(void* oldmem, size_t bytes, const void *caller);

// 内部函数:检查内存对齐分配操作的合法性
static void*   memalign_check(size_t alignment, size_t bytes, const void *caller);

查看malloc_chunk结构体

// 定义内存块的元数据结构体,用于管理堆中的内存块
struct malloc_chunk {
  
  INTERNAL_SIZE_T mchunk_prev_size;  // 前一个内存块的大小(如果该块是空闲的)
  
  INTERNAL_SIZE_T mchunk_size;       // 当前内存块的大小,包括元数据的开销
  
  // 双向链表指针,用于空闲内存块的链表管理
  struct malloc_chunk* fd;           // 指向下一个空闲块的指针(free list 链表的正向指针)
  struct malloc_chunk* bk;           // 指向上一个空闲块的指针(free list 链表的反向指针)

  // 只用于较大的内存块,指向比当前内存块大的下一个内存块
  struct malloc_chunk* fd_nextsize;  // 双向链表指针,用于管理按大小排列的空闲块
  struct malloc_chunk* bk_nextsize;  // 双向链表指针,指向下一个比当前块大的空闲块

};

在下面还能看到关于堆结构的注释

/*
   malloc_chunk details:

    (The following includes lightly edited explanations by Colin Plumb.)

    Chunks of memory are maintained using a `boundary tag' method as
    described in e.g., Knuth or Standish.  (See the paper by Paul
    Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a
    survey of such techniques.)  Sizes of free chunks are stored both
    in the front of each chunk and at the end.  This makes
    consolidating fragmented chunks into bigger chunks very fast.  The
    size fields also hold bits representing whether chunks are free or
    in use.

    An allocated chunk looks like this:


    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	    |             Size of previous chunk, if unallocated (P clear)  |
	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	    |             Size of chunk, in bytes                     |A|M|P|
      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	    |             User data starts here...                          .
	    .                                                               .
	    .             (malloc_usable_size() bytes)                      .
	    .                                                               |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	    |             (size of chunk, but used for application data)    |
	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	    |             Size of next chunk, in bytes                |A|0|1|
	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    Where "chunk" is the front of the chunk for the purpose of most of
    the malloc code, but "mem" is the pointer that is returned to the
    user.  "Nextchunk" is the beginning of the next contiguous chunk.

    Chunks always begin on even word boundaries, so the mem portion
    (which is returned to the user) is also on an even word boundary, and
    thus at least double-word aligned.

    Free chunks are stored in circular doubly-linked lists, and look like this:

    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	    |             Size of previous chunk, if unallocated (P clear)  |
	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `head:' |             Size of chunk, in bytes                     |A|0|P|
      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	    |             Forward pointer to next chunk in list             |
	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	    |             Back pointer to previous chunk in list            |
	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	    |             Unused space (may be 0 bytes long)                .
	    .                                                               .
	    .                                                               |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `foot:' |             Size of chunk, in bytes                           |
	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	    |             Size of next chunk, in bytes                |A|0|0|
	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    The P (PREV_INUSE) bit, stored in the unused low-order bit of the
    chunk size (which is always a multiple of two words), is an in-use
    bit for the *previous* chunk.  If that bit is *clear*, then the
    word before the current chunk size contains the previous chunk
    size, and can be used to find the front of the previous chunk.
    The very first chunk allocated always has this bit set,
    preventing access to non-existent (or non-owned) memory. If
    prev_inuse is set for any given chunk, then you CANNOT determine
    the size of the previous chunk, and might even get a memory
    addressing fault when trying to do so.

    The A (NON_MAIN_ARENA) bit is cleared for chunks on the initial,
    main arena, described by the main_arena variable.  When additional
    threads are spawned, each thread receives its own arena (up to a
    configurable limit, after which arenas are reused for multiple
    threads), and the chunks in these arenas have the A bit set.  To
    find the arena for a chunk on such a non-main arena, heap_for_ptr
    performs a bit mask operation and indirection through the ar_ptr
    member of the per-heap header heap_info (see arena.c).

    Note that the `foot' of the current chunk is actually represented
    as the prev_size of the NEXT chunk. This makes it easier to
    deal with alignments etc but can be very confusing when trying
    to extend or adapt this code.

    The three exceptions to all this are:

     1. The special chunk `top' doesn't bother using the
	trailing size field since there is no next contiguous chunk
	that would have to index off it. After initialization, `top'
	is forced to always exist.  If it would become less than
	MINSIZE bytes long, it is replenished.

     2. Chunks allocated via mmap, which have the second-lowest-order
	bit M (IS_MMAPPED) set in their size fields.  Because they are
	allocated one-by-one, each must contain its own trailing size
	field.  If the M bit is set, the other bits are ignored
	(because mmapped chunks are neither in an arena, nor adjacent
	to a freed chunk).  The M bit is also used for chunks which
	originally came from a dumped heap via malloc_set_state in
	hooks.c.

     3. Chunks in fastbins are treated as allocated chunks from the
	point of view of the chunk allocator.  They are consolidated
	with their neighbors only in bulk, in malloc_consolidate.
*/

翻译并整理后

malloc_chunk 详细信息

(以下内容包含由 Colin Plumb 轻微编辑的解释。)

内存块是通过一种 边界标签 方法来管理的,如 Knuth 或 Standish 所述。(有关此类技术的概述,请参见 Paul Wilson 的论文:ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps。)空闲块的大小会同时存储在每个块的前面和后面。这使得将碎片化的块合并为更大的块非常快速。大小字段还包含表示块是空闲还是正在使用的位。

分配的块结构

一个已分配的内存块如下所示:

以下是转换为中文Markdown表格的格式:

位置内容说明
chunk->前一个块的大小(当P标志清除时,表示未分配)
当前块的大小(字节数)|A|M|P|
mem->用户数据起始位置
(可用内存大小:malloc_usable_size()字节)
nextchunk->(块的大小,但用于应用程序数据)
下一个块的大小(字节数)|A|0|1|

其中,chunk 是块的前端,用于大部分 malloc 代码,但 mem 是返回给用户的指针。nextchunk 是下一个连续块的开始。块总是以偶数字边界开始,因此返回给用户的 mem 部分也会以偶数字边界开始,从而至少是双字对齐的。

空闲块结构

空闲块存储在循环双向链表中,结构如下:

以下是转换为中文Markdown表格的格式:

位置内容说明
chunk->前一个块的大小(当P标志清除时,表示未分配)
‘head:’当前块的大小(字节数)|A|0|P|
mem->指向列表中下一个块的前向指针
指向列表中前一个块的后向指针
未使用空间(可能长度为0字节)
nextchunk->
‘foot:’当前块的大小(字节数)
下一个块的大小(字节数)|A|0|0|

关键位说明

  • P(PREV_INUSE)位:存储在块大小的低位(总是一个双字的倍数),表示前一个块是否正在使用。如果该位清除,则当前块之前的字包含前一个块的大小,可用来找到前一个块的前端。分配的第一个块总是将此位设置为 1,防止访问不存在的(或未拥有的)内存。如果某个块的 prev_inuse 位设置,则无法确定前一个块的大小,甚至在尝试访问时可能会引发内存访问错误。

  • A(NON_MAIN_ARENA)位:该位在初始的主 arena 中清除,在由 main_arena 变量描述的 arena 中的块中。如果分配了额外的线程,每个线程会获得自己的 arena(直到达到可配置的限制,之后多个线程共享相同的 arena)。这些 arena 中的块会将 A 位设置为 1。要查找这种非主 arena 中的块所在的 arena,heap_for_ptr 通过位掩码操作和 heap_info 中每个堆头部的 ar_ptr 成员进行间接访问(详见 arena.c)。

特殊情况

当前块的 foot 实际上表示的是下一个块的 prev_size。这种设计使得对齐处理更简便,但在尝试扩展或适应代码时可能会非常困惑。

三个特殊情况

  1. 特殊块 top:由于没有下一个连续块需要索引,因此 top 块不使用尾部的大小字段。在初始化后,top 块必须始终存在。如果它变得小于 MINSIZE 字节,它会被补充。

  2. 通过 mmap 分配的块:这些块在其大小字段中设置了次低位的 M(IS_MMAPPED)位。由于它们是逐一分配的,因此每个块必须包含自己的尾部大小字段。如果 M 位设置,则其他位会被忽略(因为 mmap 分配的块既不在 arena 中,也不与空闲块相邻)。M 位也用于那些最初来自转储堆并通过 malloc_set_state 恢复的块(见 hooks.c)。

  3. 快速分配区的块:从块分配器的角度来看,快速分配区的块被视为已分配的块。它们只在 malloc_consolidate 中与相邻块进行合并,而不是逐块合并。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/928314.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

点灯大师——WIFI控制灯

在之前的教程中&#xff0c;我们学习了 ESP6266 的原理&#xff0c;并动手写了驱动&#xff0c;实现了串口的通讯和 STA、AP、STAAP 三种模式。本次我们就来教大家如何使用 ESP8266 控制灯。这是一个简单的示例&#xff0c;展示了如何将 WIFI 通信与硬件控制相结合&#xff0c;…

如何使用brew安装phpredis扩展?

如何使用brew安装phpredis扩展&#xff1f; phpredis扩展是一个用于PHP语言的Redis客户端扩展&#xff0c;它提供了一组PHP函数&#xff0c;用于与Redis服务器进行交互。 1、cd到php某一版本的bin下 /usr/local/opt/php8.1/bin 2、下载 phpredis git clone https://githu…

Android 使用OpenGLES + MediaPlayer 获取视频截图

概述 Android 获取视频缩略图的方法通常有: ContentResolver: 使用系统数据库MediaMetadataRetriever: 这个是android提供的类&#xff0c;用来获取本地和网络media相关文件的信息ThumbnailUtils: 是在android2.2&#xff08;api8&#xff09;之后新增的一个&#xff0c;该类为…

面向对象(二)——类和对象(上)

1 类的定义 做了关于对象的很多介绍&#xff0c;终于进入代码编写阶段。 本节中重点介绍类和对象的基本定义&#xff0c;属性和方法的基本使用方式。 【示例】类的定义方式 // 每一个源文件必须有且只有一个public class&#xff0c;并且类名和文件名保持一致&#xff01; …

echarts的双X轴,父级居中的相关配置

前言&#xff1a;折腾了一个星期&#xff0c;在最后一天中午&#xff0c;都快要放弃了&#xff0c;后来坚持下来&#xff0c;才有下面结果。 这个效果就相当是复合表头&#xff0c;第一行是子级&#xff0c;第二行是父级。 子级是奇数个时&#xff0c;父级label居中很简单&…

顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测(Maltab)

顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测&#xff08;Maltab&#xff09; 目录 顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测&#xff08;Maltab&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实…

Agile VMO分享:海尔案例

海尔集团是全球最大的家电制造商之一&#xff0c;拥有超过76 000名员工。它获得了2018-2019年全球智能家电品牌前10名和2018-2019年全球消费电子品牌前50名的荣誉。 海尔利用价值流结构将自己组织成一些可以自管理的微型企业。这些微型企业拥有决策&#xff0c;设计和交付新产品…

第七课 Unity编辑器创建的资源优化_UI篇(UGUI)

上期我们学习了简单的Scene优化&#xff0c;接下来我们继续编辑器创建资源的UGUI优化 UI篇&#xff08;UGUI&#xff09; 优化UGUI应从哪些方面入手&#xff1f; 可以从CPU和GPU两方面考虑&#xff0c;CPU方面&#xff0c;避免触发或减少Canvas的Rebuild和Rebatch&#xff0c…

LabVIEW MathScript工具包对运行速度的影响及优化方法

LabVIEW 的 MathScript 工具包 在运行时可能会影响程序的运行速度&#xff0c;主要是由于以下几个原因&#xff1a; 1. 解释型语言执行方式 MathScript 使用的是类似于 MATLAB 的解释型语言&#xff0c;这意味着它不像编译型语言&#xff08;如 C、C 或 LabVIEW 本身的 VI&…

中国移动量子云平台:算力并网590量子比特!

在技术革新的浪潮中&#xff0c;量子计算以其独特的并行处理能力和指数级增长的计算潜力&#xff0c;有望成为未来技术范式变革和颠覆式创新应用的新源泉。中国移动作为通信行业的领军企业&#xff0c;致力于量子计算技术研究&#xff0c;推动量子计算产业的跨越式发展。 量子云…

pytest(二)excel数据驱动

一、excel数据驱动 excel文件内容 excel数据驱动使用方法 import openpyxl import pytestdef get_excel():excel_obj openpyxl.load_workbook("../pytest结合数据驱动-excel/data.xlsx")sheet_obj excel_obj["Sheet1"]values sheet_obj.valuescase_li…

文库 | 从嬴图的技术文档聊起

在技术的浩瀚海洋中&#xff0c;一份优秀的技术文档宛如精准的航海图。它是知识传承的载体&#xff0c;是团队协作的桥梁&#xff0c;更是产品成功的幕后英雄。然而&#xff0c;打造这样一份出色的技术文档并非易事。你是否在为如何清晰阐释复杂技术而苦恼&#xff1f;是否纠结…

flask的第一个应用

本文编写一个简单的实例来记录下flask的使用 文章目录 简单实例flask中的路由无参形式有参形式 参数类型本文小结 简单实例 flask的依赖包都安装好之后&#xff0c;我们就可以写一个最简单的web应用程序了&#xff0c;我们把这个应用程序命名为first.py: from flask import Fla…

【UE5 C++】判断两点连线是否穿过球体

目录 前言 方法一 原理 代码 测试 结果 方法二 原理 一、检查连线与球体的相交情况 二、检查距离与球体半径的关系 三、检查连线与球体的相交 代码 前言 通过数学原理判断空间中任意两点的连线是否穿过球体&#xff0c;再通过射线检测检验算法的正确性。 方法一 …

Python办公——openpyxl处理Excel每个sheet每行 修改为软雅黑9号剧中+边框线

目录 专栏导读背景1、库的介绍①&#xff1a;openpyxl 2、库的安装3、核心代码4、完整代码5、最快的方法(50万行44秒)——表头其余单元格都修改样式总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍…

Figma入门-约束与对齐

Figma入门-约束与对齐 前言 在之前的工作中&#xff0c;大家的原型图都是使用 Axure 制作的&#xff0c;印象中 Figma 一直是个专业设计软件。 最近&#xff0c;很多产品朋友告诉我&#xff0c;很多原型图都开始用Figma制作了&#xff0c;并且很多组件都是内置的&#xff0c…

8. Debian系统中显示屏免密码自动登录

本文介绍如何在Debian系统上&#xff0c;启动后&#xff0c;自动免密登录&#xff0c;不卡在登录界面。 1. 修改lightDM配置文件 嵌入式Debian系统采用lightDM显示管理器&#xff0c;所以&#xff0c;一般需要修改它的配置文件/etc/lightdm/lightdm.conf&#xff0c;找到[Seat…

Unity类银河战士恶魔城学习总结(P156 Audio Settings音频设置)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址&#xff1a;https://www.udemy.com/course/2d-rpg-alexdev/ 本章节实现了音频的大小设置与保存加载 音频管理器 UI_VolumeSlider.cs 定义了 UI_VolumeSlider 类&#xff0c;用于处理与音频设置相关的…

控制访问权限

Swift中的控制访问权限有5种&#xff0c;分别是private&#xff0c;fileprivate&#xff0c;public&#xff0c;open&#xff0c;intelnal。 如果我们没有写访问权限关键字时&#xff0c;默认的访问权限是intelnal 访问控制权限从高到低的顺序是&#xff1a;open > public…

单例模式的析构学习

1、例子 如果单例对象是类的static成员&#xff0c;那么在程序结束时不会调用类的析构函数&#xff0c;如下&#xff1a; #include <iostream> using namespace std;class A{ private:static A* m_ins;//声明&#xff0c;静态指针成员A(){} public:static A* getIns(){…