內存地址空間布局

電腦雜談  發布時間:2021-03-13 05:08:26  來源:網絡整理

多任務操作系統中的每個進程都在其自己的內存沙箱中運行。該沙箱是虛擬地址空間(virtual address space)。

1個32位虛擬內存布局

在32位。探索過程通常需要引用絕對內存地址:堆棧地址,庫函數地址等。遠程攻擊者必須依靠地址空間布局的一致性,并摸索選擇這些地址。如果您要求他們猜對了,就會有人糾正。因此,地址空間的隨機排列已逐漸流行。 Linux通過向堆棧,內存映射段和堆的起始地址添加隨機偏移來破壞布局。不幸的是,32位地址空間非常緊湊,幾乎沒有隨機化的空間,從而削弱了該技術的效果。

堆棧

進程地址空間中最上面的段是堆棧,大多數編程語言都使用它來存儲局部變量和函數參數。調用方法或函數會將新的堆??蚣軌喝攵褩?。函數返回時,將清除堆??蚣?。也許因為數據嚴格遵循LIFO的順序,所以這種簡單的設計意味著不需要使用復雜的數據結構來跟蹤堆棧的內容,只需使用指向堆棧頂部的簡單指針即可。因此,推入和彈出過程非??焖偾覝蚀_。此外,不斷重復使用堆??臻g有助于將活動堆棧內存保留在CPU緩存中,從而加快訪問速度。進程中的每個線程都有自己的堆棧。

通過不斷將數據推入堆棧,超出其容量,將耗盡與堆棧相對應的存儲區域。這將觸發頁面錯誤,并由Linux的expand_stack()處理,該擴展將調用acct_stack_growth()來檢查是否有合適的堆棧增長空間。如果堆棧的大小小于RLIMIT_STACK(通常為8MB),則在正常情況下,堆棧將被加長,程序將繼續快樂地運行,而不會感覺到正在發生什么。這是用于將堆棧擴展到所需大小的常規機制。但是,如果達到最大堆棧大小,則會發生堆棧溢出,并且程序將收到分段錯誤。當映射的堆棧區域擴展到所需的大小時,即使堆棧不是很滿,它也不會收縮。就像聯邦預算一樣,它總是在增長。

動態堆棧增長是唯一允許訪問未映射的內存區域(圖中的白色區域)的情況。對未映射內存區域的任何其他訪問都將觸發頁面錯誤,從而導致分段錯誤。一些映射區域是只讀的,因此嘗試寫入這些區域也將導致段錯誤。

內存映射段

堆棧下方是我們的內存映射段。在這里,內核直接將文件的內容映射到內存。任何應用程序都可以通過Linux的mmap()系統調用(實現)或Windows的CreateFileMapping()/ MapViewOfFile()來請求此映射。內存映射是文件I / O的便捷高效方式,因此可用于加載動態庫。也可以創建不對應于任何文件的匿名內存映射。此方法用于存儲程序數據。在Linux中,如果您通過malloc()請求大量內存,則C運行時庫將創建此類匿名映射,而不使用堆內存。 “大塊”表示大于MMAP_THRESHOLD,默認值為128KB,可以通過mallopt()進行調整。

