堆溢出學習筆記(linux)
本文主要是linux下堆的數據結構及堆調試、堆溢出利用的一些基礎知識
首先,linux下堆的數據結構如下
/* This struct declaration is misleading (but accurate and necessary). It declares a "view" into memory allowing access to necessary fields at known offsets from a given base. See explanation below. */ struct malloc_chunk { INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */ INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */ struct malloc_chunk* fd; /* double links -- used only if 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 free. */ struct malloc_chunk* bk_nextsize; };
- prev_size, 如果該 chunk 的物理相鄰的前一地址chunk(兩個指針的地址差值為前一chunk大小)是空閑的話,那該字段記錄的是前一個 chunk 的大小(包括 chunk 頭)。否則,該字段可以用來存儲物理相鄰的前一個chunk 的數據。這裏的前一 chunk 指的是較低地址的 chunk 。
- size ,該 chunk 的大小,大小必須是 2 * SIZE_SZ 的整數倍。如果申請的內存大小不是 2 * SIZE_SZ 的整數倍,會被轉換滿足大小的最小的 2 * SIZE_SZ 的倍數。32 位系統中,SIZE_SZ 是 4;64 位系統中,SIZE_SZ 是 8。 該字段的低三個比特位對 chunk 的大小沒有影響,它們從高到低分別表示
- NON_MAIN_ARENA,記錄當前 chunk 是否不屬於主線程,1表示不屬於,0表示屬於。
- IS_MAPPED,記錄當前 chunk 是否是由 mmap 分配的。
- PREV_INUSE,記錄前一個 chunk 塊是否被分配。一般來說,堆中第一個被分配的內存塊的 size 字段的P位都會被設置為1,以便於防止訪問前面的非法內存。當一個 chunk 的 size 的 P 位為 0 時,我們能通過 prev_size 字段來獲取上一個 chunk 的大小以及地址。這也方便進行空閑chunk之間的合並。
- fd,bk。 chunk 處於分配狀態時,從 fd 字段開始是用戶的數據。chunk 空閑時,會被添加到對應的空閑管理鏈表中,其字段的含義如下
- fd 指向下一個(非物理相鄰)空閑的 chunk
- bk 指向上一個(非物理相鄰)空閑的 chunk
- 通過 fd 和 bk 可以將空閑的 chunk 塊加入到空閑的 chunk 塊鏈表進行統一管理
- fd_nextsize, bk_nextsize,也是只有 chunk 空閑的時候才使用,不過其用於較大的 chunk(large chunk)。
- fd_nextsize 指向前一個與當前 chunk 大小不同的第一個空閑塊,不包含 bin 的頭指針。
- bk_nextsize 指向後一個與當前 chunk 大小不同的第一個空閑塊,不包含 bin 的頭指針。
- 一般空閑的 large chunk 在 fd 的遍歷順序中,按照由大到小的順序排列。這樣做可以避免在尋找合適chunk 時挨個遍歷。
以上內容摘自CTF-WIKI https://ctf-wiki.github.io/ctf-wiki/pwn/heap/heap_structure/#top-chunk
給出一個簡單堆溢出的例子
#include <stdio.h> int main(void) { char *chunk; chunk=malloc(24); puts("Get input:"); gets(chunk); return 0; }
gcc -no-pie -p example1 example1.c編譯程序
objdump -d example1查看main函數地址,然後gdb在main函數起始位置下斷點。
當執行到0x400589時查看rax內容即為malloc分配堆的起始地址。
執行完0x405a5時 x/10x 0x602250用‘A‘*100覆蓋堆查看堆溢出情況(Gdb指令查看手冊https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf)
這裏查看0x602250的原因是INTERNAL_SIZE_T默認和size_t一致,32位系統下size_t 4字節,64位系統下size_t 8字節。malloc返回的堆地址指針實際是 struct malloc_chunk* fd的fd,所以64位系統下查看堆首需要用返回的堆地址減去16字節的堆頭。
我們在重新看一下堆覆蓋之前的堆內容
size部分的最後三個字節分別表示特定含義(見上數據結構),用戶真正可用的堆地址是0X602260-0X60226F,共32字節。申請24字節分配32字節的原因是32位系統8字節對齊,64位16位對齊。
0X602270是top chunk的內容,top chunk是在第一次執行malloc時heap 會被分為兩塊,一塊給用戶,剩下的那塊就是 top chunk。top chunk就是當前堆的物理地址最高chunk,這個chunk不屬於任何bin。
未完待續。。。
堆溢出學習筆記(linux)