Linux堆内存分配与管理机制详解及其利用方法
字数 1854 2025-08-24 16:48:07

Linux堆内存分配与管理机制详解及其利用方法

堆介绍

堆(Heap)是一个用于动态内存分配的数据结构,进程可以在运行时通过系统调用(如malloc和free)向操作系统请求和释放内存。堆与栈不同,栈是用于自动变量的快速内存分配,而堆则用于需要灵活大小和生存期的动态数据。

程序可以使用如malloc、calloc、realloc等函数在堆上动态分配内存。当内存不再需要时,使用free函数释放。

堆的工作方式

堆内存通过malloc函数分配,分配时会包含元数据信息:

  • 0x49-1:表示分配的空间大小(0x48),最低位1表示当前空间已被使用
  • 0x00020fd9:剩余可分配的堆空间大小
  • 实际分配大小 = 请求大小 + 元数据大小(通常为8字节)

堆的常见漏洞及原理

内存泄漏

当程序分配内存后忘记释放,未被释放的内存将无法再被使用,导致内存泄漏。长时间运行的程序如果不断泄漏内存,最终会导致可用内存耗尽。

void memoryLeakExample() {
    char *leak = (char *)malloc(1024);
    snprintf(leak, 1024, "This is a memory leak example");
    // 漏掉了free(leak)
}

使用已释放的内存(Use-After-Free)

在释放内存后,继续使用该内存区域。这通常会导致未定义行为,可能被攻击者利用。

void useAfterFree() {
    char *data = (char *)malloc(100);
    strcpy(data, "Hello, World!");
    free(data);
    printf("Data after free: %s\n", data); // UAF
}

双重释放(Double Free)

试图再次释放已经释放过的内存块。这可能会破坏堆的内部管理结构。

void doubleFree() {
    char *data = (char *)malloc(100);
    free(data);
    free(data); // Double Free
}

元数据破坏(Heap Metadata Corruption)

堆管理器使用元数据来跟踪内存块的分配和释放状态。如果程序修改这些元数据,会导致堆管理器无法正确管理内存。

堆利用技术

unlink攻击

unlink操作是从双向链表中移除一个空闲内存块的过程。攻击者可以控制fd和bk指针来覆盖任意内存。

#define unlink(P, BK, FD) { \
  FD = P->fd;               \
  BK = P->bk;               \
  FD->bk = BK;              \
  BK->fd = FD;              \
}

House of系列攻击技术

  1. House of Prime:通过修改堆块的大小和边界,破坏堆的管理结构
  2. House of Force:通过修改堆块的大小,使得内存分配器分配不正确的内存块
  3. House of Mind:通过修改堆块的指针,使得内存分配器在错误的位置进行读写操作
  4. House of Lore:通过修改堆块的元数据,使得内存分配器在错误的位置进行分配操作
  5. House of Spirit:利用堆管理器中的链表结构,通过伪造链表指针进行攻击
  6. House of Chaos:通过破坏堆管理器的内部状态,使得堆管理器的行为异常
  7. House of Underground:通过修改堆块的指针和大小,使得堆管理器在错误的位置进行内存分配和释放
  8. House of Orange:利用伪造chunk的size和prev_size字段,造成unlink操作被利用

tcache机制

tcache(线程缓存)是ptmalloc中用于优化小内存分配和释放的机制。每个线程都有独立的tcache结构,避免锁争用。

tcache_entry结构:

typedef struct tcache_entry {
    struct tcache_entry *next;
    struct tcache_perthread_struct *key;
} tcache_entry;

tcache分配和释放过程:

  1. 分配时首先检查对应大小的桶是否有可用块
  2. 释放时将块添加到对应大小的桶中
  3. 使用单链表结构管理空闲块

tcache攻击技术

  1. Double Free:通过覆盖key值绕过双重释放检查
  2. tcache污染:通过破坏tcache_entry->next指针,可能导致非法内存访问或控制程序流

chunk与metadata

chunk结构

在内存管理中,块(chunk)是分配器管理的内存区域的基本单位,包含元数据和实际数据。

chunk元数据:

  • mchunk_prev_size:前一个块的大小
  • mchunk_size:当前块的大小(包含标志位)
  • fd/bk:空闲块的双向链表指针

ptmalloc的缓存机制

  1. tcache bin:线程局部缓存,16-1032字节
  2. fast bin:单链表,最多160字节
  3. unsorted bin:双链表,临时存储释放块
  4. small bin:双链表,最多512字节
  5. large bin:双链表,超过512字节

实际利用示例

覆盖GOT表

通过堆溢出修改函数指针(如printf的GOT表项),使其跳转到攻击者控制的代码。

import struct

# 覆盖puts@GOT为winner函数地址
buf3 = ''
buf3 += '\xff'*4
buf3 += struct.pack('I', 0x804b128-12) # puts@GOT-12
buf3 += struct.pack('I', 0x804c040) # shellcode地址

