当前位置:   article > 正文

Android中ELF文件结构浅析(二)_安卓elf文件

安卓elf文件

一、程序头表(program_header_table)

        一个可执行文件或共享目标文件的程序头表(program header table)是一个数组,数组中的每一个元素称为“程序头(program header)”,每一个程序头描述了一个“段(segment)”或者一块用于准备执行程序的信息。

        在elf header中e_phentsize 和 e_phnum 成员指定了程序头的大小和数量。

        每个segment的结构如下:

  1. struct Elf64_Phdr {
  2. Elf64_Word p_type;
  3. Elf64_Word p_flags;
  4. Elf64_Off p_offset;
  5. Elf64_Addr p_vaddr;
  6. Elf64_Addr p_paddr;
  7. Elf64_Xword p_filesz;
  8. Elf64_Xword p_memsz;
  9. Elf64_Xword p_align;
  10. };

1.  p_type

        描述的段的类型。

  1. #define PT_NULL 0
  2. #define PT_LOAD 1
  3. #define PT_DYNAMIC 2
  4. #define PT_INTERP 3
  5. #define PT_NOTE 4
  6. #define PT_SHLIB 5
  7. #define PT_PHDR 6
  8. #define PT_TLS 7
  9. #define PT_NUM 8
  10. #define PT_LOOS 0x60000000
  11. #define PT_GNU_EH_FRAME 0x6474e550
  12. #define PT_GNU_STACK 0x6474e551
  13. #define PT_GNU_RELRO 0x6474e552
  14. #define PT_GNU_PROPERTY 0x6474e553
  15. #define PT_LOSUNW 0x6ffffffa
  16. #define PT_SUNWBSS 0x6ffffffa
  17. #define PT_SUNWSTACK 0x6ffffffb
  18. #define PT_HISUNW 0x6fffffff
  19. #define PT_HIOS 0x6fffffff
  20. #define PT_LOPROC 0x70000000
  21. #define PT_HIPROC 0x7fffffff

        结尾案例会说其中一部分。 

2.   p_flags

        此数据成员给出了本段内容的权限。

标志权限实际权限
PF_None0
PF_Exec1可执行可读,可执行
PF_Write2可写可读,可写,写执行
PF_Write_Exec3可写,可执行可读,可写,写执行
PF_Read4可读可读,可执行
PF_Read_Exec5可读,可执行可读,可执行
PF_Read_Write6可读,可写可读,可写,写执行
PF_Read_Write_Exec7可读,可写,写执行可读,可写,写执行

        对上表总结一下,有两条:

        1、可读与可执行是通用的,有其中一个就等于也有了另一个

        2、可写权限是最高权限,可以覆盖另外两个,有了可写权限,所有权限就都有了

 3.  p_offset

         段(segment)内容的开始位置相对于文件开头的偏移量,在010Edit解析后如下所示

        其中(p_offset)为0x0

4.  p_vaddr

        此数据成员给出本段内容的开始位置在进程空间中的虚拟地址

        听着感觉要长脑子了,小案例来喽~

 

可以看到在文件中的位置是0x3C78

但是在内存中时,对于so基址的偏移是0x4C78。

5.  p_paddr

       一般与p_vaddr的值相同,貌似无用。

6.  p_filesz

        此数据成员给出本段内容在文件中的大小,单位是字节。

        p_offset 偏移后的位置 加上 p_filesz 就是本segment 在文件中的内容

7.  p_memsz

        此数据成员给出本段内容在内存中的大小,单位是字节。

         在内存中 p_vaddr 对于基址偏移后的位置 加上 p_memsz 就是本segment 在内存中的内容

8.   p_align

        对于可装载的段来说,其 p_vaddr 和 p_offset 的值至少要向内存页面大小对齐。

        此数据成员指明本段内容如何在内存和文件中对齐。

        如果该值为 0 或 1,表明没有对齐要求;

        否则,p_align 应该是一个正整数,并且是 2 的幂次数。p_vaddr 和 p_offset 在对 p_align 取模后应该相等。

         

0x3C78 % 0x1000 (4096) =  C78

0x4C78 % 0x1000 (4096) =  C78

二、总结分析

        

         根据上一篇的 elf header 分析,我们知道program header table 的起始位置是 0x40,其中每个表项的大小相同。

        上图黑色背景的内容是和蓝色背景的内容都是程序头表的表项。

1.   program_table_element[0] (可加载段)

        

         在这个元素中的p_type 类型为 PT_LOAD

        此类型表明本程序头指向一个可装载的段。

        段的内容会被从文件中拷贝到内存中。如前所述,段在文件中的大小是 p_filesz,在内存中的大小是p_memsz。如果 p_memsz 大于 p_filesz,在内存中多出的存储空间应填 0 补充。

        也就是说,段在内存中可以比在文件中占用空间更大;而相反,p_filesz永远不应该比 p_memsz 大,因为这样的话,内存中就将无法完整地映射段的内容。在程序头表中,所有 PT_LOAD 类型的程序头按照 p_vaddr 的值做升序排列。 

        从(p_offset)0x0的位置开始加载31C8(12744)个字节 加载进内存。此内存页的权限为 可读,可执行。  当p_vadder 不为 0 时情况如下;

