Libc Heap
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
Heap Basics
Heap은 프로그램이 malloc, calloc 같은 함수를 호출해 데이터를 요청할 때 데이터를 저장할 수 있는 장소입니다. 또한 이 메모리가 더 이상 필요하지 않으면 free 함수를 호출해 반환됩니다.
보시다시피, 이것은 바이너리가 메모리에 로드된 위치 바로 다음에 있습니다 ([heap] 섹션을 확인하세요):
.png)
Basic Chunk Allocation
어떤 데이터가 heap에 저장되도록 요청되면 heap의 일부 공간이 그 데이터에 할당됩니다. 이 공간은 bin에 속하며 요청된 데이터 + bin 헤더 공간 + 최소 bin 크기 오프셋만 chunk로 예약됩니다. 목표는 각 chunk의 위치를 찾기 어렵게 만들지 않으면서 가능한 적은 메모리만 예약하는 것입니다. 이를 위해 metadata chunk 정보가 사용되어 각 사용/해제된 chunk의 위치를 파악합니다.
공간을 예약하는 방법은 사용되는 bin에 따라 다르지만, 일반적인 절차는 다음과 같습니다:
- 프로그램은 먼저 일정량의 메모리를 요청합니다.
- chunks 목록에 요청을 충족할 만큼 큰 사용 가능한 항목이 있으면 그것을 사용합니다.
- 이는 사용 가능한 chunk의 일부만 이번 요청에 사용되고 나머지는 chunks 목록에 남게 될 수도 있다는 것을 의미합니다.
- 목록에 사용 가능한 chunk가 없지만 할당된 heap 메모리에 아직 공간이 있으면 heap 관리자는 새 chunk를 생성합니다.
- 새 chunk를 할당할 충분한 heap 공간이 없으면 heap 관리자는 커널에 heap에 할당된 메모리를 확장하도록 요청한 다음 이 메모리를 사용해 새 chunk를 생성합니다.
- 모든 것이 실패하면
malloc은 null을 반환합니다.
요청한 메모리가 특정 임계값을 넘으면, **mmap**이 요청된 메모리를 매핑하는 데 사용된다는 점에 유의하세요.
Arenas
multithreaded 애플리케이션에서는 heap 관리자가 충돌을 일으킬 수 있는 race conditions을 방지해야 합니다. 초기에는 전역 뮤텍스(global mutex)를 사용해 한 번에 하나의 스레드만 heap에 접근하도록 했으나, 이는 뮤텍스에 의한 병목 현상으로 성능 문제를 초래했습니다.
이를 해결하기 위해 ptmalloc2 heap allocator는 “arenas“를 도입했습니다. 각 arena는 자체 데이터 구조와 뮤텍스를 가진 별도의 heap으로 동작하므로, 서로 다른 arenas를 사용하는 한 여러 스레드가 서로 간섭하지 않고 heap 작업을 수행할 수 있습니다.
기본 “main” arena는 단일 스레드 애플리케이션의 heap 작업을 처리합니다. new threads가 추가되면 heap 관리자는 경쟁을 줄이기 위해 secondary arenas를 할당합니다. 먼저 각 새 스레드를 사용되지 않은 arena에 연결하려 시도하고, 필요하면 새 arena를 생성합니다. 이 과정은 32-bit 시스템에서는 CPU 코어 수의 2배까지, 64-bit 시스템에서는 8배까지 제한됩니다. 한계에 도달하면 threads must share arenas, 이로 인해 경쟁이 발생할 수 있습니다.
main arena가 brk 시스템 호출을 사용해 확장되는 것과 달리, secondary arenas는 mmap과 mprotect를 사용해 “subheaps“를 생성하여 heap 동작을 시뮬레이션합니다. 이는 멀티스레드 작업에서 메모리 관리를 더 유연하게 합니다.
Subheaps
Subheaps는 multithreaded 애플리케이션에서 secondary arenas의 메모리 예비 공간 역할을 하며, 이들이 main heap과 별도로 자신들의 heap 영역을 확장하고 관리할 수 있게 해줍니다. Subheaps가 초기 heap과 어떻게 다른지, 그리고 어떻게 동작하는지 설명하면 다음과 같습니다:
- Initial Heap vs. Subheaps:
- 초기 heap은 프로그램의 바이너리 바로 다음 메모리 위치에 있으며
sbrk시스템 호출을 사용해 확장됩니다. - secondary arenas에서 사용되는 subheaps는 지정된 메모리 영역을 매핑하는 시스템 호출인
mmap을 통해 생성됩니다.
- Memory Reservation with
mmap:
- heap 관리자가 subheap을 생성할 때
mmap을 통해 큰 블록의 메모리를 예약합니다. 이 예약은 즉시 물리 메모리를 할당하지 않으며, 단순히 다른 시스템 프로세스나 할당이 해당 영역을 사용하지 않도록 지정하는 것입니다. - 기본적으로 subheap의 예약 크기는 32-bit 프로세스에서는 1 MB, 64-bit 프로세스에서는 64 MB입니다.
- Gradual Expansion with
mprotect:
- 예약된 메모리 영역은 초기에는
PROT_NONE으로 표시되어 커널이 아직 이 공간에 물리 메모리를 할당할 필요가 없음을 나타냅니다. - subheap을 “grow“하기 위해 heap 관리자는
mprotect를 사용해 페이지 권한을PROT_NONE에서PROT_READ | PROT_WRITE로 변경하고, 그러면 커널이 이전에 예약된 주소에 물리 메모리를 할당합니다. 이러한 단계적 접근 방식은 subheap이 필요에 따라 확장될 수 있게 합니다. - 전체 subheap이 소진되면 heap 관리자는 계속 할당을 위해 새 subheap을 생성합니다.
heap_info
이 struct는 heap의 관련 정보를 저장합니다. 또한 더 많은 할당 이후에 heap 메모리가 연속적이지 않을 수 있으므로, 이 struct는 그 정보도 저장합니다.
// 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
Each heap (main arena or other threads arenas) has a malloc_state structure.
이때 main arena malloc_state 구조체는 libc의 전역 변수이므로 (따라서 libc 메모리 공간에 위치한다)는 점이 중요하다.
스레드 힙들의 malloc_state 구조체들은 각 스레드의 자체 “heap” 내부에 위치한다.
There some interesting things to note from this structure (see C code below):
-
__libc_lock_define (, mutex);Is there to make sure this structure from the heap is accessed by 1 thread at a time -
플래그:
-
#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)
- The `mchunkptr bins[NBINS * 2 - 2];` contains **pointers** to the **first and last chunks** of the small, large and unsorted **bins** (the -2 is because the index 0 is not used)
- Therefore, the **first chunk** of these bins will have a **backwards pointer to this structure** and the **last chunk** of these bins will have a **forward pointer** to this structure. Which basically means that if you can l**eak these addresses in the main arena** you will have a pointer to the structure in the **libc**.
- The structs `struct malloc_state *next;` and `struct malloc_state *next_free;` are linked lists os arenas
- The `top` chunk is the last "chunk", which is basically **all the heap reminding space**. Once the top chunk is "empty", the heap is completely used and it needs to request more space.
- The `last reminder` chunk comes from cases where an exact size chunk is not available and therefore a bigger chunk is splitter, a pointer remaining part is placed here.
```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
이 구조체는 특정 메모리 chunk를 나타냅니다. 다양한 필드는 allocated 및 unallocated chunk에 대해 서로 다른 의미를 갖습니다.
// 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;
앞서 언급했듯이, 이 청크들은 또한 메타데이터를 가지고 있으며, 다음 이미지에 잘 나타나 있다:
.png)
https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png
메타데이터는 일반적으로 0x08B로, 현재 청크 크기를 나타내며 마지막 3비트는 다음을 나타낸다:
A: 1이면 subheap에서 온 것이고, 0이면 main arena에 있다M: 1이면 이 청크는 mmap으로 할당된 공간의 일부이며 heap의 일부가 아니다P: 1이면 이전 청크가 사용 중이다
그 다음에 사용자 데이터 공간이 오고, 마지막으로 청크가 사용 가능할 때(previous chunk size를 표시하기 위한) 0x08B가 온다(또는 할당된 경우 사용자 데이터를 저장한다).
더욱이, 사용 가능할 때(user data가 사용 가능한 경우) 사용자 데이터는 다음과 같은 데이터를 포함하는 데 사용된다:
fd: 다음 청크를 가리키는 포인터bk: 이전 청크를 가리키는 포인터fd_nextsize: 자신보다 작은 리스트의 첫 번째 청크를 가리키는 포인터bk_nextsize: 자신보다 큰 리스트의 첫 번째 청크를 가리키는 포인터
.png)
https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png
Tip
이렇게 리스트를 연결하면 모든 청크를 등록하기 위한 배열이 필요 없어진다는 점에 주목하라.
청크 포인터
malloc을 사용하면 작성 가능한 내용을 가리키는 포인터가 반환된다(헤더 바로 다음). 그러나 청크를 관리할 때는 헤더(metadata)의 시작을 가리키는 포인터가 필요하다.
이러한 변환에는 다음 함수들이 사용된다:
// 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))
정렬 및 최소 크기
pointer to the chunk과 0x0f는 0이어야 한다.
// 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);
}
총 필요한 공간을 계산할 때 prev_size 필드에 데이터를 저장할 수 있으므로 SIZE_SZ는 한 번만 더해집니다. 따라서 초기 헤더만 필요합니다.
Chunk 데이터 가져오기 및 메타데이터 변경
이 함수들은 chunk에 대한 포인터를 받아 동작하며 메타데이터를 확인/설정하는 데 유용합니다:
- chunk flags 확인
// 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)
- 크기 및 pointers to other chunks
/*
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)))
- Insue 비트
/* 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))
- 헤더와 푸터 설정 (chunk nos 사용 시
/* 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))
- chunk 내부의 실제 사용 가능한 데이터 크기를 얻는다
#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;
}
예제
빠른 Heap 예제
빠른 Heap 예제는 https://guyinatuxedo.github.io/25-heap/index.html의 예제를 arm64용으로 만든 것입니다:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(void)
{
char *ptr;
ptr = malloc(0x10);
strcpy(ptr, "panda");
}
Set a breakpoint at the end of the main function and lets find out where the information was stored:
.png)
It’s possible to see that the string panda was stored at 0xaaaaaaac12a0 (which was the address given as response by malloc inside x0). Checking 0x10 bytes before it’s possible to see that the 0x0 represents that the 이전 청크가 사용되지 않음 (길이 0)이고 이 청크의 길이가 0x21임을 알 수 있다.
여분으로 예약된 공간(0x21-0x10=0x11)은 추가된 헤더(0x10) 때문에 생긴 것이고 0x1은 0x21B가 예약되었다는 의미가 아니라 현재 헤더 길이의 마지막 3비트가 몇 가지 특별한 의미를 갖는다는 뜻이다. 길이는 항상 16-byte 정렬(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
멀티스레딩 예제
멀티스레드
```c #includevoid* 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>
이전 예제를 디버깅하면 처음에는 arena가 1개만 있음을 확인할 수 있다:
<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>
그 안에는 몇 개의 chunk가 존재한다:
<figure><img src="../../images/image (2) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
## Bins 및 메모리 할당/해제
bins가 무엇인지, 어떻게 구성되어 있는지, 메모리가 어떻게 할당되고 해제되는지 확인하라:
<a class="content_ref" href="bins-and-memory-allocations.md"><span class="content_ref_label">Bins & Memory Allocations</span></a>
## Heap Functions 보안 검사
힙과 관련된 함수들은 작업을 수행하기 전에 힙이 손상되지 않았는지 확인하기 위해 특정 검사를 수행한다:
<a class="content_ref" href="heap-memory-functions/heap-functions-security-checks.md"><span class="content_ref_label">Heap Functions Security Checks</span></a>
## 사례 연구
실제 버그에서 파생된 allocator-specific primitives를 연구하라:
<a class="content_ref" href="virtualbox-slirp-nat-packet-heap-exploitation.md"><span class="content_ref_label">Virtualbox Slirp Nat Packet Heap Exploitation</span></a>
## 참고 자료
- [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/)
> [!TIP]
> AWS 해킹 배우기 및 연습하기:<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> GCP 해킹 배우기 및 연습하기: <img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> Azure 해킹 배우기 및 연습하기: <img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>HackTricks 지원하기</summary>
>
> - [**구독 계획**](https://github.com/sponsors/carlospolop) 확인하기!
> - **💬 [**디스코드 그룹**](https://discord.gg/hRep4RUj7f) 또는 [**텔레그램 그룹**](https://t.me/peass)에 참여하거나 **트위터** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**를 팔로우하세요.**
> - **[**HackTricks**](https://github.com/carlospolop/hacktricks) 및 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.**
>
> </details>
HackTricks

