mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
504 lines
21 KiB
Markdown
504 lines
21 KiB
Markdown
# Libc Heap
|
||
|
||
## Heap Basics
|
||
|
||
堆基本上是程序在请求数据时存储数据的地方,调用像 **`malloc`**、`calloc`... 这样的函数。此外,当这些内存不再需要时,可以通过调用 **`free`** 函数将其释放。
|
||
|
||
如图所示,它位于二进制文件加载到内存后的位置(查看 `[heap]` 部分):
|
||
|
||
<figure><img src="../../images/image (1241).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
### Basic Chunk Allocation
|
||
|
||
当请求将某些数据存储在堆中时,会为其分配堆的一部分空间。该空间将属于一个 bin,并且仅为请求的数据 + bin 头的空间 + 最小 bin 大小偏移量保留给该块。目标是尽可能少地保留内存,而不使查找每个块的位置变得复杂。为此,使用元数据块信息来了解每个已用/空闲块的位置。
|
||
|
||
根据使用的 bin,有不同的方法来保留空间,但一般方法如下:
|
||
|
||
- 程序开始时请求一定量的内存。
|
||
- 如果在块列表中有足够大的可用块来满足请求,则将使用该块。
|
||
- 这甚至可能意味着可用块的一部分将用于此请求,其余部分将添加到块列表中。
|
||
- 如果列表中没有可用块,但已分配的堆内存中仍有空间,堆管理器将创建一个新块。
|
||
- 如果没有足够的堆空间来分配新块,堆管理器会请求内核扩展分配给堆的内存,然后使用该内存生成新块。
|
||
- 如果一切都失败,`malloc` 返回 null。
|
||
|
||
请注意,如果请求的 **内存超过阈值**,**`mmap`** 将用于映射请求的内存。
|
||
|
||
## Arenas
|
||
|
||
在 **多线程** 应用程序中,堆管理器必须防止可能导致崩溃的 **竞争条件**。最初,这是通过使用 **全局互斥锁** 来确保一次只有一个线程可以访问堆,但这导致了由于互斥锁引起的瓶颈而产生的 **性能问题**。
|
||
|
||
为了解决这个问题,ptmalloc2 堆分配器引入了“区域”,每个 **区域** 作为一个 **独立的堆**,具有 **自己的** 数据 **结构** 和 **互斥锁**,允许多个线程在不相互干扰的情况下执行堆操作,只要它们使用不同的区域。
|
||
|
||
默认的“主”区域处理单线程应用程序的堆操作。当 **新线程** 被添加时,堆管理器为它们分配 **次要区域** 以减少竞争。它首先尝试将每个新线程附加到未使用的区域,如果需要则创建新的区域,最多达到 32 位系统 CPU 核心数量的 2 倍和 64 位系统的 8 倍。一旦达到限制,**线程必须共享区域**,这可能导致潜在的竞争。
|
||
|
||
与使用 `brk` 系统调用扩展的主区域不同,次要区域使用 `mmap` 和 `mprotect` 创建“子堆”,以模拟堆行为,从而在多线程操作中灵活管理内存。
|
||
|
||
### Subheaps
|
||
|
||
子堆作为多线程应用程序中次要区域的内存储备,允许它们独立于主堆增长和管理自己的堆区域。以下是子堆与初始堆的不同之处及其操作方式:
|
||
|
||
1. **初始堆与子堆**:
|
||
- 初始堆位于程序的二进制文件后面,并通过 `sbrk` 系统调用扩展。
|
||
- 次要区域使用的子堆是通过 `mmap` 创建的,`mmap` 是一个映射指定内存区域的系统调用。
|
||
2. **使用 `mmap` 进行内存保留**:
|
||
- 当堆管理器创建子堆时,它通过 `mmap` 保留一大块内存。此保留不会立即分配内存;它只是指定一个其他系统进程或分配不应使用的区域。
|
||
- 默认情况下,子堆的保留大小为 32 位进程的 1 MB 和 64 位进程的 64 MB。
|
||
3. **使用 `mprotect` 逐步扩展**:
|
||
- 保留的内存区域最初标记为 `PROT_NONE`,表示内核尚不需要为此空间分配物理内存。
|
||
- 为了“增长”子堆,堆管理器使用 `mprotect` 将页面权限从 `PROT_NONE` 更改为 `PROT_READ | PROT_WRITE`,提示内核为先前保留的地址分配物理内存。这种逐步的方法允许子堆根据需要扩展。
|
||
- 一旦整个子堆耗尽,堆管理器将创建一个新的子堆以继续分配。
|
||
|
||
### heap_info <a href="#heap_info" id="heap_info"></a>
|
||
|
||
此结构分配堆的相关信息。此外,堆内存在更多分配后可能不是连续的,此结构还将存储该信息。
|
||
```c
|
||
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/arena.c#L837
|
||
|
||
typedef struct _heap_info
|
||
{
|
||
mstate ar_ptr; /* Arena for this heap. */
|
||
struct _heap_info *prev; /* Previous heap. */
|
||
size_t size; /* Current size in bytes. */
|
||
size_t mprotect_size; /* Size in bytes that has been mprotected
|
||
PROT_READ|PROT_WRITE. */
|
||
size_t pagesize; /* Page size used when allocating the arena. */
|
||
/* Make sure the following data is properly aligned, particularly
|
||
that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of
|
||
MALLOC_ALIGNMENT. */
|
||
char pad[-3 * SIZE_SZ & MALLOC_ALIGN_MASK];
|
||
} heap_info;
|
||
```
|
||
### malloc_state
|
||
|
||
**每个堆**(主区域或其他线程区域)都有一个**`malloc_state`结构。**\
|
||
需要注意的是,**主区域 `malloc_state`** 结构是 **libc中的全局变量**(因此位于libc内存空间中)。\
|
||
在**线程的堆的 `malloc_state`** 结构中,它们位于**各自线程的“堆”内部**。
|
||
|
||
从这个结构中有一些有趣的事情需要注意(见下面的C代码):
|
||
|
||
- `__libc_lock_define (, mutex);` 是为了确保这个堆中的结构一次只被一个线程访问
|
||
- 标志:
|
||
|
||
- ```c
|
||
#define NONCONTIGUOUS_BIT (2U)
|
||
|
||
#define contiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) == 0)
|
||
#define noncontiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) != 0)
|
||
#define set_noncontiguous(M) ((M)->flags |= NONCONTIGUOUS_BIT)
|
||
#define set_contiguous(M) ((M)->flags &= ~NONCONTIGUOUS_BIT)
|
||
```
|
||
|
||
- `mchunkptr bins[NBINS * 2 - 2];` 包含指向**小型、大型和未排序的** **bins**的**第一个和最后一个块**的**指针**(-2是因为索引0未使用)
|
||
- 因此,这些bins的**第一个块**将有一个**指向该结构的反向指针**,而这些bins的**最后一个块**将有一个**指向该结构的前向指针**。这基本上意味着,如果你能**泄漏主区域中的这些地址**,你将获得指向**libc**中结构的指针。
|
||
- 结构 `struct malloc_state *next;` 和 `struct malloc_state *next_free;` 是区域的链表
|
||
- `top` 块是最后一个“块”,基本上是**所有堆剩余空间**。一旦顶块“空”,堆就完全使用,需要请求更多空间。
|
||
- `last reminder` 块来自于没有可用的精确大小块的情况,因此一个更大的块被拆分,剩余部分的指针放置在这里。
|
||
```c
|
||
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1812
|
||
|
||
struct malloc_state
|
||
{
|
||
/* Serialize access. */
|
||
__libc_lock_define (, mutex);
|
||
|
||
/* Flags (formerly in max_fast). */
|
||
int flags;
|
||
|
||
/* Set if the fastbin chunks contain recently inserted free blocks. */
|
||
/* Note this is a bool but not all targets support atomics on booleans. */
|
||
int have_fastchunks;
|
||
|
||
/* Fastbins */
|
||
mfastbinptr fastbinsY[NFASTBINS];
|
||
|
||
/* Base of the topmost chunk -- not otherwise kept in a bin */
|
||
mchunkptr top;
|
||
|
||
/* The remainder from the most recent split of a small request */
|
||
mchunkptr last_remainder;
|
||
|
||
/* Normal bins packed as described above */
|
||
mchunkptr bins[NBINS * 2 - 2];
|
||
|
||
/* Bitmap of bins */
|
||
unsigned int binmap[BINMAPSIZE];
|
||
|
||
/* Linked list */
|
||
struct malloc_state *next;
|
||
|
||
/* Linked list for free arenas. Access to this field is serialized
|
||
by free_list_lock in arena.c. */
|
||
struct malloc_state *next_free;
|
||
|
||
/* Number of threads attached to this arena. 0 if the arena is on
|
||
the free list. Access to this field is serialized by
|
||
free_list_lock in arena.c. */
|
||
INTERNAL_SIZE_T attached_threads;
|
||
|
||
/* Memory allocated from the system in this arena. */
|
||
INTERNAL_SIZE_T system_mem;
|
||
INTERNAL_SIZE_T max_system_mem;
|
||
};
|
||
```
|
||
### malloc_chunk
|
||
|
||
该结构表示特定的内存块。不同字段对已分配和未分配的内存块具有不同的含义。
|
||
```c
|
||
// https://github.com/bminor/glibc/blob/master/malloc/malloc.c
|
||
struct malloc_chunk {
|
||
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk, if it is free. */
|
||
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
|
||
struct malloc_chunk* fd; /* double links -- used only if this chunk is free. */
|
||
struct malloc_chunk* bk;
|
||
/* Only used for large blocks: pointer to next larger size. */
|
||
struct malloc_chunk* fd_nextsize; /* double links -- used only if this chunk is free. */
|
||
struct malloc_chunk* bk_nextsize;
|
||
};
|
||
|
||
typedef struct malloc_chunk* mchunkptr;
|
||
```
|
||
如前所述,这些块也有一些元数据,在此图中很好地表示出来:
|
||
|
||
<figure><img src="../../images/image (1242).png" alt=""><figcaption><p><a href="https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png">https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png</a></p></figcaption></figure>
|
||
|
||
元数据通常是0x08B,表示当前块大小,使用最后3位表示:
|
||
|
||
- `A`:如果为1,则来自子堆,如果为0,则在主区域
|
||
- `M`:如果为1,则该块是使用mmap分配的空间的一部分,而不是堆的一部分
|
||
- `P`:如果为1,则前一个块正在使用中
|
||
|
||
然后是用户数据的空间,最后是0x08B,用于指示块可用时的前一个块大小(或在分配时存储用户数据)。
|
||
|
||
此外,当可用时,用户数据也用于包含一些数据:
|
||
|
||
- **`fd`**:指向下一个块的指针
|
||
- **`bk`**:指向前一个块的指针
|
||
- **`fd_nextsize`**:指向列表中第一个小于自身的块的指针
|
||
- **`bk_nextsize`**:指向列表中第一个大于自身的块的指针
|
||
|
||
<figure><img src="../../images/image (1243).png" alt=""><figcaption><p><a href="https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png">https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png</a></p></figcaption></figure>
|
||
|
||
> [!NOTE]
|
||
> 注意以这种方式链接列表可以避免需要一个数组来注册每一个块。
|
||
|
||
### 块指针
|
||
|
||
当使用malloc时,返回一个可以写入的内容的指针(就在头部之后),然而,在管理块时,需要一个指向头部(元数据)开始的指针。\
|
||
为这些转换使用这些函数:
|
||
```c
|
||
// https://github.com/bminor/glibc/blob/master/malloc/malloc.c
|
||
|
||
/* Convert a chunk address to a user mem pointer without correcting the tag. */
|
||
#define chunk2mem(p) ((void*)((char*)(p) + CHUNK_HDR_SZ))
|
||
|
||
/* Convert a user mem pointer to a chunk address and extract the right tag. */
|
||
#define mem2chunk(mem) ((mchunkptr)tag_at (((char*)(mem) - CHUNK_HDR_SZ)))
|
||
|
||
/* The smallest possible chunk */
|
||
#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))
|
||
|
||
/* The smallest size we can malloc is an aligned minimal chunk */
|
||
|
||
#define MINSIZE \
|
||
(unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
|
||
```
|
||
### 对齐和最小大小
|
||
|
||
指向块的指针和 `0x0f` 必须为 0。
|
||
```c
|
||
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/sysdeps/generic/malloc-size.h#L61
|
||
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
|
||
|
||
// https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/sysdeps/i386/malloc-alignment.h
|
||
#define MALLOC_ALIGNMENT 16
|
||
|
||
|
||
// https://github.com/bminor/glibc/blob/master/malloc/malloc.c
|
||
/* Check if m has acceptable alignment */
|
||
#define aligned_OK(m) (((unsigned long)(m) & MALLOC_ALIGN_MASK) == 0)
|
||
|
||
#define misaligned_chunk(p) \
|
||
((uintptr_t)(MALLOC_ALIGNMENT == CHUNK_HDR_SZ ? (p) : chunk2mem (p)) \
|
||
& MALLOC_ALIGN_MASK)
|
||
|
||
|
||
/* pad request bytes into a usable size -- internal version */
|
||
/* Note: This must be a macro that evaluates to a compile time constant
|
||
if passed a literal constant. */
|
||
#define request2size(req) \
|
||
(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \
|
||
MINSIZE : \
|
||
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
|
||
|
||
/* Check if REQ overflows when padded and aligned and if the resulting
|
||
value is less than PTRDIFF_T. Returns the requested size or
|
||
MINSIZE in case the value is less than MINSIZE, or 0 if any of the
|
||
previous checks fail. */
|
||
static inline size_t
|
||
checked_request2size (size_t req) __nonnull (1)
|
||
{
|
||
if (__glibc_unlikely (req > PTRDIFF_MAX))
|
||
return 0;
|
||
|
||
/* When using tagged memory, we cannot share the end of the user
|
||
block with the header for the next chunk, so ensure that we
|
||
allocate blocks that are rounded up to the granule size. Take
|
||
care not to overflow from close to MAX_SIZE_T to a small
|
||
number. Ideally, this would be part of request2size(), but that
|
||
must be a macro that produces a compile time constant if passed
|
||
a constant literal. */
|
||
if (__glibc_unlikely (mtag_enabled))
|
||
{
|
||
/* Ensure this is not evaluated if !mtag_enabled, see gcc PR 99551. */
|
||
asm ("");
|
||
|
||
req = (req + (__MTAG_GRANULE_SIZE - 1)) &
|
||
~(size_t)(__MTAG_GRANULE_SIZE - 1);
|
||
}
|
||
|
||
return request2size (req);
|
||
}
|
||
```
|
||
请注意,在计算所需的总空间时,仅添加了 `SIZE_SZ` 1 次,因为 `prev_size` 字段可以用来存储数据,因此只需要初始头部。
|
||
|
||
### 获取块数据并更改元数据
|
||
|
||
这些函数通过接收指向块的指针来工作,并且对于检查/设置元数据非常有用:
|
||
|
||
- 检查块标志
|
||
```c
|
||
// From https://github.com/bminor/glibc/blob/master/malloc/malloc.c
|
||
|
||
|
||
/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */
|
||
#define PREV_INUSE 0x1
|
||
|
||
/* extract inuse bit of previous chunk */
|
||
#define prev_inuse(p) ((p)->mchunk_size & PREV_INUSE)
|
||
|
||
|
||
/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
|
||
#define IS_MMAPPED 0x2
|
||
|
||
/* check for mmap()'ed chunk */
|
||
#define chunk_is_mmapped(p) ((p)->mchunk_size & IS_MMAPPED)
|
||
|
||
|
||
/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained
|
||
from a non-main arena. This is only set immediately before handing
|
||
the chunk to the user, if necessary. */
|
||
#define NON_MAIN_ARENA 0x4
|
||
|
||
/* Check for chunk from main arena. */
|
||
#define chunk_main_arena(p) (((p)->mchunk_size & NON_MAIN_ARENA) == 0)
|
||
|
||
/* Mark a chunk as not being on the main arena. */
|
||
#define set_non_main_arena(p) ((p)->mchunk_size |= NON_MAIN_ARENA)
|
||
```
|
||
- 大小和指向其他块的指针
|
||
```c
|
||
/*
|
||
Bits to mask off when extracting size
|
||
|
||
Note: IS_MMAPPED is intentionally not masked off from size field in
|
||
macros for which mmapped chunks should never be seen. This should
|
||
cause helpful core dumps to occur if it is tried by accident by
|
||
people extending or adapting this malloc.
|
||
*/
|
||
#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
|
||
|
||
/* Get size, ignoring use bits */
|
||
#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))
|
||
|
||
/* Like chunksize, but do not mask SIZE_BITS. */
|
||
#define chunksize_nomask(p) ((p)->mchunk_size)
|
||
|
||
/* Ptr to next physical malloc_chunk. */
|
||
#define next_chunk(p) ((mchunkptr) (((char *) (p)) + chunksize (p)))
|
||
|
||
/* Size of the chunk below P. Only valid if !prev_inuse (P). */
|
||
#define prev_size(p) ((p)->mchunk_prev_size)
|
||
|
||
/* Set the size of the chunk below P. Only valid if !prev_inuse (P). */
|
||
#define set_prev_size(p, sz) ((p)->mchunk_prev_size = (sz))
|
||
|
||
/* Ptr to previous physical malloc_chunk. Only valid if !prev_inuse (P). */
|
||
#define prev_chunk(p) ((mchunkptr) (((char *) (p)) - prev_size (p)))
|
||
|
||
/* Treat space at ptr + offset as a chunk */
|
||
#define chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s)))
|
||
```
|
||
- 确保位
|
||
```c
|
||
/* extract p's inuse bit */
|
||
#define inuse(p) \
|
||
((((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size) & PREV_INUSE)
|
||
|
||
/* set/clear chunk as being inuse without otherwise disturbing */
|
||
#define set_inuse(p) \
|
||
((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size |= PREV_INUSE
|
||
|
||
#define clear_inuse(p) \
|
||
((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size &= ~(PREV_INUSE)
|
||
|
||
|
||
/* check/set/clear inuse bits in known places */
|
||
#define inuse_bit_at_offset(p, s) \
|
||
(((mchunkptr) (((char *) (p)) + (s)))->mchunk_size & PREV_INUSE)
|
||
|
||
#define set_inuse_bit_at_offset(p, s) \
|
||
(((mchunkptr) (((char *) (p)) + (s)))->mchunk_size |= PREV_INUSE)
|
||
|
||
#define clear_inuse_bit_at_offset(p, s) \
|
||
(((mchunkptr) (((char *) (p)) + (s)))->mchunk_size &= ~(PREV_INUSE))
|
||
```
|
||
- 设置页眉和页脚(当使用块编号时)
|
||
```c
|
||
/* Set size at head, without disturbing its use bit */
|
||
#define set_head_size(p, s) ((p)->mchunk_size = (((p)->mchunk_size & SIZE_BITS) | (s)))
|
||
|
||
/* Set size/use field */
|
||
#define set_head(p, s) ((p)->mchunk_size = (s))
|
||
|
||
/* Set size at footer (only when chunk is not in use) */
|
||
#define set_foot(p, s) (((mchunkptr) ((char *) (p) + (s)))->mchunk_prev_size = (s))
|
||
```
|
||
- 获取块内实际可用数据的大小
|
||
```c
|
||
#pragma GCC poison mchunk_size
|
||
#pragma GCC poison mchunk_prev_size
|
||
|
||
/* This is the size of the real usable data in the chunk. Not valid for
|
||
dumped heap chunks. */
|
||
#define memsize(p) \
|
||
(__MTAG_GRANULE_SIZE > SIZE_SZ && __glibc_unlikely (mtag_enabled) ? \
|
||
chunksize (p) - CHUNK_HDR_SZ : \
|
||
chunksize (p) - CHUNK_HDR_SZ + (chunk_is_mmapped (p) ? 0 : SIZE_SZ))
|
||
|
||
/* If memory tagging is enabled the layout changes to accommodate the granule
|
||
size, this is wasteful for small allocations so not done by default.
|
||
Both the chunk header and user data has to be granule aligned. */
|
||
_Static_assert (__MTAG_GRANULE_SIZE <= CHUNK_HDR_SZ,
|
||
"memory tagging is not supported with large granule.");
|
||
|
||
static __always_inline void *
|
||
tag_new_usable (void *ptr)
|
||
{
|
||
if (__glibc_unlikely (mtag_enabled) && ptr)
|
||
{
|
||
mchunkptr cp = mem2chunk(ptr);
|
||
ptr = __libc_mtag_tag_region (__libc_mtag_new_tag (ptr), memsize (cp));
|
||
}
|
||
return ptr;
|
||
}
|
||
```
|
||
## 示例
|
||
|
||
### 快速堆示例
|
||
|
||
来自 [https://guyinatuxedo.github.io/25-heap/index.html](https://guyinatuxedo.github.io/25-heap/index.html) 的快速堆示例,但在 arm64:
|
||
```c
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
void main(void)
|
||
{
|
||
char *ptr;
|
||
ptr = malloc(0x10);
|
||
strcpy(ptr, "panda");
|
||
}
|
||
```
|
||
在主函数的末尾设置一个断点,让我们找出信息存储的位置:
|
||
|
||
<figure><img src="../../images/image (1239).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
可以看到字符串 panda 存储在 `0xaaaaaaac12a0`(这是 malloc 在 `x0` 中给出的地址)。检查 0x10 字节之前,可以看到 `0x0` 表示 **前一个块未使用**(长度为 0),而这个块的长度为 `0x21`。
|
||
|
||
保留的额外空间(0x21-0x10=0x11)来自 **添加的头部**(0x10),而 0x1 并不意味着它被保留为 0x21B,而是当前头部长度的最后 3 位具有一些特殊含义。由于长度始终是 16 字节对齐的(在 64 位机器上),这些位实际上永远不会被长度数字使用。
|
||
```
|
||
0x1: Previous in Use - Specifies that the chunk before it in memory is in use
|
||
0x2: Is MMAPPED - Specifies that the chunk was obtained with mmap()
|
||
0x4: Non Main Arena - Specifies that the chunk was obtained from outside of the main arena
|
||
```
|
||
### 多线程示例
|
||
|
||
<details>
|
||
|
||
<summary>多线程</summary>
|
||
```c
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <pthread.h>
|
||
#include <unistd.h>
|
||
#include <sys/types.h>
|
||
|
||
|
||
void* threadFuncMalloc(void* arg) {
|
||
printf("Hello from thread 1\n");
|
||
char* addr = (char*) malloc(1000);
|
||
printf("After malloc and before free in thread 1\n");
|
||
free(addr);
|
||
printf("After free in thread 1\n");
|
||
}
|
||
|
||
void* threadFuncNoMalloc(void* arg) {
|
||
printf("Hello from thread 2\n");
|
||
}
|
||
|
||
|
||
int main() {
|
||
pthread_t t1;
|
||
void* s;
|
||
int ret;
|
||
char* addr;
|
||
|
||
printf("Before creating thread 1\n");
|
||
getchar();
|
||
ret = pthread_create(&t1, NULL, threadFuncMalloc, NULL);
|
||
getchar();
|
||
|
||
printf("Before creating thread 2\n");
|
||
ret = pthread_create(&t1, NULL, threadFuncNoMalloc, NULL);
|
||
|
||
printf("Before exit\n");
|
||
getchar();
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
</details>
|
||
|
||
调试前面的示例可以看到,开始时只有 1 个 arena:
|
||
|
||
<figure><img src="../../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
然后,在调用第一个线程,即调用 malloc 的线程后,创建了一个新的 arena:
|
||
|
||
<figure><img src="../../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
在其中可以找到一些 chunks:
|
||
|
||
<figure><img src="../../images/image (2) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
## Bins & Memory Allocations/Frees
|
||
|
||
检查 bins 是什么,它们是如何组织的,以及内存是如何分配和释放的:
|
||
|
||
{{#ref}}
|
||
bins-and-memory-allocations.md
|
||
{{#endref}}
|
||
|
||
## Heap Functions Security Checks
|
||
|
||
涉及堆的函数在执行其操作之前会进行某些检查,以确保堆没有被破坏:
|
||
|
||
{{#ref}}
|
||
heap-memory-functions/heap-functions-security-checks.md
|
||
{{#endref}}
|
||
|
||
## References
|
||
|
||
- [https://azeria-labs.com/heap-exploitation-part-1-understanding-the-glibc-heap-implementation/](https://azeria-labs.com/heap-exploitation-part-1-understanding-the-glibc-heap-implementation/)
|
||
- [https://azeria-labs.com/heap-exploitation-part-2-glibc-heap-free-bins/](https://azeria-labs.com/heap-exploitation-part-2-glibc-heap-free-bins/)
|