2.  program_table_element[1] (可加载段)

         从(p_offset)0x3C78的位置开始加载3C8(968)个字节 加载进内存 对于 so基地址 加上 4C78偏移的位置。此内存页的权限为 可读,可写,可执行。

        p_data 是圈出来的段的内容。 

        此段也是init_array。

3.  program_table_element[2] (动态段)

        p_type 类型为 PT_DYNAMIC。

        如果一个目标文件参与动态连接的话,它的程序头表中一定会包含一个类型为PT_DYNAMIC 的表项,其所对应的段称为动态段(dynamic segment),段的名字为.dynamic。

        动态段的作用是提供动态连接器所需要的信息,比如依赖于哪些共享目标文件、动态连接符号表的位置、动态连接重定位表的位置等等。

        它包含一个由 如下结构体组成的数组。

  1. typedef struct {
  2. Elf64_Sxword d_tag;
  3. union {
  4. Elf64_Xword d_val;
  5. Elf64_Addr d_ptr;
  6. } d_un;
  7. } Elf64_Dyn;

对于每一个这种类型的目标项,d_tag 控制着对 d_un 的解析。

截取了一部分tags

tags[13] 的type是 DT_STRTAB 也就是字符串表所在的地址。

d_ptr 是 0x978

tags[14] 的type是 DT_SYMTAB 也就是符号表所在的地址

d_ptr 是 0x480

tags[16] 的type是 DT_SYMENT 也就是每个符号表条目的大小

d_val 是 24 

3.  tag type

名称
DT_NULL0Terminating entry.
DT_NEEDED1所需共享库的字符串表偏移量
DT_PLTRELSZ2PLT重定位的总大小(以字节为单位)
DT_PLTGOT3

                        处理器相关地址

DT_HASH4符号哈希表的地址
DT_STRTAB5字符串表的地址
DT_SYMTAB6符号表的地址
DT_RELA7ElfNN_Rela 重定位的地址
DT_RELASZ8ElfNN_Rela 重定位的总大小
DT_RELAENT9每个 ElfNN_Rela 重定位项的大小
DT_STRSZ10字符串表的总大小
DT_SYMENT11每个符号表项的大小
DT_INIT12init函数的地址
DT_FINI13fini函数的地址
DT_SONAME14以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于标识共享目标文件的名称。
DT_RPATH15以空字符结尾的库搜索路径字符串的 DT_STRTAB 字符串表偏移。此元素的用途已被 DT_RUNPATH 取代。
DT_SYMBOLIC16表示目标文件包含在其链接编辑过程中应用的符号绑定。
DT_REL17与 DT_RELA 类似,但其表中包含隐式加数。此元素要求同时存在 DT_RELSZ 和 DT_RELENT 元素。
DT_RELSZ18DT_REL 重定位表的总大小
DT_RELENT19DT_REL 重定位项的大小
DT_PLTREL20表示过程链接表指向的重定位项的类型(DT_REL 或 DT_RELA)。过程链接表中的所有重定位都必须使用相同的重定位项。此元素要求同时存在 DT_JMPREL 元素
DT_DEBUG21用于调试
DT_TEXTREL22表示一个或多个重定位项可能会要求修改非可写段,并且运行时链接程序可以相应地进行准备
DT_JMPREL23与过程链接表单独关联的重定位项的地址,此元素要求同时存在 DT_PLTRELSZ 和 DT_PLTREL 元素
DT_BIND_NOW24表示在将控制权返回给程序之前,必须处理此目标文件的所有重定位项
DT_INIT_ARRAY25init_array函数的指针数组的地址,此元素要求同时存在 DT_INIT_ARRAYSZ 元素
DT_FINI_ARRAY26fini_array函数的指针数组的地址,此元素要求同时存在 DT_FINI_ARRAYSZ 元素
DT_INIT_ARRAYSZ  27DT_INIT_ARRAY 数组的总大小
DT_FINI_ARRAYSZ28DT_FINI_ARRAY 数组的总大小
DT_RUNPATH29以空字符结尾的库搜索路径字符串的 DT_STRTAB 字符串表偏移
DT_FLAGS30特定于此目标文件的标志值。
DT_MAXPOSTAGS34正动态数组标记值的数量
DT_RELRSZ35ElfNN_Relr 重定位的总大小
DT_RELR36ElfNN_Relr 重定位的地址
DT_RELRENT37每个 ElfNN_Relr 重定位的大小
DT_LOOS0x6000000d此范围内包含的值(包括这两个值)保留用于特定于操作系统的语义
DT_SUNW_AUXILIARY0x6000000d以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于逐符号指定一个或多个辅助 filtee
DT_SUNW_RTLDINF0x6000000e保留供运行时链接程序内部使用
DT_SUNW_FILTER0x6000000f符号过滤器名称,以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于逐符号指定一个或多个标准 filtee
DT_SUNW_CAP0x60000010功能节的地址
DT_SUNW_ASLR0x60000023ASLR控制
DT_HIOS0x6ffff000特定于操作系统

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/319261
推荐阅读
相关标签
  

闽ICP备14008679号