Libheap:一款用于分析Glibc堆結(jié)構(gòu)的GDB調(diào)試工具
Libheap是一個(gè)用于在Linux平臺(tái)上分析glibc堆結(jié)構(gòu)的GDB調(diào)試腳本,使用Python語言編寫。
安裝
Glibc安裝
盡管Libheap不要求glibc使用GDB調(diào)試支持和符號(hào)進(jìn)行編譯,但是如果用戶使用的話,也不會(huì)影響它的功能。已經(jīng)有很多手動(dòng)構(gòu)建Glibc的指導(dǎo)說明,目前最合適的一個(gè):
Fedora上使用了以下簡單的方法實(shí)現(xiàn)了該方法:
debuginfo-install glibc
使用該命令,系統(tǒng)會(huì)自動(dòng)安裝并設(shè)置調(diào)試Glibc。
GDB安裝
如果用戶使用的是較新版本的Fedora,其中的GDB使用加入了最新的python支持的新技術(shù)。否則,用戶就需要從SVN中構(gòu)建GDB,而不是使用Tom Tromey提供的步驟:
$ sudo yum install python-devel git texinfo $ mkdir -p ~/archer/build ~/archer/install $ cd ~/archer $ git clone git://sourceware.org/git/archer.git $ cd archer $ git checkout --track -b python origin/archer-tromey-python $ cd ../build $ ../archer/configure --prefix=$(cd ../install && pwd) $ make all install
這一步完成后,用戶就會(huì)得到一個(gè)可以在archer/install/bin/gdb上運(yùn)行的編譯版本GDB。
Libheap安裝
最后一步是安裝Libheap庫,這一步相對簡單,只需要將其移動(dòng)到Python路徑(sys.path)下:
$ mv libheap.py /usr/lib/python2.6
用法
加載libheap同加載其他Python庫的方法類似:
$ gdb (gdb) python from libheap import *
整體堆狀態(tài)
多個(gè)不同的狀態(tài)用于輸出堆的整體狀態(tài),如下:
heap -h
(gdb) heap -h ==================== Heap Dump========================= Options: -a 0x1234 指定arena地址 -b 輸出壓縮的bin列表(只是空閑數(shù)據(jù)塊) -c 輸出壓縮的arena列表(所有數(shù)據(jù)塊) -f [#] 輸出所有的fast bin,或獨(dú)立的fast bin -l 輸出arena 中所有數(shù)據(jù)塊的flat列表 -s [#] 輸出所有的small bin,或獨(dú)立的small bin
heap
(gdb) heap ==================== HeapDump========================= Arena(s) found: arena @ 0xf2f3a0
heap -b
(gdb) heap -b ==================== Heap Dump========================= fast bin 0 @ 0x804b000 free chunk @ 0x804b000- size 0x10 unsorted bin @ 0xf2f3d8 free_chunk @ 0x804b010- size 0x88
heap -f
(gdb) heap -b ==================== HeapDump========================= fast bin 0 @ 0x804b000 free chunk @ 0x804b000- size 0x10 unsorted bin @ 0xf2f3d8 free_chunk @ 0x804b010- size 0x88
heap -f
(gdb) heap -f ==================== HeapDump========================= [ fb 0 ] 0xf2f3a8 -> [0x0804b000 ] (16) [ fb 1 ] 0xf2f3ac -> [0x00000000 ] [ fb 2 ] 0xf2f3b0 -> [0x00000000 ] [ fb 3 ] 0xf2f3b4 -> [0x00000000 ] [ fb 4 ] 0xf2f3b8 -> [0x00000000 ] [ fb 5 ] 0xf2f3bc -> [0x00000000 ] [ fb 6 ] 0xf2f3c0 -> [0x00000000 ] [ fb 7 ] 0xf2f3c4 -> [0x00000000 ] [ fb 8 ] 0xf2f3c8 -> [0x00000000 ] [ fb 9 ] 0xf2f3cc -> [0x00000000 ]
heap -s
(gdb) heap -s 1 ==================== HeapDump========================= [ sb 01 ] 0xf2f3d8 -> [ 0x0804b010 | 0x0804b010 ] [0x00f2f3d0 | 0x00f2f3d0 ] (136)
heap -l
(gdb) heap -l ==================== Heap Dump========================= ADDR SIZE STATUS sbrk_base 0x602c00 chunk 0x602c00 0x110 (inuse) chunk 0x602d10 0x110 (F) FD 75dea366deb8 BK 602f30 chunk 0x602e20 0x110 (inuse) chunk 0x602f30 0x110 (F) FD 602d10 BK 75dea366deb8 chunk 0x603040 0x110 (inuse) chunk 0x603150 0x20eb0 (top) sbrk_end 0x624008
heap -c
(gdb) heap -c ==================== Heap Dump========================= |A||11||A||11||A||T|
數(shù)據(jù)塊
libheap為用戶提供了多種方法用于檢查內(nèi)存分配數(shù)據(jù)塊。該庫使用一個(gè)比較完善的malloc_chunk結(jié)構(gòu)體輸出程序,因此只要是有效的數(shù)據(jù)庫,就可以輸出其地址:
(gdb) p *(mchunkptr) 0x608790 struct malloc_chunk { prev_size = 0x0 size = 0x21a81 fd = 0x0 bk = 0x0 fd_nextsize = 0x0 bk_nextsize = 0x0
為了獲取數(shù)據(jù)庫的更加細(xì)粒度的訪問權(quán)限,libheap使用了一個(gè)代表內(nèi)存分配數(shù)據(jù)塊的python類:
(gdb) python print malloc_chunk(0x608790) struct malloc_chunk { prev_size = 0x0 size = 0x21a81 fd = 0x0 bk = 0x0 fd_nextsize = 0x0 bk_nextsize = 0x0
默認(rèn)情況下,程序會(huì)將一個(gè)地址看作已釋放的數(shù)據(jù)庫,并讀取malloc_chunk結(jié)構(gòu)體的所有字段。但是如果用戶傳遞一個(gè)名為‘inuse’的可選布爾項(xiàng)就可以改變這種情況。如果用戶只是想要讀取已分配數(shù)據(jù)塊的頭部,那么可以傳入一個(gè)名為‘read_data’的可選布爾項(xiàng)。該類默認(rèn)讀取數(shù)據(jù)塊中指定的任意大小的內(nèi)存,這樣顯示是有弊端的:攻擊者可以使用虛假的值覆蓋size字段。因此程序設(shè)置了一個(gè)可選的size標(biāo)志,用戶可以指定真實(shí)的數(shù)據(jù)塊大小。綜上所述,用戶可以訪問和更改數(shù)據(jù)塊中獨(dú)立的字段:
(gdb) python chunk = malloc_chunk(0x608790, inuse=True,read_data=False) (gdb) python print chunk struct malloc_chunk { prev_size = 0x0 size = 0x21a81 (gdb) python chunk.size = 1 (gdb) python chunk.write() (gdb) python print chunk struct malloc_chunk { prev_size = 0x0 size = 0x1 (gdb) python print malloc_chunk(0x608790, inuse=True, size=8) struct malloc_chunk { prev_size = 0x0 size = 0x1 data = (0,) raw ="\x00\x00\x00\x00"
最后,如果用戶想要查看內(nèi)存分配數(shù)據(jù)塊在堆實(shí)現(xiàn)中的表現(xiàn)形式,可以向該類傳遞一個(gè)行內(nèi)存字符串,并查看解析情況:
(gdb) python printmalloc_chunk(mem='\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00',inuse=True) struct malloc_chunk { prev_size = 0x1 size = 0x2
Glibc結(jié)構(gòu)體
libheap中還包含malloc_par和malloc_state結(jié)構(gòu)體的輸出程序。用戶可以通過請求輸出全局變量查看:
(gdb) p mp_ $1 = struct malloc_par { (gdb) p main_arena $2 = struct malloc_state {
以下為這兩個(gè)重要結(jié)構(gòu)體的Python類實(shí)現(xiàn),用戶可以使用這些類查看任意內(nèi)存:
(gdb) python print malloc_state(0x6503d0b37e60) struct malloc_state { mutex = 0x0 flags = 0x1 fastbinsY = {...} top = 0x608790 last_remainder = 0x0 bins = {...} binmap = {...} next =0x6503d0b37e60 system_mem = 0x21890 max_system_mem = 0x21890 (gdb) python print malloc_par(0x6cb800) struct malloc_par { trim_threshold = 0x9e000 top_pad = 0x20000 mmap_threshold = 0x4f000 n_mmaps = 0x0 n_mmaps_max = 0x10000 max_n_mmaps = 0x1 no_dyn_threshold = 0x0 mmapped_mem = 0x0 max_mmapped_mem = 0x4f000 max_total_mem = 0x0 sbrk_base =0x809c000
便捷函數(shù)
如果用戶想要擴(kuò)展該庫或使用其中的任意功能,以下為在Python中重新實(shí)現(xiàn)的Glibc函數(shù)列表:
chunk2mem(p) mem2chunk(mem) request2size(req) prev_inuse(p) chunk_is_mmapped(p) chunk_non_main_arena(p) chunksize(p) next_chunk(p) prev_chunk(p) chunk_at_offset(p, s) inuse(p) set_inuse(p) clear_inuse(p) inuse_bit_at_offset(p, s) set_inuse_bit_at_offset(p, s) clear_inuse_bit_at_offset(p, s) bin_at(m, i) next_bin(b) first(b) last(b) in_smallbin_range(sz) smallbin_index(sz) largebin_index_32(sz) largebin_index_64(sz) largebin_index(sz) bin_index(sz) fastbin(ar_ptr, idx) fastbin_index(sz) have_fastchunks(M) clear_fastchunks(M) set_fastchunks(M) contiguous(M) noncontiguous(M) set_noncontiguous(M) set_contiguous(M) mutex_lock(ar_ptr [, inferior]) mutex_unlock(ar_ptr [, inferior]) top(ar_ptr) heap_for_ptr(ptr)