手動編譯測試musl1.2.2 meta dequeue特性

一、基礎知識
https://bbs.pediy.com/thread-269533-1.htm#msg_header_h3_8
師傅寫的很好,研究一下午就大致能了解透徹musl 1.2.2的內存管理結構和機制。美中不足的是沒講講怎么編譯一個用musl libc的程序。本文就簡單介紹介紹如何編譯一個musl libc下的程序,并通過它簡單調試調試musl libc 的一些特性。
安裝musl環境&&符號表
這里直接用0xRGz師傅的做法。
下載.deb安裝包(0xRGz師傅的鏈接在筆者的電腦上貌似失效了):http://ftp.de.debian.org/debian/pool/main/m/musl/musl_1.2.2-1_amd64.deb
安裝:
sudo dpkg -i musl_1.2.2-1_amd64.deb
安裝調試符號:
下載安裝包musl-dbgsym_1.2.2-1_amd64.ddeb(https://launchpad.net/ubuntu/+archive/primary/+files/musl-dbgsym_1.2.2-1_amd64.ddeb)
安裝:
sudo dpkg -i musl-dbgsym_1.2.2-1_amd64.ddeb
安裝gdb小插件(from xf1les 師傅):
git clone https://github.com/xf1les/muslheap.gitecho "source /path/to/muslheap.py" >> ~/.gdbinit
(這里的/path/to是需要修改的,也就是你git的muslheap的路徑。安裝了這個就能使用mheap和mchunk等一些非常好用的輔助調試的命令)
二、手動編譯第一個musl程序
下載&&安裝
curl -LO http://musl.libc.org/releases/musl-1.2.2.tar.gztar vxf musl-1.2.2.tar.gzcd musl-1.2.2./configure --prefix=/usr/local/musl CFLAGS='-O2 -v'make && sudo make install
源碼&&編譯
(在musl-1.2.2目錄下的操作)
main.c:
#include <stdio.h> int main() { printf("Hello musl libc\n"); return 0;}
編譯:
./obj/musl-gcc main.c -o test
鏈接:
patchelf --set-interpreter ./libc.so ./test
運行

三、測試
demo1
#include<stdio.h>#include <unistd.h> void init(){ setbuf(stdin, 0); setbuf(stdout, 0); setbuf(stderr, 0);} int main() { init(); size_t *p1,*p2,*p3; p1=(size_t *)malloc(0x20); p2=(size_t *)malloc(0x30); p3=(size_t *)malloc(0x40); read(0,p1,0x10); read(0,p2,0x20); read(0,p3,0x30); free(p1); free(p2); free(p3); return 0;}
通過read我們就能定位到分配的chunk的地址,從而通過mchunk看出對應的group和meta。然后通過mheap中的active看出對應的meta有沒有被dequeue。
初始狀態

malloc p1后

發現多了一個meta(其實是先多一個chunk,然后多一個group再多一個meta)。
malloc p3后

多了兩個meta。
mchunk p1
到read看見p1的addr:

mchunk它:

可以看見它的group的首地址是它的地址減0x10,和文章里寫的一樣,并且group的首地址存了meta的地址。
p (struct meta ) 這個地址看看:

發現這個meta此時是指向自己的,并且mem存的是group的地址(發現meta有東西指向group,group也有東西指向meta)。
avail_mask為1022是因為它的二進制表示為:
01111111110

freed_mask記錄group中已經被free釋放的堆塊,當前沒有任何被釋放的堆塊,所以它為0。
last_idx為9表示group中最多10個同大小的堆塊(0~9)。
free_able為1表示當前有一個堆塊能free。
sizeclass為2 表示由0x2這個group進行管理這一類的大小的chunk。
maplen為0說明這個group不是通過mmap分配的。
看看group中的chunk

0x70本來是chunk頭,結果被last_idx和meta_addr占位了,從0x80開始是我們讀入的數據“nameless”。
free chunk過后看看:

發現active[2]不見了。這其實觸發了meta dequeue的第一個條件——在free的時候,group中只有一個堆塊處于使用狀態中即free它過后,group中的所有堆塊都處于未使用狀態。這個時候還留它干嘛呢,直接將該meta dequeue了。
demo2
通過demo1簡單了解了gdb下musl 的chunk、group和meta怎么調試,以及meta dequeue的第一種觸發方式。接下來我們調試調試meta dequeue第二種觸發方式——malloc的時候。
源碼:
#include<stdio.h>#include <unistd.h> void init(){ setbuf(stdin, 0); setbuf(stdout, 0); setbuf(stderr, 0);} int main() { init(); size_t *p1; p1=(size_t *)malloc(0x20); //1 p1=(size_t *)malloc(0x20); //2 p1=(size_t *)malloc(0x20); //3 p1=(size_t *)malloc(0x20); //4 p1=(size_t *)malloc(0x20); //5 p1=(size_t *)malloc(0x20); //6 p1=(size_t *)malloc(0x20); //7 p1=(size_t *)malloc(0x20); //8 p1=(size_t *)malloc(0x20); //9 p1=(size_t *)malloc(0x20); //10 p1=(size_t *)malloc(0x20); //11 p1=(size_t *)malloc(0x20); //12 p1=(size_t *)malloc(0x20); //13 p1=(size_t *)malloc(0x20); //14 return 0;}
測試
發現當malloc第10個堆塊過后,active[2]就從0x298變成0x2c0即發生了meta dequeue。

而且group的位置也從0xc70變成了0xc50。這說明在這種dequeue的時候,會產生一個新grep,以及對應的meta。
學meta dequeue有啥用呢?
做題!(即答)
別別別,ctf to learn,not learn to ctf
我們從p (struct meta ) 能看出來meta是一個雙鏈表結構,dequeue的過程就牽扯到一個類似UB或者large bin chunk 的unlink的操作。就有可能通過這里的dequeue實現unlink 任意地址寫,但具體怎么操作就留給讀者思考或者看其它大師傅博客復現賽題了。(筆者寫這篇的時候還沒做任何一道musl 1.2.2的題,雖然確實是奔著做題去學的,不過搞嵌入式的話不會arm,不會musl怎么行呢?)
一些話
紙上得來終覺淺,絕知此事要躬行,覺得在正式做題之前還是要自己寫個demo簡單調調,檢驗檢驗自己是否學懂了musl 1.2.2的特性。不能總想著做題……