以前一些老旧的技术就不介绍了,比如,动态加载、内存不落地加载的方式。
现在的加壳类型分为3种:
-
指令抽取
-
VMP
-
dexc2c
-
https://www.dingxiang-inc.com/blog/post/2
指令抽取
android的指令抽取,主要在于函数基本的抽取保护。通过使用android虚拟机自带的解释器进行执行代码。将原始App中dex文件的函数内容进行清除,并将单独移动到一个加密文件中,在App运行的时候,再将函数内容重新恢复到对应的函数体。
这一指令抽取技术的不足之处在于:
-
使用大量的虚拟机内部结构,会出现兼容性问题;
-
使用android虚拟机进行函数内容的执行,无法对抗自定义虚拟机;
-
它跟虚拟机的JIT优化出现冲突,达不到最佳的性能表现。
VMP
VMP加固的核心原理是基于 Dalvik 的解释器实现自己定义的指令。
比如要对 MainActivity onCreate 进行 VMP 加壳保护, 从实现过程讲要解决两个关键问题:
-
让系统执行 onCreate 时,进入到加固壳代码执行;
-
壳代码得到运行时机后,如何解释原来的 onCreate。
其实现为更改 onCreate 的方法体为下面的方式:
VLibrary.v1 调用就会进入到自己的虚拟机里面去执行。
或者下面的方式:
DEX2C
它与 VMP 表现比较类似,有一个开源项目 DCC 可以看看:
-
https://github.com/amimo/dcc
其加固过的 DEX 反编译如下,与 vmp 差不多:
混合加固
有时候我们反编译一个APP后,发现很多指令都是 nop 指令,那么这就是一个很明显的指令抽取型加壳。进行脱壳之后,发现函数体仍然看不见,是一个 native 的,那么就是用了混合加固。这个时候就需要去分析 so 里面的逻辑了。
壳类型区分
由于 dex2c 与 vmp 在dex里面看起来一样,那么怎么区分呢?
有一个很简单的方法,就是看native函数的注册地址是否相同。由于dex2c是静态注册的,那么它的方法注册地址肯定都是不一样的,而且其方法逻辑也是不一样的。
dex2c 是用 jni 重写了 java 层逻辑,反编译效果看起来如下:
可以看到里面有很多的 jni 调用。如果没有加 ollvm 的话,实际上阅读起来与 smail 差不多。
而对于 vmp 来说,dex 中的 native 方法都是对应的同一个,所以它只有一个注册地址。上面说到 vmp 有两种表现,其中一种表现在 dex 层,就是 dex 层逻辑非常类似,都是通过某个方法调用到 native 层的虚拟机。另外一种,也是差不多的。
所以,看native方法对应的注册地址与方法逻辑是否类似,也可判断是 vmp 函数 dex2c。
关注我的微信公众号:二手的程序员,获取最新文章。