- Great article: https://azeria-labs.com/heap-exploitation-part-1-understanding-the-glibc-heap-implementation/
- https://heap-exploitation.dhavalkapil.com/diving_into_glibc_heap/bins_chunks
- https://0x434b.dev/overview-of-glibc-heap-exploitation-techniques/#basics
## Allocation strat:
- When calling malloc (and similar functions), the returned buffer on the heap is called a **chunk**.
- On 32bit, chunks are **8-byte aligned**
- On 64bit, chunks are **16-byte aligned**:
- If request size is `n` in 64bit
- Then real chunk size is: `(n + 8 + 15) & ~15`
- A chunk includes: chunk metadata and actual buffer where user can use
- The order how glibc find/alloc a chunk:
- Looking at previously free'd chunks. If one of them has enough size, return it. Glibc manages previously free'd chunks using some link lists called **bin**
- Looking at the free space at the end of the heap (this space is called "top chunk" or "remainder chunk"), if there is enough space --> create a new chunk there.
- If there is not enough space at the end of the heap --> ask the OS to alloc more space using `sbrk()`, then create a new chunk at the newly provided space. If `sbrk()` fails, then use `mmap()`. If `mmap()` also fails then gives up.
- If the allocation requests size is very very large --> use `mmap()` instead of create a chunk on the heap.
## Arena:
- Basically a strat to prevent race condition while malloc, while also retain some performace for multi-thread programs.
- "Each arena is essentially an entirely different heap that manages its own chunk allocation and free bins completely separately" --> Each thread has its own arena.
- "The initial (“main”) arena of the program only contains the heap we’ve already seen, and for single-threaded applications this is the only arena that the heap manager will ever use"
- If there are too much threads (2x cpu-cores in 32-bit processes and 8x cpu-cores in 64-bit processes), many threads have to share an arena and the mutex strat is used to prevent race condition.
- Read more about how arenas are allocated and managed on the articles above.
## Chunk metadata:
This pic says it all.

Note that the chunk size and 3 "A", "M", "P" bits are stored in the same size_t field
## Bins:
- Linked list containing free'd chunks
### Small bins:
- There are **62** small bins.
- Each small bin contains chunk with **same size**
- Every chunk less than 512 bytes on 32-bit (which are 16, 24, ... 504 bytes) or less than 1024 bytes on 64-bit (which are 32, 48,... 1008 bytes) has its own corresponding small bin.
- Insertions happen at the **HEAD** while removals happen at the **TAIL** (in a FIFO manner).
### Large bins:
- There are **63** large bins.
- Each large bin stores chunks within a size range.
- In each bin, chunks are sorted by size in decreasing order.
### Unsorted bin:
- There is **1** unsorted bin.
- Unlike other bins, this bin is unsort (duh)
- When a chunk is free'd, instead of putting it to small/large bins, the heap manager will merge it with its neighbors, then throw it in unsorted bin.
- During malloc, each chunks in unsorted bin is checked if it fits the request. If yes, use it. If no, it is put into small/large bin.
- Insertion happens at the **HEAD** of the list, removals at **TAIL** (FIFO)
- To leak a libc address, a common technique is to get a chunk into the unsorted bin, because then the forward and backward pointers will point to the main_arena struct inside the struct.
### Fast bin:
- There are **10** fast bins.
- They cover the following chunk size: 16, 24, 32, 40, 48, 56, 64, 72, 80, and 88 bytes (on 32bit, metadata included).
- Chunks less than 0x80 bytes (on x64?) are assigned to the “fastbin”. According to: https://www.bordergate.co.uk/heap-fastbin-exploitation/
- The chunks inside fast bins are not really free'd: they are not merged to their neighbors and are still marked as in use.
- The chunks inside fast bin is *truly* free'd when:
- There is a malloc request is made that is larger than a fastbin can service (i.e., for chunks over 512 bytes or 1024 bytes on 64-bit)
- Freeing any chunk over 64KB (where 64KB is a heuristically chosen value)
- `malloc_trim` or `mallopt` are called by the program.
- Chunks are inserted and removed at the **HEAD** (LIFO)
### Tcache bin:
- Each thread has 64 tcache bins.
- In the case that 2 or more threads has to share an arena, tcache bins help reducing the waiting time for mutex lock.
- Each tcache bin contains a maximum of 7 same-size chunks ranging from 24 to 1032 bytes on 64-bit systems and 12 to 516 bytes on 32-bit systems (metadata size not included in size??).
- Same as unsorted bin, chunks in tcache bin are not truly free'd
- https://hackmd.io/@5Mo2wp7RQdCOYcqKeHl2mw/ByTHN47jf
- Chunks in tcache bin are taken out at `libc_malloc`, not `int_malloc`. However, chunks are put inside tcache inside `int_malloc` ==> So if there is a chunk in tcache when we call malloc(), `_int_malloc()` will never be called.
## Heap exploitation techniques:
Maybe helpful?
- https://heap-exploitation.dhavalkapil.com/attacks
- Helpful tips for pwn: https://github.com/thezakman/CTF-Heaven/blob/master/extra/pwn-tips.md
- https://tc.gts3.org/cs6265/tut/tut09-02-advheap.html
# __malloc_hook, __free_hook:
- Legacy technique, __malloc_hook, __free_hook is removed in glibc 2.34.
- malloc_hook is called when malloc() is called, similar to free_hook
- __malloc_hook is also called when triggering double free corruption: https://blog.osiris.cyber.nyu.edu/2017/09/30/csaw-ctf-2017-auir/