原理分析见《利用动态加载技术加固APK原理解析》
源代码地址:https://github.com/mars-ma/ApkSecure/
bin目录下是直接可用的工具,注意readme.txt的说明,需要配置好java运行环境。
config.xml注意这里需要安装WinRAR对APK压缩,需要给出安装好的WinRAR的路径。签名和密码的配置也在这里。
dex/apksecure下是壳dex的源码
java_tool/ApkSecure下是java工具类的源码
加固技术实现(基础)
Java加壳工具的实现
实现Java加壳程序需要壳Classes.dex,以及相关so库。Java加固工具的工作流程如下:
反编译原APK,将AndroidManifest.xml中的Application Name改为壳中的Application Name。
在<application>标签下增加<meta-data>,存入原Application Name,以便在解壳后恢复原始Application。
将反编译的APK再次编译得到新的Apk。
将新的Apk解压,将原Classes.dex(可能是多个)拷贝到assets下,并用加密算法加密为新的文件,再用zip压缩。
用壳classes.dex替换新APK中的classes.dex。
将解压的新的Apk用压缩工具压缩成zip,更改后缀名为.apk,用jarsigner签名。
壳DEX的实现
壳dex负责逆向解密出dex,上述加固思路已经详细说明,通过构造DexClassLoader,并用反射技术替换调默认的ClassLoader。执行流程如下:
将assets下加密的.dex文件拷贝至自己的数据目录,先用java.util.zip解压文件并用解密算法解密出原.dex文件。
以解密出的原.dex为路径,构造DexClassLoader,并用反射技术替换调默认的PathClassLoader。
将ActivityThread中ApplicantsInfo的Class Name替换为存在下中的应用名,并用新的ClassLoader加载原Application替换调现有Application,最后执行原Application对象的onCreate函数。
最初版本遇到的问题:
壳dex中不能包含和原dex相同的全限定类名
java.util.zip压缩的apk无法被系统识别资源,后续使用了zip4j可以正常工作。
JNI反射内部类的签名格式是"a/b/C$D"
压缩后apk明显大于原始apk,因为增加了解密用的so文件。
Android4.4及以下使用HashMap类,以上使用ArrayMap类,反射时要适配不同的API版本。
2017.5.19
支持multidex,但是未做ART虚拟机下DexClassLoader加载.dex时跳过编译.oat的优化,对于使用ART虚拟机,且体积较大的dex存在加载缓慢的问题。
待优化:
绕过ART虚拟机编译OAT文件
防止so的动态调试
防止dump dex