现在越来越多的安卓 app 为了保证软件的安全性,会使用 NDK 来进行 Native 开发,最后会产生一个 .so 这样的 ELF 文件,通过动态加载这个 ELF 文件调用隐藏在 .so 文件里的方法。
不过有的时候经过处理的 ELF 文件直接拖入 IDA 会报错,像下面这样,如果强行在 IDA 里查看的话会丧失很多有用信息,比较麻烦。
可以看到 SHT 节头表起始为空节里没有数据,继续往下看第一个节。
0x000211b4 : 000b 0000 000b 0000 0002 0000 0114 0000
s_name_off |s_type |s_flags |s_addr
0x000211c4 : 0114 0000 0740 0000 0002 0000 0001 0000
s_offset | s_size |s_link |s_info
0x000311d4 : 0004 0000 0010 0000
s_addralign |s_entsize
对照下图查看 Section Header Table 的结构也比较清楚了,
不过有的时候经过处理的 ELF 文件直接拖入 IDA 会报错,像下面这样,如果强行在 IDA 里查看的话会丧失很多有用信息,比较麻烦。
可以看 IDA 载入 ELF 的时候报了两个错, 1. SHT 入口位置不对,2. SHT 表大小或者偏移量不对。归纳来说就是 IDA 加载 ELF 的 SHT( Section Header Table) 内容出了错误。不过为什么出了这样的错误对安卓程序来说没有影响呢?
这是因为 Java 加载 .so 这样的 ELF 文件时不参照 Section Header Table 里的内容,所以开发者可以索性直接全部删除这个片段的内容,加大分析难度。这个时候如果需要分析的话最好可以先修复 ELF 文件结构。
ELF Structure
ELF 的主要有下面三个索引表片段。
1. ELF Header : 在 ELF 文件的最开始,保存了文件的路线图,描述该文件的组织情况。
2. Program Header Table : 告诉系统如何创建进程映像。用来构造进程映像的目标文件必须具有程序头部表,可重定位文件(e_type = 1)不需要这个表。
3. Section Header Table : 包含了描述文件节区的信息,每个节区在表中都有一项,每一项给出诸如节区名称、节区大小这类信息。用于链接的目标文件必须包含节区头部表,其他目标文件可以有,也可以没有这个表。
下面简单介绍一下各个索引表片段的内容,使用工具有 readelf(OS X - greadelf)和 xxd。
-h 选项代表显示 ELF Header 的内容,从这里可以看到 Size of this header: 52 (bytes),即 ELF Header 的长度为 52 = 0x34,使用 xxd 查看 ELF 前 52 bytes 的内容。
这里对照上图查看:
0x00000000: 457f 464c 01 01 0001 0000 0000 0000 0000
.ELF |32 |LE|Version |Padding
0x00000010: 0003 0028 0001 0000 0000 0000 0034 0000
e_type |e_machine |e_version |e_entry |e_phoff
0x00000020: 118c 0002 0000 0500 0034 0020 0007 0028
e_shoff |e_flags |e_ehsize |e_phentsize |e_phnum |e_shentsize
0x00000030: 0016 0015
e_shnum |e_shstrndx
具体含义不一一说明,可以看到整个 ELF 的结构在 ELF Header 就比较清楚了,这里跳过 Program Header Table 不表,因为我比较关注的是 Section Header Table。
通过 ELF Header 里 e_shoff 可以看到 SHT 的偏移量为 0x02118c,所以这里直接往 0x2118c 看。
可以看到 SHT 节头表起始为空节里没有数据,继续往下看第一个节。
0x000211b4 : 000b 0000 000b 0000 0002 0000 0114 0000
s_name_off |s_type |s_flags |s_addr
0x000211c4 : 0114 0000 0740 0000 0002 0000 0001 0000
s_offset | s_size |s_link |s_info
0x000311d4 : 0004 0000 0010 0000
s_addralign |s_entsize
对照下图查看 Section Header Table 的结构也比较清楚了,
ELF Fix
因为 SHT 在安卓引用 .so 文件时无关紧要,所以有两种保护情况:
1. 将 ELF Header 里和 SHT 有关的内容修改成错误的内容,但是不动 SHT。
2. 直接将 ELF 中的 SHT 删除。
第一种情况因为保留了正确的 SHT 所以只需要修复 ELF Header 就行,这时有下面几个需要注意的点:
1. e_shentsize = 0x28 = 40
2. e_shnum = (total_size - e_shoff) / e_shensize
3. e_shstrndx = e_shnum - 1
如果上述无法修复,那么就需要和 SHT 被删除的情况一样处理,可以使用附件中给出的一个小工具,此时修复原理比较复杂,可以在参考链接中查看具体内容。
* 附件 Github note-7。
参考链接:
2. ELF 文件格式分析