负数size利用

使用-4(0xfffffffc)作为块大小,绕过检查并触发unlink操作。

buf2 += struct.pack('I', 0xfffffffc)*2 # 覆盖prev_size和size

防御措施

  1. 及时释放内存并置空指针
  2. 使用安全的内存管理函数(如calloc代替malloc)
  3. 启用堆保护机制(如GNU Libc的防护措施)
  4. 使用内存安全语言或静态分析工具检测漏洞

堆漏洞利用需要对内存管理机制有深入理解,攻击者通过精心构造的输入可以绕过各种防护措施实现代码执行。防御方需要结合多种防护手段来降低风险。

Linux堆内存分配与管理机制详解及其利用方法 堆介绍 堆(Heap)是一个用于动态内存分配的数据结构,进程可以在运行时通过系统调用(如malloc和free)向操作系统请求和释放内存。堆与栈不同,栈是用于自动变量的快速内存分配,而堆则用于需要灵活大小和生存期的动态数据。 程序可以使用如malloc、calloc、realloc等函数在堆上动态分配内存。当内存不再需要时,使用free函数释放。 堆的工作方式 堆内存通过malloc函数分配,分配时会包含元数据信息: 0x49-1:表示分配的空间大小(0x48),最低位1表示当前空间已被使用 0x00020fd9:剩余可分配的堆空间大小 实际分配大小 = 请求大小 + 元数据大小(通常为8字节) 堆的常见漏洞及原理 内存泄漏 当程序分配内存后忘记释放,未被释放的内存将无法再被使用,导致内存泄漏。长时间运行的程序如果不断泄漏内存,最终会导致可用内存耗尽。 使用已释放的内存(Use-After-Free) 在释放内存后,继续使用该内存区域。这通常会导致未定义行为,可能被攻击者利用。 双重释放(Double Free) 试图再次释放已经释放过的内存块。这可能会破坏堆的内部管理结构。 元数据破坏(Heap Metadata Corruption) 堆管理器使用元数据来跟踪内存块的分配和释放状态。如果程序修改这些元数据,会导致堆管理器无法正确管理内存。 堆利用技术 unlink攻击 unlink操作是从双向链表中移除一个空闲内存块的过程。攻击者可以控制fd和bk指针来覆盖任意内存。 House of系列攻击技术 House of Prime :通过修改堆块的大小和边界,破坏堆的管理结构 House of Force :通过修改堆块的大小,使得内存分配器分配不正确的内存块 House of Mind :通过修改堆块的指针,使得内存分配器在错误的位置进行读写操作 House of Lore :通过修改堆块的元数据,使得内存分配器在错误的位置进行分配操作 House of Spirit :利用堆管理器中的链表结构,通过伪造链表指针进行攻击 House of Chaos :通过破坏堆管理器的内部状态,使得堆管理器的行为异常 House of Underground :通过修改堆块的指针和大小,使得堆管理器在错误的位置进行内存分配和释放 House of Orange :利用伪造chunk的size和prev_ size字段,造成unlink操作被利用 tcache机制 tcache(线程缓存)是ptmalloc中用于优化小内存分配和释放的机制。每个线程都有独立的tcache结构,避免锁争用。 tcache_ entry结构: tcache分配和释放过程: 分配时首先检查对应大小的桶是否有可用块 释放时将块添加到对应大小的桶中 使用单链表结构管理空闲块 tcache攻击技术 Double Free :通过覆盖key值绕过双重释放检查 tcache污染 :通过破坏tcache_ entry->next指针,可能导致非法内存访问或控制程序流 chunk与metadata chunk结构 在内存管理中,块(chunk)是分配器管理的内存区域的基本单位,包含元数据和实际数据。 chunk元数据: mchunk_ prev_ size:前一个块的大小 mchunk_ size:当前块的大小(包含标志位) fd/bk:空闲块的双向链表指针 ptmalloc的缓存机制 tcache bin :线程局部缓存,16-1032字节 fast bin :单链表,最多160字节 unsorted bin :双链表,临时存储释放块 small bin :双链表,最多512字节 large bin :双链表,超过512字节 实际利用示例 覆盖GOT表 通过堆溢出修改函数指针(如printf的GOT表项),使其跳转到攻击者控制的代码。 负数size利用 使用-4(0xfffffffc)作为块大小,绕过检查并触发unlink操作。 防御措施 及时释放内存并置空指针 使用安全的内存管理函数(如calloc代替malloc) 启用堆保护机制(如GNU Libc的防护措施) 使用内存安全语言或静态分析工具检测漏洞 堆漏洞利用需要对内存管理机制有深入理解,攻击者通过精心构造的输入可以绕过各种防护措施实现代码执行。防御方需要结合多种防护手段来降低风险。