營(yíng)銷(xiāo)型網(wǎng)站建設(shè)教學(xué)淘寶優(yōu)秀軟文范例100字
前言
一次意外執(zhí)行了 malloc(0x5000)
,結(jié)構(gòu)使用 gdb
調(diào)試發(fā)現(xiàn)其分配的位置在 TLS
區(qū)域,這令我不解(:最后去看了下 malloc
源碼和 mmap
源碼實(shí)現(xiàn),發(fā)現(xiàn)似乎可能是 gdb
插件的問(wèn)題,樂(lè)
場(chǎng)景復(fù)現(xiàn)
#include <stdio.h>
int main() {malloc(0x5000);return 0;
}
調(diào)試:
wtf
不應(yīng)該啊,這咋能分配到 tls
區(qū)域去了呢?
問(wèn)題探索
通過(guò)對(duì) malloc
源碼的查看,發(fā)現(xiàn)其超過(guò)了 mmap_threshold
,所有最后走的 mmap
映射的一段私有匿名區(qū)域(這里大家應(yīng)該都很熟悉了)所以不應(yīng)該分配到 tls
區(qū)域去啊,在與 henry
師傅交流后,覺(jué)得可能與 mmap
的行為有關(guān),于是說(shuō)干就干,簡(jiǎn)單地去審了一下 mmap
的源碼,最后發(fā)現(xiàn)可能是插件的問(wèn)題
mmap
源碼分析就不寫(xiě)了,我也沒(méi)做筆記,畢竟是開(kāi)源的,需要時(shí)自己去看看就行了,而且我看完 mmap
源碼時(shí)已經(jīng)快凌晨 3 點(diǎn)了,就簡(jiǎn)單說(shuō)下 mmap
是如何分配虛擬內(nèi)存的。在內(nèi)核態(tài),為進(jìn)程維護(hù)了一顆紅黑樹(shù),其根據(jù)地址組織了空閑的 vma(struct vm_area_struct 結(jié)構(gòu)體表示)
,而在我的 ubuntu 22.04
上,文件與匿名映射區(qū)是從高地址往低地址增長(zhǎng)的,所以 mmap
在尋找虛擬內(nèi)存區(qū)域時(shí)是從高地址往低地址掃描的
知道這之后一切都可以解釋了,簡(jiǎn)單調(diào)試一下,啥也不執(zhí)行,看下內(nèi)存布局:把隨機(jī)化關(guān)了,方便調(diào)試
可以看到在 tls
前后存在 0x17e000
和 0x11000
的空間,我們來(lái)驗(yàn)證一下:
#include <stdio.h>
#include <sys/mman.h>
int main() {void *p0 = malloc(0x17e000-0x1000);void *p1 = mmap(0, 0x11000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);printf("p0: %p\np1: %p\n", p0, p1);return 0;
}
我的機(jī)器上
mmap_threshold = 128kb
,所以malloc(0x11000-0x1000)
會(huì)走brk
,所以這里我直接使用mmap
關(guān)于這里 malloc
分配為啥要減去 0x1000
請(qǐng)自行審計(jì) sysmalloc
源碼(大家應(yīng)該比較熟悉)
調(diào)試結(jié)果如下:
可以看到 mmap
的 0x11000
區(qū)域就是 tls
下方的區(qū)域,malloc
的 0x17e000
就是 tls
前面的那塊區(qū)域,這里的 gdb
插件把其當(dāng)作 TLS
了似乎。當(dāng)然這里可能就是 tls
預(yù)留的,我沒(méi)有具體調(diào)試內(nèi)核,僅僅靜態(tài)分析了 mmap
的代碼。當(dāng)然我通過(guò)分配一些線程局部變量調(diào)整了一下 tls
區(qū)域的位置,然后也是這樣的:
#include <sys/mman.h>
__thread char p0[0x17e000+0x1000];
int main() {void *p1 = mmap(0, 0x193000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);printf("p0: %p\np1: %p\n", p0, p1);return 0;
}
調(diào)試結(jié)果:
所以當(dāng)我們 malloc
超過(guò) 0x17e000-0x1000
時(shí),就會(huì)在 libc
前面映射一段空間,也就是大家打 CTF
時(shí)說(shuō)的 malloc
一塊大堆塊,會(huì)使用 mmap
在 libc
前面分配一個(gè)堆塊,一般網(wǎng)上都是說(shuō)分配 0x200000
,其實(shí)自己簡(jiǎn)單算一算就知道了,驗(yàn)證一下:
#include <stdio.h>
#include <sys/mman.h>
int main() {void *p0 = malloc(0x17e000);printf("p0: %p\n", p0);return 0;
}
調(diào)試結(jié)果如下:
一切都是吻合的
一個(gè)有趣的 demo
這里給大家看一個(gè) demo
,感興趣的可以自行研究下背后的原因
#include <stdio.h>
#include <stdint.h>
#include <string.h>int show() {FILE *maps_file;char line[256];char pathname[256];maps_file = fopen("/proc/self/maps", "r");if (maps_file == NULL) {perror("Failed to open /proc/self/maps");return 1;}while (fgets(line, sizeof(line), maps_file)) {if (sscanf(line, "%*x-%*x %*s %*x %*x:%*x %*u %s", pathname) == 1) {if (strcmp(pathname, "[stack]") == 0 || strcmp(pathname, "/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2") == 0) {printf("%s", line);}}}fclose(maps_file);return 0;
}int main() {uint64_t addr;show();puts("==========================================");scanf("%llx", &addr);*(uint64_t*)addr = 0xdeadbeef;puts("==========================================");show();return 0;
}
輸出如下:
總結(jié)
mmap
的源碼還是比較復(fù)雜的,我只是看了我感興趣的部分,有興趣的同學(xué)可以自己去看看源碼,本來(lái)想說(shuō)搭建環(huán)境調(diào)調(diào) mmap
的,最后還是算了,太累了,睡覺(jué)了(:
當(dāng)然這個(gè)東西本身連技術(shù)都不算,之所以記錄一下,也算是回答自己剛開(kāi)始學(xué)習(xí)時(shí)的一些疑問(wèn),記得當(dāng)時(shí)看網(wǎng)上說(shuō) malloc
要分配 0x20000
才能夠保證分配的空間在 libc
前面,其實(shí)根本沒(méi)有問(wèn)過(guò)自己為什么?也沒(méi)有想過(guò) mmap
底層是如何進(jìn)行虛擬內(nèi)存分配的?
當(dāng)然其實(shí)現(xiàn)在來(lái)解決這些問(wèn)題也挺好的,畢竟有一些基礎(chǔ),如果最開(kāi)始就去看 mmap
的源碼,相信我早就被勸退了