Android中实现「类方法指令抽取方式」加固方案原理解析
一、前言
Android中加固方案一直在进步,因为新的加固方案出来就会被人无情的破解脱壳了,从第一代加固方案落地加密dex文件,第二代加固方案不落地加密dex文件,在到第三代加固方案类方法抽取,以后后面的更多加固方案来说都是安全的进步,关于脱壳方案网上有很多资料,但是加固方案却没多少资料,因为有些加固方案是一些加固公司的商业机密不可泄露的,所以我们现在看到的网上加固方案还是以前的加固方案。
二、指令抽取方案
首先不多说,这种方案依赖的技术就是拦截系统的加载类函数,然后进行类方法指令还原。所以这个技术必须了解,好在我们在之前一篇文章中已经详细介绍了如何在native层拦截系统函数实现应用方法指令篡改;本文的技术是依赖于这篇文章,如果这篇文章没看明白,那么本文将很难看懂。一定要先看这篇文章。不多说了,我们在介绍加固方案主要从两方面入手,一方面是如何进行指令抽取,一方面是如何进行指令还原,所以下面先来介绍如何进行类方法的指令抽取功能。
我们还是先用之前的工程作为案例:
在这个类中,我们定义了一个获取密码的方法:
现在为了安全,我们将这个方法信息置空,可以先用010Editor工具查看这个方法的指令信息:
看到这个方法包含了3个指令,那么我们如何将其设置成0呢?这里就需要自己写个工具了,因为我们不能依赖于这个工具手动去置空,那就太不智能了。这个就需要借助之前介绍dex文件格式解析的时候那个源码了。我们需要做一下改动就可以实现这个功能。是个java工程,下载之后直接导入Eclipse即可;我们的目的就是想置空指定方法的的指令数据,那么直接去解析方法指令的那块逻辑即可:
这里在解析代码结构的时候,对类方法和对象方法的代码结构体信息,进行保存,用到的是map结构,而key值就必须用方法的唯一签名信息,这样后面会通过具体的方法签名信息获取到该方法对应的代码结构体信息,然后进行操作:
这里通过解析完dex文件格式之后:在通过指定类的指定方法检索到其代码结构体信息,然后拿到指令个数和指令的偏移地址,构造一个空指令集,覆盖原始指令。最后重新计算文件的checksum和signature值,回写回去。这里最后一步非常重要,如果dex文件的checksum和signature值不正确,dex文件就是失效的。没任何意义了加载也是失败。下面在来详细看一下这两个值的计算方法:
这两个值的计算方法很简单,我们可以从系统源码查看[Android源码目录/dalvik/libdex/DexFile.cpp]中,首先看看checksum的计算方法:
整个dex文件去除头部中的magic和checksum共12个字节,余下的内容计算crc即可。而signature的计算方法:
对整个dex文件,去除头部信息中的magic,checksum,signature共32个字节,余下的内容做sha1计算即可。最终都要回写到文件中的头部信息,我们可以利用010Editor查看:
这样我们就一气呵成,利用自己写的工具对指定方法进行指令置空:先解析原始dex文件格式,保存所有类的所有方法的代码结构体信息,然后通过传入的方法签名信息检索到其代码结构体信息,在获取其指令个数和指令偏移地址,构造出同数量的空指令集,覆盖原始指令实现置空。最后一步非常关键,就是需要重新计算dex文件的checksum和signature信息回写到头部信息中。
下面我们就用上面的CoreDex工程编译出来的dex文件作为案例,借助这个工具,将CoreUtils.getPwd方法指令置空,然后用IDA工具进行查看:
不相信的话,我们在继续用Jadx工具进行查看:
这个方法就是个空方法了,到这里我们抽空方法指令的功能就完成了,下面继续来看如何进行还原指令。
三、指令还原
还原指令就简单了,直接借助之前那个篡改内存指令的代码即可,在native层hook系统函数dexFindClass,然后进行类的方法名过滤,获取指定方法的代码结构体,修改指令内存块为可读属性,然后把指令还原即可:
这里为了简单,就把原始指令保存在native中,然后直接还原了。到这里我们还原指令的逻辑也完成了,下面就运行项目看看日志和效果,先把之前抽取指令的dex文件拷贝到SD卡中,然后用这个工程进行动态加载运行看看效果:
这里看到了,dex中的指令的确被抽取置空了,然后我们也还原成功了,方法执行也有结果了:
四、流程总结
到这里我们就介绍完了本文内容,下面来总结一下流程:
第一、抽取指令流程
1、解析原始dex文件格式,保存所有方法的代码结构体信息。2、通过传入需要置空指令的方法和类名,检索到其代码结构体信息。3、通过方法的代码结构体信息获取指令个数和偏移地址,构造空指令集,然后覆盖原始指令。4、重新计算dex文件的checksum和signature信息,回写到头部信息中。第二、指令还原流程
1、native层hook系统函数dexFindClass,获取类结构体信息。2、获取类中所有的方法信息,通过指定方法名进行过滤,获取该方法的代码结构体信息。3、获取该方法被抽取的指令集,修改方法对应的内存地址为可读属性,直接进行指令还原。在这个过程中,抽取指令需要借助编写的工具,当然工具可以进行深入优化。可以留给你们进行完善。对于还原指令中,如何保存抽取指令并没有介绍,这部分内容可以后续完善优化,比如我们可以将抽取的指令再做一次加密保存到程序中的某个地方,在指令还原的时候去这个文件进行读取解密即可。下面来看看整个流程图:
上一篇: HTML5语音搜索
下一篇: CSO最可能犯下的五大安全失误