說到堆,它是下一個地址空間。像堆棧一樣,堆用于運行時內存分配。但是不同之處在于,堆用于存儲其生命周期與函數調用無關的數據。大多數語言都提供堆管理功能。因此,滿足內存請求已成為語言運行庫和內核的共同任務。在C語言中,用于堆分配的接口是malloc()系列函數,而在具有垃圾回收功能的語言(例如C#)中,該接口是new關鍵字。

如果堆中有足夠的空間來滿足內存請求,則語言運行庫可以對其進行處理,而無需內核的參與。否則,將擴展堆,并通過brk()系統調用(實現)分配請求所需的內存塊。堆管理非常復雜,需要復雜的算法來應對程序中混亂的分配模式,并優化速度和內存使用效率。處理堆請求所需的時間可能相差很大。實時系統通過專用分配器解決了這個問題。堆也可能變得碎片化,如下圖所示:

圖5

BSS數據段代碼段

最后,讓我們看一下底部的內存段:BSS,數據段,代碼段。在C語言中,靜態(全局)變量的內容存儲在BSS和數據段中。區別在于BSS保存未初始化的靜態變量的內容,并且它們的值未直接在程序的源代碼中設置。 BSS內存區域是匿名的:它沒有映射到任何文件。如果您編寫靜態int cntActiveUsers,則cntActiveUsers的內容將保存在BSS中。

另一方面,數據段存儲在已在源代碼中初始化的靜態變量內容中。該存儲區不是匿名的。它映射程序的二進制映像的一部分,它是具有源代碼中指定的初始值的靜態變量。因此,如果您編寫靜態int cntWorkerBees = 10,則cntWorkerBees的內容將保存在數據段中,并且初始值為10。盡管該數據段映射了一個文件,但它是一個私有內存映射,這意味著更改內存這里不會影響映射文件。情況也必須如此,否則為全局變量分配值將更改硬盤上的二進制映像,這是不可想象的。

下圖中的數據段示例更加復雜,因為它使用了指針。在這種情況下,指針奇聞趣事(4字節內存地址)本身的值存儲在數據段中。它指向的實際字符串不在此處。該字符串存儲在代碼段中。該代碼段是只讀的。它可以保存您所有的代碼以及點點滴滴,例如字符串文字。該代碼段還將您的二進制文件映射到內存,但是寫入該區域將導致您的程序收到分段錯誤。這有助于防止指針錯誤,盡管在用C進行編程時不如采取預防措施有效。下圖顯示了這些段和示例中的變量:

wps_clip_image-10904

圖6

您可以通過讀取文件/ proc / pid_of_process / maps來檢查Linux進程中的內存區域。請記住,一個細分可能包含許多區域。例如,每個內存映射文件在mmap段中都有其自己的區域,而動態庫具有類似于BSS和數據段的其他區域。下一篇文章解釋了這些“區域”(區域)的真正含義。有時人們指的是“數據段”,即整個數據段+ BSS +堆。

2個64位虛擬內存布局

64位系統具有相對較大的尋址空間,因此仍使用經典的32位布局,但是添加了一個隨機的mmap起始地址以防止溢出攻擊。無論如何,如此大的內存地址將不會使用一段時間,因此至少N不會改變很多年。

首先,大多數當前的操作系統和應用程序不需要如此大的16EB(264)地址空間。實現64位地址只會增加系統的復雜性和地址轉換的成本,并且不會帶來任何好處。因此,當前的x86-64架構CPU遵循AMD的Canonical形式,即,僅虛擬地址的最低48位將用于地址轉換,任何虛擬地址的48至63位必須與47位保持一致(標志擴展名)。換句話說,虛擬地址總空間為256TB(248)。

wps_clip_image-22928

圖7

然后,在此256TB虛擬內存空間中,00000-00007fffffffffff(128TB)是用戶空間,而ffff8-ffffffffffffffffff(128TB)是內核空間。這里應該注意的是,內核空間中有很多空洞,它超出了第一個空洞。在一個空洞之后,ffff880000000000-ffffc7ffffffffff(64TB)是直接映射物理內存的區域,也就是說,默認的PAGE_OFFSET為ffff88000000000 0.從這里,我們還可以看到,如此大的直接映射區域足以映射所有物理內存,因此,在x86-64架構下,目前沒有高端內存,即ZONE_HIGHMEM(請參閱上一篇文章)


本文來自電腦雜談,轉載請注明本文網址:
http://www.cvs5.com/a/shoujiruanjian/article-364149-1.html

    相關閱讀
    發表評論  請自覺遵守互聯網相關的政策法規,嚴禁發布、暴力、反動的言論

    熱點圖片
    拼命載入中...
    岛国动作片AV在线网站_亚洲精品欧美综合一区二区_2021年最新无码福利视频