malloc 结构体_二进制安全之堆溢出(系列)——堆基础 amp; 结构(二)
哈嘍啊
這里是二進制安全之堆溢出(系列)第二期“堆基礎(chǔ) & 結(jié)構(gòu)”第二節(jié)!!
話不多說,直接上干貨!
微觀結(jié)構(gòu)
函數(shù)執(zhí)行流程
void *malloc (size_t bytes) void *__libc_malloc (size_t bytes) //對于_int_malloc做簡單封裝__malloc_hook //類似于虛函數(shù),派生接口,指定一個malloc的方式 _int_malloc(mstate av, size_t bytes) //申請內(nèi)存塊的核心main_arena
- 集中管理bins鏈的結(jié)構(gòu)體,使用含fd和bk的bin頭一對一管理各個free的chunk
- 分釋放配堆塊是基于main_arena來尋址的,首先找的是fastbin,其次再找bins
- main_arena存儲在libc上,用以管理所有bins的頭和尾,每個bins鏈頭的fd和尾的b與之連接
- main_arena:用來管理整個bin鏈的結(jié)構(gòu)體,總共128個bin,10個fastbin
- 每個bin頭可以簡化為fd和bk兩個前后項指針
- glibc ---> main_arena ---> 對應(yīng)的bins頭的fd和bk ---> 遍歷找到對應(yīng)free的chunk
- main_arena存放在libc中,其中存放的是每一個bin鏈的頭尾
main_chunk
- 在程序的執(zhí)行過程中,我們稱malloc申請的內(nèi)存為chunk。這塊內(nèi)存在ptmalloc內(nèi)部用malloc_chunk結(jié)構(gòu)體來表示。
- 當程序申請的chunk被free后,會被加入到相應(yīng)的空閑管理列表中。
- 無論一個chunk的大小如何,處于分配狀態(tài)還是釋放狀態(tài),它們都使用一個統(tǒng)一的結(jié)構(gòu)。但根據(jù)是否被釋放,結(jié)構(gòu)會有所更改。
- prev_size
- malloc(0x18)會分配0x20的內(nèi)存
- malloc(0x19)分會配0x30的內(nèi)存
- 如果該chunk的物理相鄰的前一地址chunk(兩個指針的地址差值為前一個chunk大小)是空閑的話,那該字段記錄的是前一個chunk的大小
- 否則用來存儲物理相鄰的前一個chunk的數(shù)據(jù),這里前一個chunk指的是較低地址的chunk。
- prev_size位可以被共享,當前的chunk, 如果不夠用就會占用下一塊chunk的prev_size
- size
- chunk1的數(shù)據(jù)有效區(qū)域覆蓋到chunk2的prev_size位,并且chunk2的size位的prev_inuse被覆蓋為0。系統(tǒng)認為chunk2之前的chunk1已經(jīng)未在使用了。
- 當free(chunk2)的時候,系統(tǒng)會將chunk2與chunk2中prev_size大小的空間合并到bins。
- 我們可以通過改變chunk2的prev_size的內(nèi)容,操縱向前合并的大小。
- 造成的問題:overlap(堆塊重疊),chunk1被釋放了,但是我們可以操縱修改它(堆利用的核心思想),從而修改bins鏈的內(nèi)容,泄露其中的地址。
- 形成的攻擊:fastbin ---> fd ---> main_arena ---> 分配新的堆塊,我們通過修改chunk1的fd內(nèi)容,達到分配任意內(nèi)存的目的,造成fastbin attack。
- 記錄前一個chunk是否被分配。
- 一般來說,堆中第一個被分配的內(nèi)存塊的size字段的P位都會被設(shè)置為1,以便于防止訪問前面的非法內(nèi)存。
- 當一個chunk的size位的P位為0時,我們能通過prev_size獲取上一個chunk的大小及地址,方便進行空閑堆塊的合并。
- 對于fastbin的堆塊,不管前面還有沒有被分配的chunk,PREV_INUSE都為1。
- 64位chunk的size必須是16字節(jié)對齊
- 32位chunk的size必須是8 字節(jié)對齊
- 64位 低4位沒用 11110000
- 32位 低3位沒用 11111000
- define chunksize(p) (chunk_nomask (p) & ~(SIZE_BITS))
- define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
- NON_MAIN_ARENA記錄當前chunk是否是main_arena管理的堆塊,1表示不屬于,0表示屬于
- IS_MAPPED記錄當前的chunk是否是由mmap分配的。
- PREV_INUSE
- 最小堆原則 : malloc(0)會分配0x20的空間,prev_size + size + 數(shù)據(jù)對齊的0x10字節(jié)
- prev_inuse 漏洞利用
- fd / bk
- 釋放到bins鏈有效
- fd指向下一個(非物理相鄰)空閑的chunk
- bk指向上一個(非物理相鄰)空閑的chunk
- 通過fd和bk可以將空閑的chunk塊加入到空閑的chunk鏈表進行統(tǒng)一管理。
- fd_nextsize / bk_nextsize
- 釋放到bins鏈有效,不過其用于較大的chunk(large chunk)
- fd_nextsize指向前一個與當前chunk大小不同的第一個空閑塊,不包含bin的頭指針
- bk_nextsize指向后一個與當前chunk大小不同的第一個空閑塊,不包含bin的頭指針
- 一般空閑的largechunk在fd的遍歷順序中,按照從大到小的順序排列,可以避免在尋找合適的chunk時挨個遍歷。
__libc_malloc
void *__libc_malloc (size_t bytes) {mstate ar_ptr;void *victim;void *(*hook) (size_t, const void *)= atomic_forced_read (__malloc_hook);if (__builtin_expect (hook != NULL, 0))return (*hook)(bytes, RETURN_ADDRESS (0)); #if USE_TCACHE/* int_free also calls request2size, be careful to not pad twice. */size_t tbytes;checked_request2size (bytes, tbytes); //注意:用戶申請的字節(jié)一旦進入申請內(nèi)存函數(shù)被轉(zhuǎn)化為了無符號整數(shù)size_t tc_idx = csize2tidx (tbytes);MAYBE_INIT_TCACHE ();DIAG_PUSH_NEEDS_COMMENT;if (tc_idx < mp_.tcache_bins/*&& tc_idx < TCACHE_MAX_BINS*/ /* to appease gcc */&& tcache&& tcache->entries[tc_idx] != NULL){return tcache_get (tc_idx);}DIAG_POP_NEEDS_COMMENT; #endifif (SINGLE_THREAD_P){victim = _int_malloc (&main_arena, bytes);assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||&main_arena == arena_for_chunk (mem2chunk (victim)));return victim;}arena_get (ar_ptr, bytes);victim = _int_malloc (ar_ptr, bytes);/* Retry with another arena only if we were able to find a usable arenabefore. */if (!victim && ar_ptr != NULL){LIBC_PROBE (memory_malloc_retry, 1, bytes);ar_ptr = arena_get_retry (ar_ptr, bytes);victim = _int_malloc (ar_ptr, bytes);}if (ar_ptr != NULL)__libc_lock_unlock (ar_ptr->mutex);assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||ar_ptr == arena_for_chunk (mem2chunk (victim)));return victim; }- 要么沒有申請到內(nèi)存
- 要么是mmap的內(nèi)存
- 要么申請的的內(nèi)存必須在其所分配的arena中
- assert
__int_malloc
__int_malloc是內(nèi)存分配的核心函數(shù),其核心思路為:
它根據(jù)用戶申請的內(nèi)存塊大小以及相應(yīng)大小chunk通常使用的頻度,依次實現(xiàn)了不同的分配方法它由小大到大依次檢查不同的bin中是否有相應(yīng)的空閑塊可以滿足用戶請求的內(nèi)存當所有空閑的chunk都無法滿足時,他會考慮top_chunk當top_chunk也無法滿足時,堆分配器才會進行內(nèi)存塊申請1. 定義變量
2. 判斷有沒有可用的arena
如果沒有可用的arena,則返回系統(tǒng)調(diào)用mmap去申請一塊內(nèi)存3. 判斷是否在fastbin范圍
如果申請的chunk的大小正好位于fastbin的范圍,則從fastbin的頭節(jié)點開始取chunk。需要注意的是,這里比較的是無符號整數(shù)調(diào)用remove_fb取出,并返回得到的fastbin的頭4. 判斷是否在smallbin
如果獲取的內(nèi)存塊的范圍為smallbin的范圍,執(zhí)行以下流程找到其大小對應(yīng)的下標,判斷其鏈表是否為空,不為空則取最后一個注意,為了防止一個堆塊能夠正常free且不前向后并,需要修改當前堆塊的物理相鄰的緊接著的2個堆塊的inuse位為1。
5.調(diào)用consolidate
當fastbin,small bin中的chunk都不能滿足要求時,就會考慮是不是largebin,在此之前先調(diào)用malloc_consolidate處理fastbin中的chunk將有可能合并的chunk先進行合并后放到unsorted bin中,不能合并的就直接放到unsorted bin中,然后再進入大循環(huán),以減少堆中的碎片。只有在分配一個size在largebin范圍內(nèi)的堆塊,才能觸發(fā)malloc_consolidate
6. 小總結(jié)
在fastbin范圍內(nèi),先判斷對應(yīng)鏈表是否為空,不為空則取剛放入的chunk在smallbin范圍內(nèi),先判斷對應(yīng)鏈表是否為空,不為空則取第一個放入的chunk這兩者都無法匹配用戶申請的chunk時,就會進入大循環(huán)7. 進入大循環(huán)
a. 嘗試從unsorted bin中分配用戶需要的內(nèi)存b. 嘗試從large bin中分配用戶需要的內(nèi)存b. 嘗試從top_chunk中分配用戶需要的內(nèi)存8. 從unsorted bin中分配nb
如果申請的size小于unsorted bin中符合要求的chunk的size,會對其進行切割,剩下的進入last_remainder(由unsorted bin管理)如果unsorted bin中沒有滿足要求的chunk時,會先place in order整理,然后再去large bin中尋找9. 從large bin中分配nb
注意,large bin中的堆塊不會split,不滿足的話就從top_chunk中切割10. 大循環(huán)之對于unsorted bin的check
對于size的check:檢查當前size是否滿足對齊的 要求對于fd和bk的check:bck -> fd != victim對于double free的check:next->prev_inuse = 011. 大循環(huán)之切割unsorted bin
如果用戶請求為small bin chunk,那么我們首先考慮last_remainder如果last_remainder分割后還夠可以作為一個chunk,則使用set_head,set_foot設(shè)置標志位,將last_remainder放入原來unsorted bin的位置12. 大循環(huán)之取出unsorted bin
首先將unsorted bin取出,如果其size和我們的nb(need bytes)一樣則直接放回這個unsorted bin13. 大循環(huán)之放入對應(yīng)的bin
根據(jù)取出的size來判斷應(yīng)該放入哪個bin,放入small bin的時候則雙向鏈表插入在else if中處理large bin的邏輯,包括大小排序以及fd_nextsize和bk_nextsize14. 大循環(huán)總結(jié)
整個過程迭代了10000次__int_malloc的大循環(huán)主要用來處理unsorted bin如果整個循環(huán)沒有找到合適的bin,說明所有的unsorted bin的大小都不滿足要求如果經(jīng)過了10000次的循環(huán),所有的unsorted bin中的bin都被放入了對應(yīng)的bin中,即small bin放入對應(yīng)的index中,large bin排好序后放入對應(yīng)的index中
15. 大循環(huán)之large bin
如果請求的chunk在large bin范圍內(nèi),就在對應(yīng)的bin中從小到大依次掃描,直到找到第一個合適的,并不一定精確
切割后的remainder會被放入到unsorted bin中,同時設(shè)置標志位等信息
16. 尋找較大的chunk
如果走到了這里,說明對于用戶所需的chunk,不能直接從其對應(yīng)的合適的bin中獲取chunk,需要掃描所有的bin,查找比當前bin更大的fast bin或small bin 以及l(fā)arge bin
17. 找到一個合適的map
18. 取出chunk
切割之后還是一樣,放入到unsorted bin19. 使用top_chunk
如果所有的bin中的chunk都沒有辦法直接滿足要求(即不合并),或者沒有空閑的chunk時,就只能使用top_chunk了20. top_chunk不夠用
> 如果top_chunk不夠用的時候并不是直接申請內(nèi)存,而是先調(diào)用consolidate合并空閑的fastbin > > 然后等待下次循環(huán)再去判斷是否夠用,不夠用才會調(diào)用sysmalloc申請內(nèi)存 > > _int_malloc總結(jié)
- malloc尋找堆塊的順序
總結(jié)
以上是生活随笔為你收集整理的malloc 结构体_二进制安全之堆溢出(系列)——堆基础 amp; 结构(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: word总页数不包含封面_6个实用的wo
- 下一篇: mooc哈尔滨c语言作业答案,哈尔滨工业