Android逆向之旅---基于对so中的函数加密技术实现so加固

时间:2024-04-12 18:18:04
               


致谢:

感谢看雪论坛的ThomasKing大神,提供牛逼思路,原文地址:http://bbs.pediy.com/thread-191649.htm


一、前言

今天我们继续来介绍so加固方式,在前面一篇文章中我们介绍了对so中指定的段(section)进行加密来实现对so加固

http://blog.****.net/jiangwei0910410003/article/details/49962173

这篇文章我们延续之前的这篇文章来介绍一下如何对函数进行加密来实现加固,当然这篇文章和前篇文章有很多类似的地方,这里就不做太多的解释了,所以还请阅读这篇文章之前先去了解前一篇文章。


二、技术原理

这篇和之前的那篇文章唯一的不同点就是如何找到指定的函数的偏移地址和大小

那么我们先来了解一下so中函数的表现形式:

在so文件中,每个函数的结构描述是存放在.dynsym段中的。每个函数的名称保存在.dynstr段中的,类似于之前说过的每个section的名称都保存在.shstrtab段中,所以在前面的文章中我们找到指定段的时候,就是通过每个段的sh_name字段到.shstrtab中寻找名字即可,而且我们知道.shstrtab这个段在头文件中是有一个index的,就是在所有段列表中的索引值,所以很好定位.shstrtab.

但是在这篇文章我们可能遇到一个问题,就是不能按照这种方式去查找指定函数名了:

Android逆向之旅---基于对so中的函数加密技术实现so加固

可能有的人意识到一个方法,就是我们可以通过section的type来获取.dynsym和.dynstr。我们看到上图中.dynsym类型是:DYNSYM,

.dynstr类型是STRTAB,但是这种方法是不行的,因为这个type不是唯一的,也就说不同的section,type可能相同,我们没办法区分,比如.shstrtab和.dynstr的type都是STRTAB.其实从这里我们就知道这两个段的区别了:

.shstrtab值存储段的名称,.dynstr是存储so中的所有符号名称。

那么我们该怎么办呢?这时候我们再去看一下elf的说明文档:

http://download.****.net/detail/jiangwei0910410003/9204051

我们看到有一个.hash段,在上图中我们也可以看到的:

由 Elf32_Word 对象组成的哈希表支持符号表访问。下面的例子有助于解释哈希表

Android逆向之旅---基于对so中的函数加密技术实现so加固
组织,不过不是规范的一部分。bucket 数组包含 nbucket 个项目,chain 数组包含 nchain 个项目,下标都是从 0 开始。bucket 和 chain 中都保存符号表索引。Chain 表项和符号表存在对应。符号 表项的数目应该和 nchain 相等,所以符号表的索引也可用来选取 chain 表项。哈希 函数能够接受符号名并且返回一个可以用来计算 bucket 的索引。
因此,如果哈希函数针对某个名字返回了数值 X,则 bucket[X%nbucket] 给出了 一个索引 y,该索引可用于符号表,也可用于 chain 表。如果符号表项不是所需要的, 那么 chain[y] 则给出了具有相同哈希值的下一个符号表项。我们可以沿着 chain 链 一直搜索,直到所选中的符号表项包含了所需要的符号,或者 chain 项中包含值 STN_UNDEF。

上面的描述感觉有点复杂,其实说的简单点就是:

用目标函数名在用hash函数得到一个hash值,然后再做一些计算就可以得到这个函数在.dynsym段中这个函数对应的条目了。关于这个hash函数,是公用的,我们在Android中的bonic/linker.c源码中也是可以找到的:

unsigned long elf_hash (const unsigned char *name) {unsigned long h = 0, g; while (*name){ h=(h<<4)+*name++; if (g = h & 0xf0000000) h^=g>>24; h&=-g;}return h; }
那么我们只要得到.hash段即可,但是我们怎么获取到这个section中呢?elf中并没有对这个段进行数据结构的描述,有人可能想到了我们在上图看到.hash段的type是HASH,那么我们再通过这个type来获取?但是之前说了,这个type不是唯一的,通过他来获取section是不靠谱的?那么我们该怎么办呢?这时候我们就要看一下程序头信息了:

Android逆向之旅---基于对so中的函数加密技术实现so加固
我们知道程序头信息是最后so被加载到内存中的映像描述,这里我们看到有一个.dynamic段。我们再看看so文件的装载视图和链接视图:

Android逆向之旅---基于对so中的函数加密技术实现so加固

这个我们在之前也说过,在so被加载到内存之后,就没有section了,对应的是segment了,也就是程序头中描述的结构,而且一个segment可以包含多个section,相同的section可以被包含到不同的segment中。.dynamic段一般用于动态链接的,所以.dynsym和.dynstr,.hash肯定包含在这里。我们可以解析了程序头信息之后,通过type获取到.dynamic程序头信息,然后获取到这个segment的偏移地址和大小,在进行解析成elf32_dyn结构。下面两种图就是程序头的type类型和dyn结构描述,可以在elf.h中找到: