【安全与逆向】- so文件格式分析
简介
为了增加反编译难度,保护应用安全,现在越来越多的应用把一些重要的逻辑,加密,重要信息用so库存储。如果想反编译so,那么我们就需要了解so文件格式。这样有助于帮助我分析so文件内容和加固so,防止自己的so被别人轻易破解。
so文件使用的是elf格式来存在信息,所以,我们需要了解elf文件格式。这样就可以读取到我们需要的数据。
说明
这篇文章不会把基本的东西讲太多,网上关于so文件格式也有讲解,本文会把自己分析过程遇到的问题,网上查不到,或者你们在学习过程可能会产生的疑问一一列举出来。
对于基础的东西请自己百度或者看下面给出的ELF文件格式文档。
概述
ELF文件由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。实际上,一个文件中不一定包含全部内容,而且它们的位置也未必如同所示这样安排,只有ELF头的位置是固定的,其余各部分的位置、大小等信息由ELF头中的各项值来决定。
准备(mac)
-
010editor
二进制文件查看工具
https://www.sweetscape.com/010editor/ -
ELF文件格式文档
链接:https://pan.baidu.com/s/1GgUqFF1dJ9sbCoSU3XzElw 密码:zmac -
ELF Header解析java库
ReLinker
该库对导入so文件进行了优化,可以使用该库代替系统的so加载方法。 -
ELF文件格式定义
打开Android系统源码,定位到/Users/dao/pds/Android/android-8.1.0_r1/art/runtime/elf.h
该文件,便是ELF在底层的定义。
readelf
在Linux用该命令可以直接查看so相关信息,mac可以百度,查看替代方案。
- 查看so文件头部信息
readelf -h lib.so
- 查看so文件的段(Section)头的信息
readelf -S libTest.so
- 查看so文件的程序段头信息(Program)
readelf -l libTest.so
- 查看so文件的全部内容
readelf -a libTest.so
分析
so文件二进制数据图

接下来就以ELF头来做分析
看一下ELF头在elf.h中的定义,如果没有源码,可以参考上面的ELF文件格式文档。

e_ident[EI_NIDENT]这16个字节代表数据如下:

- 前四个字节(EI_MAG0 - EI_MAG3)代表ELF文件的魔数:�7F 45 4C 46。
- 第5个字节EI_CLASS:是32位还是64位程序 ,01是32,02是64。
- 第6字节EI_DATA:数据编码,即大小端,01 高位在前 02 低位在前。
- 第7字节EI_VERSION:文件版本,固定值01
- 第8字节EI_OSABI:OS / ABI标识。
- 第9字节EI_ABIVERSION:
- 第10到16字节EI_PAD: 一堆全是00的用来补全大小的数组。
EI_NIDENT :e_ident数组的大小。
剩余字节,可对照数据结构查看,我们用命令看一下ELF头信息

从ELF信息里面可以拿到:
- e_entry 可执行程序入口点地址。
- e_phoff Program Header Offset,程序头部表索引地址,没有则为0。
- e.shoff Section Header Offset,节区表索引地址,没有则为0。
- e_ehsize ELF_Header Size,嗯..ELF头部的大小
- e_phentsize 程序头部表的单个表项的大小
- e_phnum 程序头部表的表项数
- e_shentsize 节区表的单个表项的大小
- e_shnum 节区表的表项数
- e_shstrndx String Table Index,字符串表索引,后面讲解,这里再怎么描述都是懵逼。
这些字段意思可以看源码注释,可以查看上面给出的文档。
通过e_phoff和e_phnum,e_phentsize可以找到程序头部表的在文件中的起始地址和个数以及单个程序头部表的大小。
同理,可以找到节点表的起始地址和个数以及单个表的大小。
010editor工具左边显示了二进制文件的地址,很方便查看。
-
我们来节区表(Section)
该表的数据结构可以字节查看文档和源码,根据程序段头表信息,可以找到起始地址。
截屏2020-02-08下午10.49.26.png
通过命令看看具体数据

第一列就是Section表索引,还记得上面的e_shstrndx字段吧,它表示节区字符串表在节区头部表格中索引。由上面的数据图可以看出e_shstrndx是0x25,即37,对于图中的.shstrtab这个正是节区字符串表的名字,具体这些名字代表什么,请查看文档。
从二进制数据看到只是一个数字,并不是像.shstrtab的字符串,那么又是怎么回事呢?
在Section表数据结构中的一个字段是:sh_name 表示节区名称,表示在节区字符串表中的索引,即信息很可能存储在.shstrtab这个字符串表中。根据上面图中的Off得到起始地址。

Section表数据结构索引为1的sh_name值为3A,即58。而节区字符串表的起始地址是E9F4a,即图中为红色00的位置,然后从这个位置往后数58个字节,刚好是图中蓝色部分的数据,是不是Section节区表格中的第二行的名字(图中少了nt字符,我怀疑是长度过长,没有显示出来,经验证短一点的,完全一只)。
知道了sh_name起始地址,那怎么知道到哪里结束呢,sh_name必须以空字符结束,从起始地址一直找到为空字符的地方即可。
结束
更多请参考文档: 链接:https://pan.baidu.com/s/1GgUqFF1dJ9sbCoSU3XzElw 密码:zmac