Android安全–ELF文件格式解析

1、簡介

   可執行連接格式(Executable and Linking Format)最初是由 UNIX 系統實驗室(UNIX System Laboratories,USL)開發併發布的,做爲應用程序二進制接口(Application Binary Interface,ABI)的一部分。工具接口標準(Tool Interface Standards,TIS)委員會將還在發展的 ELF 標準選做爲一種可移植的目標文件格式,能夠在 32 位 Intel 體系結構上的不少操做系統中使用。php

   目標文件有三種類型:html

  • 可重定位文件(Relocatable File)包含適合於與其餘目標文件連接來建立可執行文件或者共享目標文件的代碼和數據。android

  • 可執行文件(Executable File)包含適合於執行的一個程序,此文件規定了exec() 如何建立一個程序的進程映像。編程

  • 共享目標文件(Shared Object File)包含可在兩種上下文中連接的代碼和數據。首先連接編輯器能夠將它和其它可重定位文件和共享目標文件一塊兒處理,生成另一個目標文件。其次,動態連接器(Dynamic Linker)可能將它與某個可執行文件以及其它共享目標一塊兒組合,建立進程映像。數組

  • 目標文件所有是程序的二進制表示,目的是直接在某種處理器上直接執行。數據結構

2、目標文件格式

   目標文件既要參與程序連接又要參與程序執行。出於方便性和效率考慮,目標文件格式提供了兩種並行視圖,分別反映了這些活動的不一樣需求。架構

   image

   文件開始處是一個ELF 頭部(ELF Header),用來描述整個文件的組織。節區部分包含連接視圖的大量信息:指令、數據、符號表、重定位信息等等。
程序頭部表(Program Header Table),若是存在的話,告訴系統如何建立進程映像。用來構造進程映像的目標文件必須具備程序頭部表,可重定位文件不須要這個表。
節區頭部表(Section Heade Table)包含了描述文件節區的信息,每一個節區在表中都有一項,每一項給出諸如節區名稱、節區大小這類信息。用於連接的目標文件必須包含節區頭部表,其餘目標文件能夠有,也能夠沒有這個表。併發

   注意:儘管圖中顯示的各個組成部分是有順序的,實際上除了 ELF 頭部表之外,其餘節區和段都沒有規定的順序編輯器

   目標文件中的數據表示ide

   image

   目標文件中的全部數據結構都聽從「天然」大小和對齊規則。若是必要,數據結構能夠包含顯式的補齊,例如爲了確保4字節對象按4字節邊界對齊。數據對齊一樣適用於文件內部。

   這裏使用android-ndk下面的例子hello-jni.so來分析。

3、ELF Header部分

   elf header數據以下:

   image

   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;                          //程序入口的虛擬地址,若沒有,可爲0
Elf32_Off e_phoff;                            //程序頭部表格(Program Header Table)的偏移量(按字節計算),若沒有,可爲0
Elf32_Off e_shoff;                            //節區頭部表格(Section Header Table)的偏移量(按字節計算),若沒有,可爲0
Elf32_Word e_flags;                        //保存與文件相關的,特定於處理器的標誌。標誌名稱採用 EF_machine_flag的格式。
Elf32_Half e_ehsize;                        //ELF 頭部的大小(以字節計算)。
Elf32_Half e_phentsize;                 //程序頭部表格的表項大小(按字節計算)。
Elf32_Half e_phnum;                      //程序頭部表格的表項數目。能夠爲 0。
Elf32_Half e_shentsize;                  //節區頭部表格的表項大小(按字節計算)。
Elf32_Half e_shnum;                      //節區頭部表格的表項數目。能夠爲 0。
Elf32_Half e_shstrndx;                  //節區頭部表格中與節區名稱字符串表相關的表項的索引。若是文件沒有節區名稱字符串表,此參數能夠爲 SHN_UNDEF。
}Elf32_Ehdr;

   其中,e_ident數組給出了ELF的一些標識信息,這個數組中不一樣下標的含義如表:

   image

   數聽說明:

   ①e_ident[EI_MAG0]~e_ident[EI_MAG3]即e_ident[0]~e_ident[3]被稱爲魔數(Magic Number),其值通常爲0x7f,’E’,’L’,’F’。

   ②e_ident[EI_CLASS](即e_ident[4])識別目標文件運行在目標機器的類別,取值可爲三種值:ELFCLASSNONE(0)非法類別;ELFCLASS32(1)32位目標;ELFCLASS64(2)64位目標。

   ③e_ident[EI_DATA](即e_ident[5])給出處理器特定數據的數據編碼方式。即大端仍是小端方式。取值可爲3種:ELFDATANONE(0)非法數據編碼;ELFDATA2LSB(1)高位在前;ELFDATA2MSB(2)低位在前。

   ④e_ident[EI_VERSION]](即e_ident[6])ELF頭部的版本號碼,此值必須是EV_CURRENT。

   ⑤e_ident[EI_PAD](即e_ident[7])標記e_ident中未使用字節的開始,初始化爲0。

   其它各個字段的含義以下:

   image

4、Program Header Table部分

   數據結構以下所示:

   /* Program segment header. */ typedef struct { Elf32_Word p_type; /* Segment type */ Elf32_Off p_offset; /* Segment file offset */ Elf32_Addr p_vaddr; /* Segment virtual address */ Elf32_Addr p_paddr; /* Segment physical address */ Elf32_Word p_filesz; /* Segment size in file */ Elf32_Word p_memsz; /* Segment size in memory */ Elf32_Word p_flags; /* Segment flags */ Elf32_Word p_align; /* Segment alignment */ } Elf32_Phdr;

   其中p_type的定義以下:

/* Legal values for p_type (segment type).  */

