文章目录

  • 原生程序文件格式
    • 原生程序的文件类型
    • AArch64 ELF 文件格式
      • e_ident
      • e_type
      • e_machine
      • e_version
      • e_entry
      • e_phoff
      • e_shoff
      • e_flags
      • e_phentsize、e_phnum
      • e_shentsize、e_shnum
      • e_shstrndx
    • Program Header Table
      • p_type
      • p_flags
      • p_offset
      • p_vaddr
      • p_paddr
      • p_filesz
      • p_memsz
      • p_align
    • Section Header Table
      • sh_name
      • sh_type
      • sh_flags
      • sh_addr
      • sh_offset、sh_size
      • sh_link、sh_info
      • sh_addralign
      • sh_entsize
    • .dynamic 节区
      • d_tag

原生程序文件格式

  • Android 原生程序符合 ARM 的 EABI 规范并用 ELF 作为可执行程序文件格式
  • ARM ELF File Format 文档描述了 ARM 平台上 ELF 文件必须实现和遵循的规范和细节,Android 在实现该规范时扩展了它
  • 不同处理器架构的 ELF 文件,仅在平台架构细节方面存在差异,ELF 文件的整体结构和使用的数据类型在定义上是一致的
  • 除了要符合 EABI 规范,Android 平台上的 ELF 文件的生成还要依靠链接器执行链操作时的链接脚本。链接脚本定义了生成的可执行程序中各节区的名称、地址空间布局及一些文件格式的链接,不同的链接脚本生成的原生可执行文件的格式可能不同。Android 原生程序的编译工具链用的编译器是 gcc 和 Clang,Clang 用的链接器是 gcc 的后端,因此所有原生程序的链接工作都由编译工具链中的 ld 命令和相应的链接脚本完成
  • 可用之前章节提到的 010 Editor 的 ELF.bt 模板学习 ELF 文件格式

原生程序的文件类型

  • 软件开发中,经常能看到原生程序类型有扩展名为“.a”的静态链接库、“.so”的动态链接库及没有扩展名的原生可执行文件。若关注编译细节,还能看到“.o”的目标文件(Object File)。虽然这些文件都属于原生程序,但只有原生可执行文件才可直接执行,其他的都要通过间接的方式加载执行
  • ELF 格式文档将原生程序分为三类描述:
    • Relocatable File:可重定位文件,以“.o”结尾的目标文件
    • Executable File:原生可执行文件
    • Shared Object File:共享的目标文件
  • 可执行如下命令生成这三种类型文件:

  • 上图一是用的 Clang,加不加 -fPIE -pie 编译出的都是共享的目标文件。上图二用的是 gcc,不加上述参数编译出的是原生可执行文件,加了编译出的是共享的目标文件。编译时,若只编译不链接,生成的“.o”文件就是可重定位文件
  • 上图中用 file 命令查看 app 文件时,输出中有“LSB”,说明程序是小端字节序(Little-Endian)的,若输出是“MSB”,说明是大端字节序的。Android 系统默认使用小端字节序

AArch64 ELF 文件格式

  • 原生可执行文件与共享的目标文件很相似,下面的文件格式学习中,若不特意指明,则对二者皆有效,二者的区别会在合适时机学习
  • 一个 ELF 文件可按内容分为三部分:
    • ELF Header:文件头
    • Program Header Table:程序头表,包含多个 Program Header 和 Segment(段)数据
    • Section Header Table:节区头表,包含多个 Section Header 和 Section(节区)数据
  • ELF Header 描述了 ELF 的基本文件信息,其中就有 Program Header Table 和 Section Header Table 在 ELF 中的偏移
  • Program Header Table 定义的是 Segment 信息。Segment 的概念和 macOS 中的 Mach-O、Windows 中的 PE 类似,描述了一个地址段数据的读、写、执行属性及是否会加载到内存等信息。ELF 中的 Program Header Table 可有多个 Program Header,每个 Program Header 对应一个 Segment,Segment 的个数由 ELF Header 给出
  • Section Header Table 定义的是 Section 信息。其中的 Section Header 在 ELF 中可有多个,每个 Section Header 对应一个 Section 的数据,Section 的个数在 ELF Header 中给出
  • Section 和 Segment 的关系:一个 Segment 可包含多个 Section;每个 Section 只属于一个 Segment(特殊情况下可同时属于多个 Segment,且 Segment 间的数据可重叠)
  • ELF 根据链接和执行阶段所需的文件信息,提供了链接视图(Linking View)和执行视图(Execution View):
  • 可看到,Section 包含了链接时需要的信息,而 Segment 包含了运行时需要的信息。ELF 在链接时,链接器通过 Section Header Table 寻找节区信息;在运行时,加载器通过 Program Header Table 寻找 Segment 并加载
  • “.o”文件作为“中间结果”,既不能加载,也不能执行。可重定位的“.o”文件不包含 Program Header Table。低版本的 Android 系统中,由于执行时不依赖 Section Header Table 的信息,对加载到内存的 ELF,Section Header Table 没有用,很多 so 的软件壳通过这个特性修改与 ELF 文件头的 Section Header Table 相关的文件结构信息,达到反逆向分析。Android 7.0 及后来的版本,这种保护方式无效,动态链接库在加载 ELF 时加强了对文件格式的验证,包括在加载时校验 ELF 的 Section Header Table 信息,因此软件壳要调整保护策略
  • ELF 文件的格式定义可在 Android NDK 的头文件 elf.h 中找到,也可在 Android 源码文件 art/runtime/elf.h 中找到。Android 平台上 ELF 文件的 EABI 还没稳定下来,每次 Android 系统升级和 NDK 升级都可能使文件格式变化
  • elf.h 重新定义了程序使用的基本数据类型:
