安卓逆向之native层的so文件格式
安卓逆向之native层的so文件格式
安卓逆向的相关工具介绍
反编译代码的工具:
反编译资源的工具:
- APKTool: 本文重要工具,APK逆向工具。它可以将资源解码,并在修改后可以重新构建它们。它还可以执行一些自动化任务,例如构建apk。解码就是将其恢复成未打包的资源文件(包括resources.arsc,class.dex,9.png和xml);解码的资源可以重新打包成apk/jar文件;组织和处理依赖于框架资源的APK;Smali调试;执行自动化任务
- JEB:JEB是一个功能强大为安全专业人士设计的安卓应用程序反编译工具,用于逆向工程或者审计apk文件。它是可以直接反编译apk文件的。
- jadx:jadx是一款反编译利器,同时支持命令行和图形界面,能以最简便的方式完成apk的反编译操作。工具支持apk、dex、jar、aar等格式的文件
so文件介绍
概念:.so 文件是ELF对象文件中可被共享的对象文件(Shared object file),这些就是所谓的动态库文件。
动态文件的作用:如果用静态库来生成可执行程序,那每个生成的可执行程序中都会有一份库代码的拷贝。如果在磁盘中存储这些可执行程序,那就会占用额外的磁盘空 间;另外如果拿它们放到Linux系统上一起运行,也会浪费掉宝贵的物理内存。如果将静态库换成动态库,那么这些问题都不会出现。
Android中的so文件就是elf文件,所以需要了解so文件,必须先来了解一下elf文件的格式。
elf文件格式
为了让大家对elf文件格式有一个整体的了解,首先先看一张非虫先生总结好的图片讲解:
ELF文件的两种视图,分别是链接视图和执行视图。
链接视图:是在链接时用到的视图, 以节(section)为单位,下图左侧的视角是从链接来看的。从图中我们得到,在链接阶段,我们可以忽略program header table来处理此文件,因为它是按照section header table来处理此文件的。
执行视图:在执行时用到的视图,是以段(segment)为单位,下图右侧的视角是执行来看的。从图中我们可以得到,在运行阶段可以忽略section header table来处理此程序,因为它是按照program header table来处理此文件的。
文件的组成:
- ELF header: 描述整个文件的组织。
- Program Header Table: 描述文件中的各种segments,用来告诉系统如何创建进程映像的。
- sections 或者 segments:segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件。从图中我们也可以看出,segments与sections是包含的关系,一个segment包含若干个section。
- Section Header Table: 包含了文件各个segction的属性信息
区分两种视图的原因:
在内存之中,多个具有相同权限(flg值)section合并一个segment。操作系统往往以页为基本单位来管理内存分配,以及内存的权限管理的粒度也是以页为单位。每个section在映射时的长度都是系统页长度的整数倍,如果section的长度不是其整数倍,则导致多余部分也将占用一个页,因为一个ELF文件具有很多的section,那么这样将会导致内存浪费严重。为了减少页面内部的碎片,节省了空间,显著提高内存利用率,就将相同权限(flg值)section合并一个segment。
ELF Header
32位ELF文件中常用的数据格式:
ELF Header的结构体:
1 | #define EI_NIDENT 16 |
查看ELF Header结构的内容命令: readelf -h android_server
e_entry
e_entry表示程序入口地址。谓程序进入点是指当程序真正执行起来的时候,要运行的指令的运行时地址。其第一条可执行文件test和动态库.so都存在所谓的进入点,且可执行文件的e_entry指向C库中的**_start,而动态库.so**中的进入点指向 call_gmon_start。
重点关注的字段
在ELF Header中我们需要重点关注以下几个字段:
- e_entry:表示程序入口地址
- e_ehsize:ELF Header结构大小
- e_phoff、e_phentsize、e_phnum:描述Program Header Table的偏移、大小、结构。
- e_shoff、e_shentsize、e_shnum:描述Section Header Table的偏移、大小、结构。
- e_shstrndx:这一项描述的是字符串表在Section Header Table中的索引,值25表示的是Section Header Table中第25项是字符串表(String Table)
Section Header Table
section head table(SHT)包含了用来描述每一个section的条目(entry),每一个entry的内容主要包括该 section 的名称、类型、大小以及在整个ELF文件中的字节偏移位置等等。
每个条目结构定义:
1 | typedef struct{ |
Section
有些节区是系统预订的,一般以点开头号,
常用的系统节区
| 名称 | 类型 | 属性 | 含义 |
|---|---|---|---|
| .bss | SHT_NOBITS | SHF_ALLOC + SHF_WRITE | 包含将出现在程序的内存映像中的为初始化数据。根据定义,当程序开始执行,系统将把这些数据初始化为 0。此节区不占用文件空间。 |
| .comment | SHT_PROGBITS | (无) | 包含版本控制信息。 |
| .data | SHT_PROGBITS | SHF_ALLOC + SHF_WRITE | 这些节区包含初始化了的数据,将出现在程序的内存映像中。 |
| .data1 | SHT_PROGBITS | SHF_ALLOC + SHF_WRITE | 这些节区包含初始化了的数据,将出现在程序的内存映像中。 |
| .debug | SHT_PROGBITS | (无) | 此节区包含用于符号调试的信息。 |
| .dynamic | SHT_DYNAMIC | 此节区包含动态链接信息。节区的属性将包含 SHF_ALLOC 位。是否 SHF_WRITE 位被设置取决于处理器。 | |
| .dynstr | SHT_STRTAB | SHF_ALLOC | 此节区包含用于动态链接的字符串,大多数情况下这些字符串代表了与符号表项相关的名称。 |
| .dynsym | SHT_DYNSYM | SHF_ALLOC | 此节区包含了动态链接符号表。 |
| .fini | SHT_PROGBITS | SHF_ALLOC + SHF_EXECINSTR | 此节区包含了可执行的指令,是进程终止代码的一部分。程序正常退出时,系统将安排执行这里的代码。 |
| .got | SHT_PROGBITS | 此节区包含全局偏移表。 | |
| .hash | SHT_HASH | SHF_ALLOC | 此节区包含了一个符号哈希表。 |
| .init | SHT_PROGBITS | SHF_ALLOC + SHF_EXECINSTR | 此节区包含了可执行指令,是进程初始化代码的一部分。当程序开始执行时,系统要在开始调用主程序入口之前(通常指 C 语言的 main 函数)执行这些代码。 |
| .interp | SHT_PROGBITS | 此节区包含程序解释器的路径名。如果程序包含一个可加载的段,段中包含此节区,那么节区的属性将包含 SHF_ALLOC 位,否则该位为 0。 | |
| .line | SHT_PROGBITS | (无) | 此节区包含符号调试的行号信息,其中描述了源程序与机器指令之间的对应关系。其内容是未定义的。 |
| .note | SHT_NOTE | (无) | 此节区中包含注释信息,有独立的格式。 |
| .plt | SHT_PROGBITS | 此节区包含过程链接表(procedure linkage table)。 | |
| .relname .relaname | SHT_REL SHT_RELA | 这些节区中包含了重定位信息。如果文件中包含可加载的段,段中有重定位内容,节区的属性将包含 SHF_ALLOC 位,否则该位置 0。传统上 name 根据重定位所适用的节区给定。例如 .text 节区的重定位节区名字将是:.rel.text 或者 .rela.text。 | |
| .rodata .rodata1 | SHT_PROGBITS | SHF_ALLOC | 这些节区包含只读数据,这些数据通常参与进程映像的不可写段。 |
| .shstrtab | SHT_STRTAB | 此节区包含节区名称。 | |
| .strtab | SHT_STRTAB | 此节区包含字符串,通常是代表与符号表项相关的名称。如果文件拥有一个可加载的段,段中包含符号串表,节区的属性将包含SHF_ALLOC 位,否则该位为 0。 | |
| .symtab | SHT_SYMTAB | 此节区包含一个符号表。如果文件中包含一个可加载的段,并且该段中包含符号表,那么节区的属性中包含SHF_ALLOC 位,否则该位置为 0。 | |
| .text | SHT_PROGBITS | SHF_ALLOC + SHF_EXECINSTR | 此节区包含程序的可执行指令。 |
so文件中重要的Section
-符号表(.dynsym)
符号表包含用来定位、重定位程序中符号定义和引用的信息,简单的理解就是符号表记录了该文件中的所有符号,所谓的符号就是经过修饰了的函数名或者变量名,不同的编译器有不同的修饰规则。例如符号_ZL15global_static_a,就是由global_static_a变量名经过修饰而来。
格式:
1 | typedef struct { |
符号表的内容
-字符串表(.dynstr)
字符串表中存放着所有符号的名称字符串。
-重定位表
重定位就是为程序不同部分分配加载地址,调整程序中的数据和代码以反映所分配地址的过程。简单的言之,则是将程序中的各个部分映射到合理的地址上来。例如,当程序调用了一个函数时,相关的调用指令必须把控制传输到适当的目标执行地址。
重定位文件必须包含如何修改其节区内容的信息,从而允许可执行文件和共享目标文件保存进程的程序映象的正确信息。
重定位表项的格式:
1 | typedef struct { |
r_info 成员使用 ELF32_R_TYPE 宏运算可得到重定位类型,使用 ELF32_R_SYM 宏运算可得到符号在符号表里的索引值。
三种宏定义:
1 | #define ELF32_R_SYM(i) ((i)>>8) |
重定位表中的内容
Program Header Table
程序头部(Program Header)用来在文件中定位各个段的映像,同时包含其他一些用来为程序创建映像所必须的信息。
文件头部的格式:
1 | typedef struct { |
程序头部表中的内容:
so文件格式中结构体的某些参数未详细说明,可以去查看下面的这个参考文章。
参考文章:










