剖析一下 ELF 文件
關於計算機的文件有很多種,今天分享一種用於二進制文件、可執行文件、目標代碼、共享庫和核心轉儲格式文件。
一、ELF 文件簡介
ELF: Executable and Linkable Format,可執行與可鏈接格式。
首先,你需要知道的是所謂對象文件 (Object files) 有三個種類:
1)可重定向文件:文件保存着代碼和適當的數據,用來和其他的目標文件一起來創建一個可執行文件或者是一個共享目標文件。(目標文件或者靜態庫文件,即通常後綴爲. a 和. o 的文件)
2)可執行文件:文件保存着一個用來執行的程序。(例如 bash,gcc 等)
3)共享目標文件:共享庫。文件保存着代碼和合適的數據,用來被下連接編輯器和動態鏈接器鏈接。
二、ELF 文件格式
首先,ELF 文件格式提供了兩種視圖,分別是鏈接視圖和執行視圖。
鏈接視圖是以節(section)爲單位,執行視圖是以段(segment)爲單位。鏈接視圖就是在鏈接時用到的視圖,而執行視圖則是在執行時用到的視圖。上圖左側的視角是從鏈接來看的,右側的視角是執行來看的。總個文件可以分爲四個部分:
-
ELF header:描述整個文件的組織。
-
Program Header Table: 描述文件中的各種 segments,用來告訴系統如何創建進程映像的。
-
sections 或者 segments:segments 是從運行的角度來描述 elf 文件,sections 是從鏈接的角度來描述 elf 文件,也就是說,在鏈接階段,我們可以忽略 program header table 來處理此文件,在運行階段可以忽略 section header table 來處理此程序(所以很多加固手段刪除了 section header table)。從圖中我們也可以看出,segments 與 sections 是包含的關係,一個 segment 包含若干個 section。
-
Section Header Table: 包含了文件各個 segction 的屬性信息,我們都將結合例子來解釋。
程序頭部表(Program Header Table),如果存在的話,告訴系統如何創建進程映像。
節區頭部表(Section Header Table)包含了描述文件節區的信息,比如大小、偏移等。
如下圖,可以通過執行命令”readelf -S android_server” 來查看該可執行文件中有哪些 section。
通過執行命令 readelf –segments android_server,可以查看該文件的執行視圖。
這驗證了第一張圖中所述,segment 是 section 的一個集合,sections 按照一定規則映射到 segment。那麼爲什麼需要區分兩種不同視圖?
當 ELF 文件被加載到內存中後,系統會將多個具有相同權限(flg 值)section 合併一個 segment。操作系統往往以頁爲基本單位來管理內存分配,一般頁的大小爲 4096B,即 4KB 的大小。同時,內存的權限管理的粒度也是以頁爲單位,頁內的內存是具有同樣的權限等屬性,並且操作系統對內存的管理往往追求高效和高利用率這樣的目標。ELF 文件在被映射時,是以系統的頁長度爲單位的,那麼每個 section 在映射時的長度都是系統頁長度的整數倍,如果 section 的長度不是其整數倍,則導致多餘部分也將佔用一個頁。而我們從上面的例子中知道,一個 ELF 文件具有很多的 section,那麼會導致內存浪費嚴重。這樣可以減少頁面內部的碎片,節省了空間,顯著提高內存利用率。
需要注意地是:儘管圖中顯示的各個組成部分是有順序的,實際上除了 ELF 頭部表以外,其他節區和段都沒有規定的順序。
三、ELF Header
首先,我們先來看下 32 位 ELF 文件中常用的數據格式:
然後我們來觀察一下 ELF Header 的結構體:
#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT];
ELF32_Half e_type;
ELF32_Half e_machine;
ELF32_Word e_version;
ELF32__Addr e_entry;
ELF32_Off e_phoff;
ELF32_Off e_shoff;
ELF32_Word e_flags;
ELF32_Half e_ehsize;
ELF32_Half e_phentsize;
ELF32_Half e_phnum;
ELF32_Half e_shentsize;
ELF32_Half e_shnum;
ELF32_Half e_shstrndx;
}Elf32_Ehdr;
e_ident :ELF 的一些標識信息,前四位爲. ELF, 其他的信息比如大小端等
e_machine :文件的目標體系架構,比如 ARM
e_version : 0 爲非法版本,1 爲當前版本
e_entry :程序入口的虛擬地址
e_phoff :程序頭部表偏移地址
e_shoff :節區頭部表偏移地址
e_flags :保存與文件相關的,特定於處理器的標誌
e_ehsize :ELF 頭的大小
e_phentsize :每個程序頭部表的大小
e_phnum :程序頭部表的數量
e_shentsize:每個節區頭部表的大小
e_shnum :節區頭部表的數量
e_shstrndx:節區字符串表位置
接着運行 readelf -h android_server 命令,可以看到 ELF Header 結構的內容。
或者使用 010Editor 的 ELF 模板也可以看到 ELF Header 結構。對比以下三類 ELF 文件,我們得到了以下結論:
1、e_type 標識了文件類型
2、Relocatable File(.o 文件)不需要執行,因此 e_entry 字段爲 0,且沒有 Program Header Table 等執行視圖
3、不同類型的 ELF 文件的 Section 也有較大區別,比如只有 Relocatable File 有. strtab 節。
Executable File(可執行文件 android_server)
Relocatable File(.o 文件)
四、Section Header Table
一個 ELF 文件中到底有哪些具體的 sections,由包含在這個 ELF 文件中的 section head table(SHT) 決定。在 SHT 中,針對每一個 section,都設置有一個條目(entry),用來描述對應的這個 section,其內容主要包括該 section 的名稱、類型、大小以及在整個 ELF 文件中的字節偏移位置等等。我們也可以在 TISCv1.2 規範中找到 SHT 表中條目的 C 結構定義:
typedef struct{
Elf32_Word sh_name; //節區名,是節區頭部字符串表節區(Section Header String Table Section)的索引。名字是一個 NULL 結尾的字符串。
Elf32_Word sh_type; //爲節區類型
Elf32_Word sh_flags; //節區標誌
Elf32_Addr sh_addr; //如果節區將出現在進程的內存映像中,此成員給出節區的第一個字節應處的位置。否則,此字段爲 0。
Elf32_Off sh_offset; //此成員的取值給出節區的第一個字節與文件頭之間的偏移。
Elf32_Word sh_size; //此 成 員 給 出 節 區 的 長 度 ( 字 節 數 )。
Elf32_Word sh_link; //此成員給出節區頭部表索引鏈接。其具體的解釋依賴於節區類型。
Elf32_Word sh_info; //此成員給出附加信息,其解釋依賴於節區類型。
Elf32_Word sh_addralign; //某些節區帶有地址對齊約束.
Elf32_Word sh_entsize; //某些節區中包含固定大小的項目,如符號表。對於這類節區,此成員給出每個表項的長度字節數。
}Elf32_Shdr;
sh_type 的取值如下:
五、Section
有些節區是系統預訂的,一般以點開頭號,因此,我們有必要了解一些常用到的系統節區。
六、Program Header Table
程序頭部(Program Header)描述與程序執行直接相關的目標文件結構信息。用來在文件中定位各個段的映像。同時包含其他一些用來爲程序創建映像所必須的信息。
可執行文件或者共享目標文件的程序頭部是一個結構數組,每個結構描述了一個段或者系統準備程序執行所必須的其他信息。目標文件的 “段” 包含一個或者多個“節區”,也就是“段內容(Segment Contents)”。程序頭部僅對可執行文件和共享目標文件有意義。
程序頭部的數據結構如下:
typedef struct {
Elf32_Word p_type; //此數組元素描述的段的類型,或者如何解釋此數組元素的信息。
Elf32_Off p_offset; //此成員給出從文件頭到該段第一個字節的偏移
Elf32_Addr p_vaddr; //此成員給出段的第一個字節將被放到內存中的虛擬地址
Elf32_Addr p_paddr; //此成員僅用於與物理地址相關的系統中。System V忽略所有應用程序的物理地址信息。
Elf32_Word p_filesz; //此成員給出段在文件映像中所佔的字節數。可以爲0。
Elf32_Word p_memsz; //此成員給出段在內存映像中佔用的字節數。可以爲0。
Elf32_Word p_flags; //此成員給出與段相關的標誌。
Elf32_Word p_align; //此成員給出段在文件中和內存中如何對齊。
} Elf32_phdr;
p_type:
好了,本文主要內容就分享到這裏,具體可以參看 ELF 文件詳細描述。
素材來源:網絡,綜合 CSDN,直接來源 strongerHuang
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/CkWuayFm-D9IvZou5270Kg