typedef __u32 Elf32_Addr;
typedef __u16 Elf32_Half;
typedef __u32 Elf32_Off;
typedef __s32 Elf32_Sword;
typedef __u32 Elf32_Word;typedef __u64 Elf64_Addr;
typedef __u16 Elf64_Half;
typedef __s16 Elf64_SHalf;
typedef __u64 Elf64_Off;
typedef __s32 Elf64_Sword;
typedef __u32 Elf64_Word;
typedef __u64 Elf64_Xword;
typedef __s64 Elf64_Sxword;

  • Elf32 表示该数据类型是 32 位的 ELF,Elf64表示该数据类型是 64 位的 ELF,“Addr”结尾的数据表示地址指针,“Off”结尾的数据表示文件偏移,其他数据类型表示不同的字段占用的字节数
  • 不同的 ELF 架构,其 ELF Header 也不同。32 位的 ELF 文件头用结构体 Elf32_Ehdr 表示,64 位的用 Elf64_Ehdr 表示:
#define EI_NIDENT 16
typedef struct elf32_hdr {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;typedef struct elf64_hdr {unsigned char e_ident[EI_NIDENT];Elf64_Half e_type;Elf64_Half e_machine;Elf64_Word e_version;Elf64_Addr e_entry;Elf64_Off e_phoff;Elf64_Off e_shoff;Elf64_Word e_flags;Elf64_Half e_ehsize;Elf64_Half e_phentsize;Elf64_Half e_phnum;Elf64_Half e_shentsize;Elf64_Half e_shnum;Elf64_Half e_shstrndx;
} Elf64_Ehdr;

e_ident

  • Elf32_Ehdr 和 Elf64_Ehdr 的字段基本一样,只数据类型的位数不同,判断一个 ELF 是 32 位还是 64 位,依据是第一个 e_ident 字段,EI_NIDENT 的值为 16,在 e_ident 字段的 16 字节中,按每个字节的序号定义了它们的用途
#define EI_MAG0 0
#define EI_MAG1 1
#define EI_MAG2 2
#define EI_MAG3 3
#define EI_CLASS 4
#define EI_DATA 5
#define EI_VERSION 6
#define EI_OSABI 7
#define EI_PAD 8#define ELFMAG0 0x7f
#define ELFMAG1 'E'
#define ELFMAG2 'L'
#define ELFMAG3 'F'
#define ELFMAG "\177ELF"
#define SELFMAG 4
#define ELFCLASSNONE 0
#define ELFCLASS32 1
#define ELFCLASS64 2
#define ELFCLASSNUM 3
#define ELFDATANONE 0
#define ELFDATA2LSB 1
#define ELFDATA2MSB 2
#define EV_NONE 0
#define EV_CURRENT 1
#define EV_NUM 2
#define ELFOSABI_NONE 0
#define ELFOSABI_LINUX 3
#ifndef ELF_OSABI
#define ELF_OSABI ELFOSABI_NONE

  • EI_MAG0 ~ EI_MAG3 这四个字节固定为 0x7f、‘E’、‘L’、‘F’,表示这是 ELF 文件
  • EI_CALSS 表示 ELF 的文件类别:有效值 1 被定义为 ELFCLASS32,表示这是 32 位的 ELF 文件;有效值 2 被定义为 ELFCLASS64,表示这是 64 位的 ELF 文件
  • EI_DATA 表示 ELF 的字节序类型:有效值 1 被定义为 ELFDATA2LSB,表示这是小端字节序的 ELF;有效值 2 被定义为 ELFDATA2MSB,表示这是大端字节序的 ELF
  • EI_VERSION 表示文件版本,有效值 1 和 2 表示 EV_CURRENT 和 EV_NUM
  • EI_OSABI 表示 ELF 的系统 ABI,可选值较多,默认情况下值 0 被定义为 ELFOSABI_NONE,表示 UNIX System V ABI
  • EI_PAD 是起始 0 填充字段,表示从这里开始 e_ident 字段中的字节都为 0

e_type

  • 该字段表示 ELF 文件的类型,其可选值如下:
#define ET_NONE 0
#define ET_REL 1
#define ET_EXEC 2
#define ET_DYN 3
#define ET_CORE 4
#define ET_LOPROC 0xff00
#define ET_HIPROC 0xffff

  • ET_REL 表示 Relocatable,是可重定位文件,“.o”文件通常属于该类型
  • ET_EXEC 表示 Executable,是可执行文件,没用 -fPIE -pie 参数编译的原生程序即属该类型
  • ET_DYN 表示 Shared Object,是动态链接的目标文件,用上述参数编译的原生程序和用 -shared 参数编译的动态库即属该类型

e_machine

  • 该字段描述机器架构(Machine Architectures),即该 ELF 文件可在什么样的机器上运行。由于 ELF 适用于各类 Linux 设备和嵌入式设备,该字段可选值很多。对 64 位的 Android 原生程序来说,该字段的值为 EM_AARCH64;对 32 位来说,值为 EM_ARM

e_version

  • 该字段描述文件版本,在 Android 的 ELF 上,只有一个有效值为 1 的 EV_CURRENT

e_entry

  • 该字段指明 ELF 的入口函数,其值通常指向 ELF 中 _start 导出符号的地址

e_phoff

  • 该字段指明 Program Header Table 在文件中的偏移地址

e_shoff

  • 该字段指明 Section Header Table 在文件中的偏移地址

e_flags

  • 该字段指明 EABI 的标志信息。若编译时用 -mfpu 参数指定浮点寄存器,则其值可能为 EF_ARM_SOFT_FLOAT 或 EF_ARM_VFP_FLOAT;若没指定,则其值为 0,表示 EF_ARM_EABI_UNKNOWN

  • e_ehsize
  • 该字段表示当前 Elf32_Ehdr 或 Elf64_Ehdr 结构体占用的字节数

e_phentsize、e_phnum

  • 该字段分别指定 Program Header Table 的每一项占用的字节数和个数

e_shentsize、e_shnum

  • 该字段分别指定 Section Header Table 的每一项占用的字节数和个数

e_shstrndx

  • 该字段是个索引值,表示“.shstrtab”字符串表是第几个 Section Header Table

  • 整个 ELF 文件头包含的信息就这些,通过文件头信息可知道 ELF 是 32 位还是 64 位,是小端字节序还是大端字节序,包含多少个 Program Header Table 和 Section Header Table 等
  • 用 NDK 中的 aarch64-linux-android-readelf 可查看 app 的文件头信息:

Program Header Table

  • 文件头 Elf32_Ehdr 或 Elf64_Ehdr 下面就是多个 Program Header Table 和 Section Header Table
  • 先看 Program Header Table。它也分为 32 和 64 位版本,分别是 Elf32_Phdr 和 Elf64_Phdr:
typedef struct elf32_phdr {Elf32_Word p_type;Elf32_Off p_offset;Elf32_Addr p_vaddr;Elf32_Addr p_paddr;Elf32_Word p_filesz;Elf32_Word p_memsz;Elf32_Word p_flags;Elf32_Word p_align;
} Elf32_Phdr;typedef struct elf64_phdr {Elf64_Word p_type;Elf64_Word p_flags;Elf64_Off p_offset;Elf64_Addr p_vaddr;Elf64_Addr p_paddr;Elf64_Xword p_filesz;Elf64_Xword p_memsz;Elf64_Xword p_align;
} Elf64_Phdr;

p_type

  • 该字段描述了段信息(Segment Type),其常见取值:
#define PT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define PT_INTERP 3
#define PT_NOTE 4
#define PT_SHLIB 5
#define PT_PHDR 6
#define PT_TLS 7
#define PT_LOOS 0x60000000
#define PT_HIOS 0x6fffffff
#define PT_LOPROC 0x70000000
#define PT_HIPROC 0x7fffffff
#define PT_GNU_EH_FRAME 0x6474e550
#define PT_GNU_STACK (PT_LOOS + 0x474e551)#define PT_GNU_RELRO 0x6474e552

  • 原生可执行文件与共享的目标文件通常包含如下部分:

    • 多个(一般为两个) PT_LOAD 类型的 Segment。原生程序运行时,此类型的 Segment 会加载到内存
    • 一个 PT_DYNAMIC 类型的 Segment。此类型的 Segment 记录了非常重要的动态链接信息,包括原生程序依赖的动态链接库列表、初始化结构数组 init_array 的地址和大小、字符串表、地址重定位表等
    • 一个 PT_GNU_STACK 类型的 Segment。此类型的 Segment 决定了运行时的栈是否可执行。对 Android 原生程序而言,其默认值为可读、可写、不可执行
    • 一个 PT_GNU_RELRO 类型的 Segment。此类型的 Segment 决定了链接器执行重定位操作后,哪一块内存区域(通常为包含所有重定位信息的节区)将被设置为只读。PT_GNU_RELRO 和 PT_GNU_STACK 作用一样:加强程序的安全防护,通过减少程序运行时可写的存储区域来降低程序在运行时遭受动态攻击的风险
  • 原生可执行文件还包括如下部分:
    • 一个 PT_PHDR 类型的 Segment。它指向 Program Header Table 自身
    • 一个 PT_INTERP 类型的 Segment。它指向可执行程序的加载器路径。由 arm64-v8a 生成的 64 位原生程序,加载器通常是 /system/bin/linker64;由 armeabi 和 armeabi-v7a 生成的 32 位原生程序,加载器通常是 /system/bin/linker
  • 若在编译原生程序时用 -fexceptions 参数开启了异常支持,则会用一个 PT_ARM_EXIDX(elf.h 中没找到)类型的 Segment 存放与异常相关的信息

p_flags

  • 该字段指定了 Segment 的属性信息。其可选值:
#define PF_R 0x4    // Read
#define PF_W 0x2    // Write
#define PF_X 0x1    // Execute
  • 该字段的值可以是这些可选值的组合,如,值为 7,表示这三个标志全部有效,即 Segment 是可读可写可执行的

p_offset

  • 该字段指定该 Segment 在文件中的偏移量

p_vaddr

  • 该字段指定该 Segment 加载到内存后的虚拟地址

p_paddr

  • 该字段指定该 Segment 在用物理内存寻址的系统中加载后的物理地址,在用虚拟地址寻址的系统中该值被保留(无用)

p_filesz

  • 指定该 Segment 在当前文件中占用的字节数

p_memsz

  • 指定该 Segment 加载到内存后的字节数

p_align

  • 指定该 Segment 的内存地址对齐约束。对 64 位原生程序而言,PT_LOAD 指定了可执行代码的 Segment,其内存对齐值为 65536;PT_DYNAMIC 指定了存放数据的 Segment,其内存对齐值为 8(若取值为 0 或 1,表示没有地址对齐约束)

  • 执行如下命令,查看 app 的 Program Header Table 信息:
  • 可看出,Segment 和 Section 间存在映射关系,如,INTERP 对应唯一的 .interp 节区(01 行),LOAD 对应多个节区(03 行),包括 .preinit_array.init_array.fini_array.dynamic.got.bss

Section Header Table

  • Section Header Table 由多个 Section Header 和 Section 数据本身组成
  • 32 位 ELF 的 Section Header 用 Elf32_Shdr 结构体表示,64 位的用 Elf64_Shdr 结构体表示:
typedef struct elf32_shdr {Elf32_Word sh_name;Elf32_Word sh_type;Elf32_Word sh_flags;Elf32_Addr sh_addr;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;typedef struct elf64_shdr {Elf64_Word sh_name;Elf64_Word sh_type;Elf64_Xword sh_flags;Elf64_Addr sh_addr;Elf64_Off sh_offset;Elf64_Xword sh_size;Elf64_Word sh_link;Elf64_Word sh_info;Elf64_Xword sh_addralign;Elf64_Xword sh_entsize;
} Elf64_Shdr;

sh_name

  • 该字段指定 Section 的名称。是一个索引值,指向字符串在字符串表 .shstrtab 中的首地址

sh_type

  • 该字段描述 Section 的类型。取值较多:
#define SHT_NULL 0
#define SHT_PROGBITS 1
#define SHT_SYMTAB 2
#define SHT_STRTAB 3
#define SHT_RELA 4
#define SHT_HASH 5
#define SHT_DYNAMIC 6
#define SHT_NOTE 7
#define SHT_NOBITS 8
#define SHT_REL 9
#define SHT_SHLIB 10
#define SHT_DYNSYM 11
#define SHT_NUM 12
#define SHT_LOPROC 0x70000000
#define SHT_HIPROC 0x7fffffff
#define SHT_LOUSER 0x80000000
#define SHT_HIUSER 0xffffffff
  • 常见的值:

    • SHT_PROGBITS:表示 Section 包含了程序中定义的数据,.text.rodata 节区就是此类型的
    • SHT_STRTAB:字符串表,.dynstrstrtabshstrtab 节区就是此类型的
    • SHT_DYNAMIC:.dynamic 节区,对应 PT_DYNAMIC 类型的 Segment
    • SHT_SYMTAB:符号表,.symtab 节区就是此类型的
    • SHT_RELA:地址重定位表,.rela.dyn.rela.plt 节区就是此类型的

sh_flags

  • 该字段描述了 Section 的标志信息
#define SHF_WRITE 0x1
#define SHF_ALLOC 0x2
#define SHF_EXECINSTR 0x4
#define SHF_RELA_LIVEPATCH 0x00100000
#define SHF_RO_AFTER_INIT 0x00200000
#define SHF_MASKPROC 0xf0000000
  • 如,SHF_ALLOC 表示该 Section 可分配内存;SHF_EXECINSTR 表示该 Section 包含可执行的机器指令;SHF_EXECINSTR 和 SHF_ALLOC 组合后表示包含可执行机器指令的可分配内存(.text 节区通常如此设置)

sh_addr

  • 该字段指定了 Section 加载到内存后的地址

sh_offset、sh_size

  • 分别指定 Section 的数据在文件中的偏移量和大小

sh_link、sh_info

  • 只作用于少数类型的 Section,针对不同的 Section,其含义不同:

sh_addralign

  • 指定了 Section 的地址对齐约束,其值通常为 2 的幂。若该字段的值为 0 或 1,表示当前 Section 没有地址对齐约束

sh_entsize

  • 对固定大小的条目数据的 Section(如符号表)而言,sh_entsize 字段指定了条目数据中每条条目占用的字节数。若该字段取值为 0,表示当前 Section 没有固定大小的条目

  • 用 aarch64-linux-android-readelf 和 aarch64-linux-android-objdump 都可输出 app 的 Section Header Table 信息:
root@myUbuntu:~# $READELF -t app
There are 24 section headers, starting at offset 0x19f0:Section Headers:[Nr] NameType              Address          Offset            LinkSize              EntSize          Info              AlignFlags[ 0] NULL                   NULL             0000000000000000  0000000000000000  00000000000000000 0000000000000000  0                 0[0000000000000000]: [ 1] .interpPROGBITS               PROGBITS         0000000000000200  0000000000000200  00000000000000015 0000000000000000  0                 1[0000000000000002]: ALLOC[ 2] .note.android.identNOTE                   NOTE             0000000000000218  0000000000000218  00000000000000098 0000000000000000  0                 4[0000000000000002]: ALLOC[ 3] .hashHASH                   HASH             00000000000002b0  00000000000002b0  5000000000000003c 0000000000000004  0                 8[0000000000000002]: ALLOC[ 4] .gnu.hashGNU_HASH               GNU_HASH         00000000000002f0  00000000000002f0  50000000000000034 0000000000000000  0                 8[0000000000000002]: ALLOC[ 5] .dynsymDYNSYM                 DYNSYM           0000000000000328  0000000000000328  600000000000000f0 0000000000000018  3                 8[0000000000000002]: ALLOC[ 6] .dynstrSTRTAB                 STRTAB           0000000000000418  0000000000000418  0000000000000006c 0000000000000000  0                 1[0000000000000002]: ALLOC[ 7] .gnu.versionVERSYM                 VERSYM           0000000000000484  0000000000000484  50000000000000014 0000000000000002  0                 2[0000000000000002]: ALLOC[ 8] .gnu.version_rVERNEED                VERNEED          0000000000000498  0000000000000498  60000000000000020 0000000000000000  1                 8[0000000000000002]: ALLOC[ 9] .rela.dynRELA                   RELA             00000000000004b8  00000000000004b8  50000000000000060 0000000000000018  0                 8[0000000000000002]: ALLOC[10] .rela.pltRELA                   RELA             0000000000000518  0000000000000518  50000000000000048 0000000000000018  18                8[0000000000000042]: ALLOC, INFO LINK[11] .pltPROGBITS               PROGBITS         0000000000000560  0000000000000560  00000000000000050 0000000000000010  0                 16[0000000000000006]: ALLOC, EXEC[12] .textPROGBITS               PROGBITS         00000000000005b0  00000000000005b0  000000000000000ac 0000000000000000  0                 4[0000000000000006]: ALLOC, EXEC[13] .rodataPROGBITS               PROGBITS         000000000000065c  000000000000065c  00000000000000009 0000000000000001  0                 1[0000000000000032]: ALLOC, MERGE, STRINGS[14] .preinit_arrayPREINIT_ARRAY          PREINIT_ARRAY    0000000000010d68  0000000000000d68  00000000000000010 0000000000000008  0                 8[0000000000000003]: WRITE, ALLOC[15] .init_arrayINIT_ARRAY             INIT_ARRAY       0000000000010d78  0000000000000d78  00000000000000010 0000000000000008  0                 8[0000000000000003]: WRITE, ALLOC[16] .fini_arrayFINI_ARRAY             FINI_ARRAY       0000000000010d88  0000000000000d88  00000000000000010 0000000000000008  0                 8[0000000000000003]: WRITE, ALLOC[17] .dynamicDYNAMIC                DYNAMIC          0000000000010d98  0000000000000d98  60000000000000210 0000000000000010  0                 8[0000000000000003]: WRITE, ALLOC[18] .gotPROGBITS               PROGBITS         0000000000010fa8  0000000000000fa8  00000000000000058 0000000000000008  0                 8[0000000000000003]: WRITE, ALLOC[19] .bssNOBITS                 NOBITS           0000000000011000  0000000000001000  00000000000000008 0000000000000000  0                 8[0000000000000003]: WRITE, ALLOC[20] .commentPROGBITS               PROGBITS         0000000000000000  0000000000001000  00000000000000108 0000000000000001  0                 1[0000000000000030]: MERGE, STRINGS[21] .shstrtabSTRTAB                 STRTAB           0000000000000000  000000000000191e  000000000000000d2 0000000000000000  0                 1[0000000000000000]: [22] .symtabSYMTAB                 SYMTAB           0000000000000000  0000000000001108  230000000000000660 0000000000000018  49                8[0000000000000000]: [23] .strtabSTRTAB                 STRTAB           0000000000000000  0000000000001768  000000000000001b6 0000000000000000  0                 1[0000000000000000]:

.dynamic 节区

  • .dynamic 节区在 Section Header Table 中较重要。链接器在执行链接操作前,会调用 soinfo::prelink_image() 进行预链接处理,后者内部会调用 phdr_table_get_dynamic_section() 解析 .dynamic 节区,然后通过 .dynamic 节区获取其他所有要用的节区的信息。phdr_table_get_dynamic_section() 解析 .dynamic 的方法是在 Program Header Table 中查找类型为 PT_DYNAMIC 的 Segment,从而得到 .dynamic 节区在文件中的偏移量和大小
  • .dynamic 节区用特定的数据结构存放 Section 条目信息,32 位的 ELF 用的是 Elf32_Dyn 结构体,64 位用的是 Elf64_Dyn 结构体:
typedef struct dynamic {Elf32_Sword d_tag;union {Elf32_Sword d_val;Elf32_Addr d_ptr;} d_un;
} Elf32_Dyn;typedef struct {Elf64_Sxword d_tag;union {Elf64_Xword d_val;Elf64_Addr d_ptr;} d_un;
} Elf64_Dyn;

d_tag

  • 描述了节区的类型,其值会影响 d_un 字段的类型和值。该字段的可选值:
#define DT_NULL 0
#define DT_NEEDED 1
#define DT_PLTRELSZ 2
#define DT_PLTGOT 3
#define DT_HASH 4
#define DT_STRTAB 5
#define DT_SYMTAB 6
#define DT_RELA 7
#define DT_RELASZ 8
#define DT_RELAENT 9
#define DT_STRSZ 10
#define DT_SYMENT 11
#define DT_INIT 12
#define DT_FINI 13
#define DT_SONAME 14
#define DT_RPATH 15
#define DT_SYMBOLIC 16
#define DT_REL 17
#define DT_RELSZ 18
#define DT_RELENT 19
#define DT_PLTREL 20
#define DT_DEBUG 21
#define DT_TEXTREL 22
#define DT_JMPREL 23
#define DT_ENCODING 32
#define OLD_DT_LOOS 0x60000000
#define DT_LOOS 0x6000000d
#define DT_HIOS 0x6ffff000
#define DT_VALRNGLO 0x6ffffd00
#define DT_VALRNGHI 0x6ffffdff
#define DT_ADDRRNGLO 0x6ffffe00
#define DT_ADDRRNGHI 0x6ffffeff
#define DT_VERSYM 0x6ffffff0
#define DT_RELACOUNT 0x6ffffff9
#define DT_RELCOUNT 0x6ffffffa
#define DT_FLAGS_1 0x6ffffffb
#define DT_VERDEF 0x6ffffffc
#define DT_VERDEFNUM 0x6ffffffd
#define DT_VERNEED 0x6ffffffe
#define DT_VERNEEDNUM 0x6fffffff
#define OLD_DT_HIOS 0x6fffffff
#define DT_LOPROC 0x70000000
#define DT_HIPROC 0x7fffffff/* http://www.sco.com/developers/gabi/latest/ch5.dynamic.html */
#define DT_BIND_NOW 24
#define DT_INIT_ARRAY 25
#define DT_FINI_ARRAY 26
#define DT_INIT_ARRAYSZ 27
#define DT_FINI_ARRAYSZ 28
#define DT_RUNPATH 29
#define DT_FLAGS 30
/* glibc and BSD disagree for DT_ENCODING; glibc looks wrong. */
#define DT_PREINIT_ARRAY 32
#define DT_PREINIT_ARRAYSZ 33/* Experimental support for SHT_RELR sections. For details, see proposalat https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg */
#define DT_RELR 0x6fffe000
#define DT_RELRSZ 0x6fffe001
#define DT_RELRENT 0x6fffe003
#define DT_RELRCOUNT 0x6fffe005/* Android compressed rel/rela sections */
#define DT_ANDROID_REL (DT_LOOS + 2)
#define DT_ANDROID_RELSZ (DT_LOOS + 3)
#define DT_ANDROID_RELA (DT_LOOS + 4)
#define DT_ANDROID_RELASZ (DT_LOOS + 5)#define DT_GNU_HASH 0x6ffffef5
#define DT_TLSDESC_PLT 0x6ffffef6
#define DT_TLSDESC_GOT 0x6ffffef7

  • DT_NEEDED 表示 d_un 字段中存放的是当前 ELF 依赖的动态库的名称字符串在字符串表中的偏移地址;DT_STRTAB 表示 d_un 字段中存放的是动态字符串表在文件中的偏移地址;DT_SONAME 表示当前动态库的名称字符串在字符串表中的偏移地址;DT_INIT_ARRAY 和 DT_FINI_ARRAY 分别表示 d_un 字段中存放的 .init_array.fini_array 在文件中的偏移地址
  • 总结:.dynamic 节区中存放了进行动态链接时需要用的所有与链接相关的信息
  • 所有用 d_tag 和 d_un 表示的节区类型,以及解析和处理这些节区的方法,都可在 phdr_table_get_dynamic_section() 中找到
  • 执行如下命令,可查看 .dynamic 节区信息:

第八章 Android 原生程序开发与逆向分析(三)(原生程序文件格式)相关推荐

  1. 第八章 Android 原生程序开发与逆向分析(五)(原生 C 程序逆向分析)

    文章目录 原生 C 程序逆向分析 编译原生 C 程序 for 循环分支结构 for1() for2() while 循环分支结构 dowhile() whiledo() if--else 分支结构 i ...

  2. 移动安全逆向著作《Android应用安全防护和逆向分析》终于发售了,先来一波签名送书福利!

    经历了一年半,我的毕生精力著作终于和大家见面了,写书很不容易,这段时间感谢大家对我的文章的支持和肯定,也有很多同学和我反馈要是能写一本书这么详细介绍内容就好了.其实我一直在写,只是没有写好的那一天还不 ...

  3. 微信小程序开发系列五:微信小程序中如何响应用户输入事件

    微信小程序开发系列教程 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 微信小程序开发系列二:微信小程序的视图设计 微信小程序开发系列三:微信小程序的调试方法 微信小程序开发系列四:微信小程序 ...

  4. 微信小程序开发系列二:微信小程序的视图设计

    大家如果跟着我第一篇文章 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 一起动手,那么微信小程序的开发环境一定搭好了.效果就是能把该小程序的体验版以二维码的方式发送给其他朋友使用. 这个系列 ...

  5. 微信小程序开发(3) - 微信小程序调用摄像头静默隐藏拍照功能实现方式

    微信小程序最近非常火热,小编最近做了一个微信小程序开发新项目,使用小程序开发考试系统,在使用App参加考试的时候调用摄像头抓拍用户是否作弊,在开发过程中遇到点问题,下面小编把问题描述和解决方法分享给大 ...

  6. 开放下载 | 《Knative 云原生应用开发指南》开启云原生时代 Serverless 之门

    点击下载<Knative 云原生应用开发指南> 自 2018 年 Knative 项目开源后,就得到了广大开发者的密切关注.Knative 在 Kubernetes 之上提供了一套完整的应 ...

  7. arm-linux 程序开发入门(QT窗口应用程序、编码、交叉编译、调试)(三机器和双机器搭建方法)(笔记)

    Linux及Arm-Linux程序开发笔记(零基础入门篇) 文章目录 前言 一.Arm-Linux程序开发平台简要介绍 1.1程序开发所需系统及开发语言 1.2系统平台搭建方式 二.Linux开发平台 ...

  8. 微信小程序开发系列七:微信小程序的页面跳转

    微信小程序开发系列教程 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 微信小程序开发系列二:微信小程序的视图设计 微信小程序开发系列三:微信小程序的调试方法 微信小程序开发系列四:微信小程序 ...

  9. 微信小程序开发系列四:微信小程序之控制器的初始化逻辑

    微信小程序开发系列教程 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 微信小程序开发系列二:微信小程序的视图设计 微信小程序开发系列三:微信小程序的调试方法 这个教程的前两篇文章,介绍了如何 ...

最新文章

  1. 嵌入式学习笔记之四 (uboot启动流程)
  2. 余承东:华为 P50 系列无 5G 版本,但依然流畅
  3. hibernate教程--持久化类状态详解
  4. 1QPushButton的使用,QLineEdit的使用,设置组件位置,布局(QHBoxLayout,QGridLayout)
  5. 最常用计算机机箱,电脑机箱的常用材质是什么?
  6. php读取大文件详解【OK】
  7. zend studio【快捷键】
  8. Brinson归因模型
  9. php项目推荐,php教程推荐:最值得推荐的10个php教程
  10. 《人件》(Peopleware)文摘
  11. 误码率与信噪比的关系matlab,误码率BER与信噪比SNR的关系解析
  12. 中文谚语 enlish version
  13. 0xFEFEFEFE 处有未经处理的异常(在xx中): 0xC00001A5: 检测到无效的异常处理程序例程。
  14. 【更新公告】AirtestPoco更新
  15. 计算机研究生论文数学公式,研究生论文公式符号细则.doc
  16. 哈佛啥时候已经成功制造了60只人工蜜蜂?2013年么?《黑镜》第三季第六集,细思极恐
  17. matlab jpg合成gif,用MATLAB将照片合成视频或者GIF图片、以及Photoshop制作GIF图片
  18. Java温故而知新-递归
  19. AutoSar DaVinci Developer工具的基本介绍
  20. JavaScript网页生日快乐

热门文章

  1. 微服务中的熔断、限流、降级
  2. 我可以蹲下来,陪你做一只蘑菇
  3. python中input()函数详解
  4. 数字化转型要避免“唯技术论”!
  5. 开发者实名认证的一般通行做法
  6. 因换系统 scp 无法使用了,已经解决
  7. MATLAB event 和 listener 简介
  8. 强烈推荐3DMax十三款出色的VFX插件
  9. WampServer2.0i.exe可正常安装ECShop_V2.7.2 utf8
  10. python os.environ 不生效_python – os.environ没有显示一些变量