elf文件格式和內存映射
ELF 全稱 “Executable and Linkable Format”,即可執行可鏈接文件格式,目前常見的Linux、 Android可執行文件、共享庫(.so)、目標文件( .o)以及Core 文件(吐核)均為此格式。
ELF的數據按照Segment和Section兩個概念進行劃分。
常見的elf文件格式大致結構如下

鏈接視圖:
靜態鏈接器(即編譯后參與生成最終ELF過程的鏈接器,如ld )會以鏈接視圖解析ELF。編譯時生成的 .o(目標文件)以及鏈接后的 .so (共享庫)均可通過鏈接視圖解析,鏈接視圖可以沒有段表(如目標文件不會有段表)。
執行視圖:
動態鏈接器(即加載器,如x86架構 linux下的 /lib/ld-linux.so.2或者安卓系統下的 /system/linker均為動態鏈接器)會以執行視圖解析ELF并動態鏈接,執行視圖可以沒有節表。
Segment 與 Section
- Segment
- 用于告訴內核,在執行ELF文件時應該如何映射內存
- 每個Segment主要包含加載地址、文件中的范圍、內存權限、對齊方式等信息
- 是運行時必須提供的信息
- Section
- 用于告訴連接器,ELF中每個部分是什么,哪里是代碼、哪里只是度數據,哪里是重定位信息
- 每個Section主要包含Section類型、文件中的位置、帶下等信息
- 連接器依賴Section信息將不同的對象文件的代碼數據信息合并,并修復互相引用
- Segment 與 Section的關系
- 相同權限的Section會放入同一個Segment, 例如.text和.rodata section
- 一個Segment包含許多Section,一個Section可以屬于多個Segment
ELF文件格式

- ELF Header
- 架構、ABI版本等基礎信息
- program header table的位置和數量(圖中的指針已經給出示意)
- section header table的位置和數量(圖中的指針已經給出示意)
- Program header table
- 每個表項定義了一個segment
- 每個segment可包含多個section
- Section header table
- 每個表項定義了一個section
其中Section為最基礎的梳理單元,上述這些都是定義好的,只需了解結構即可,不需要記住每個表中的具體信息,因為都有現成的文檔供我們查閱。
查看ELF Header
通過readelf -h命令可查看具體信息

通過readelf -l查看程序頭信息
內存映射
進程內存空間布局

這里我們考察內存大小為4GB,操作系統32位的情況。
從高內存到低內存排列:
1、內核態內存空間,其大小一般比較固定(可以編譯時調整),但 32 位系統和 64 位系統的值不一樣。
2、用戶態的棧,大小不固定,可以用ulimit -s 進行調整,默認一般為 8M,從高地址向低地址增長。
3、mmap區域(內存映射段),既可以從高地址到低地址延伸(所謂 flexible layout),也可以從低到高延伸(所謂 legacy layout),看進程具體情況。
4、brk 區域(堆),緊鄰數據段(甚至貼著),從低位向高位伸展,但它的大小主要取決于 mmap 如何增長,一般來說,即使是 32 位的進程以傳統方式延伸,也有差不多 1 GB 的空間。
5、數據段,主要是進程里初始化和未初始化(BSS)的全局數據總和,當然還有編譯器生成一些輔助數據結構等等),大小取決于具體進程,其位置緊貼著代碼段。
6、代碼段,主要是進程的指令,包括用戶代碼和編譯器生成的輔助代碼,其大小取決于具體程序,但起始位置根據 32 位還是 64 位一般固定(-fPIC, -fPIE等除外)。
具體段的內容我們在下一章會詳細介紹,堆和棧是pwn題目考查的重點,希望大家能重點掌握并熟練運用。
elf和內存空間的關系

左邊是ELF文件在磁盤上的格式,右邊是加載到內存中的形式。在加載時,內核會解析相應的項,加載到相應的地址。接著初始化棧和堆,如果有共享庫的話會用lib.so去加載共享庫,當全部加載完成后內核會將控制權交還給應用進程。
參考