#define PT_NULL         0               /* Program header table entry unused */
#define PT_LOAD         1               /* Loadable program segment */
#define PT_DYNAMIC      2               /* Dynamic linking information */
#define PT_INTERP       3               /* Program interpreter */
#define PT_NOTE         4               /* Auxiliary information */
#define PT_SHLIB        5               /* Reserved */
#define PT_PHDR         6               /* Entry for header table itself */
#define PT_TLS          7               /* Thread-local storage segment */
#define PT_NUM          8               /* Number of defined types */
#define PT_LOOS         0x60000000      /* Start of OS-specific */
#define PT_GNU_EH_FRAME 0x6474e550      /* GCC .eh_frame_hdr segment */
#define PT_LOSUNW       0x6ffffffa
#define PT_SUNWBSS      0x6ffffffa      /* Sun Specific segment */
#define PT_SUNWSTACK    0x6ffffffb      /* Stack segment */
#define PT_HISUNW       0x6fffffff
#define PT_HIOS         0x6fffffff      /* End of OS-specific */
#define PT_LOPROC       0x70000000      /* Start of processor-specific */
#define PT_HIPROC       0x7fffffff      /* End of processor-specific */

   p_flag表示該該段是否可讀可寫可執行

/* Legal values for p_flags (segment flags).  */

#define PF_X            (1 << 0)        /* Segment is executable */
#define PF_W            (1 << 1)        /* Segment is writable */
#define PF_R            (1 << 2)        /* Segment is readable */
#define PF_MASKOS       0x0ff00000      /* OS-specific */
#define PF_MASKPROC     0xf0000000      /* Processor-specific */

5、Section Header Table部分

   ELF 頭部中,e_shoff 成員給出從文件頭到節區頭部表格的偏移字節數;e_shnum給出表格中條目數目;e_shentsize 給出每一個項目的字節數。從這些信息中能夠確切地定位節區的具體位置、長度。

   數據結構以下:

   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字段:

   image

   sh_flags字段:

   sh_flags字段定義了一個節區中包含的內容是否能夠修改、是否能夠執行等信息。若是一個標誌位被設置,則該位取值爲1。未定義的各位都設置爲0。

   SHF_WRITE   0×1

   SHF_ALLOC   0×2

   SHF_EXECINSTR   0×4

   SHF_MASKPROC   0xF0000000

   其中已經定義了的各位含義以下:

   SHF_WRITE: 節區包含進程執行過程當中將可寫的數據。

   SHF_ALLOC: 此節區在進程執行過程當中佔用內存。某些控制節區並不出現於目標文件的內存映像中,對於那些節區,此位應設置爲 0。

   SHF_EXECINSTR: 節區包含可執行的機器指令。

   SHF_MASKPROC: 全部包含於此掩碼中的四位都用於處理器專用的語義。

   sh_link和sh_info字段:

   根據節區類型的不一樣,sh_link 和 sh_info 的具體含義也有所不一樣:

   image

6、特殊節區

   image在分析這些節區的時候,須要注意以下事項:

   ①以「.」開頭的節區名稱是系統保留的。應用程序可使用沒有前綴的節區名稱,以免與系統節區衝突。

   ②目標文件格式容許人們定義不在上述列表中的節區。

   ③目標文件中也能夠包含多個名字相同的節區。

   ④保留給處理器體系結構的節區名稱通常構成爲:處理器體系結構名稱簡寫 + 節區名稱。

   ⑤處理器名稱應該與 e_machine 中使用的名稱相同。例如 .FOO.psect 街區是由FOO 體系結構定義的 psect 節區。

7、字符串表(String Table)

   首先要知道,字符串表它自己就是一個節區,從第二章描述中可知,每個節區都存在一個節區頭部表項與之對應,因此字符串表這個節區也存在一個節區頭部表項對應,而在elf文件頭部結構中存在一個成員e_shstrndx給出這個節區頭部表項的索引位置。所以能夠經過

   shstrab  = (rt_uint8_t *)module_ptr +shdr[elf_module->e_shstrndx].sh_offset;

   字符串表節區包含以NULL(ASCII碼0)結尾的字符序列,一般稱爲字符串。ELF目標文件一般使用字符串來表示符號和節區名稱。對字符串的引用一般以字符串在字符串表中的下標給出。
通常,第一個字節(索引爲 0)定義爲一個空字符串。相似的,字符串表的最後一個字節也定義爲 NULL,以確保全部的字符串都以NULL結尾。索引爲0的字符串在不一樣的上下文中能夠表示無名或者名字爲 NULL的字符串。
容許存在空的字符串表節區,其節區頭部的sh_size成員應該爲0。對空的字符串表而言,非0的索引值是非法的。

   在使用、分析字符串表時,要注意如下幾點:

   ①字符串表索引能夠引用節區中任意字節

   ②字符串能夠出現屢次

   ③能夠存在對子字符串的引用

   ④同一個字符串能夠被引用屢次

   ⑤字符串表中也能夠存在未引用的字符串

   

   先介紹這麼多,其它的信息你們本身參考:

   非蟲大大附圖:http://bbs.pediy.com/attachment.php?attachmentid=74501&d=1355835585

   ELF文件格式分析:http://staff.ustc.edu.cn/~sycheng/sst/exp_crack/ELF.pdf

   ELF文件格式官方文檔:http://download.csdn.net/detail/walkingman321/3016369

  做者:AloneMonkey    來源: Coder

問啊-定製化IT教育平臺,牛人一對一服務,有問必答,開發編程社交頭條 官方網站:www.wenaaa.com 下載問啊APP,參與官方懸賞,賺百元現金。

QQ羣290551701 彙集不少互聯網精英,技術總監,架構師,項目經理!開源技術研究,歡迎業內人士,大牛及新手有志於從事IT行業人員進入!

相關文章
相關標籤/搜索