加固技术
第一代加固技术——混淆技术;
第二代加固技术——加壳技术;
第三代加固技术——指令抽离;
第四代加固技术——指令转换,VMP;
接下来逐步实现一个自动化的加壳服务。
鉴于软件工程的重要性因此大致按着软工的步骤来,鉴于个人开发步骤有所删减
需求工程
首先需要一个修改apk的方案:
- 使得apk改动幅度尽量小,这样便于自动化实现以及通用性
- 保护覆盖面大,小部分的加密代码可以加密全部的重要资源/代码
- 扩展性强,易于后续维护开发
功能动态补充中:
- 壳dex替换保护
- 自动化dex替换
- …
项目设计
第一点&第二点
因为apk在安装过程中会注册到PMS中,启动时大量的配置也是依据AndroidManifest.xml设置的,因此AndroidManifest.xml文件不能大量更改,lib、Assets、resource等需要时才加载因此可以提前加固。
classes.dex是加密核心,尽量全部隐藏。
因此采用替换app启动时最先加载的Application类来实现加固基础,以下称为壳application与源application
第三点
为了便于调试与增加功能,针对完全替换的新dex做开发,AS下可以方便进行调试与编码,因为先建立的classloader再建立的application,因此可以编写so进行加固。
开发在AS下进行,导出apk。从中提取出dex以及so就是需要植入的解密逻辑了。注意导出的dex中仅有固定名的壳application与系统库,编译时会自动加上无关的东西需要手动smali删除。
之后实现自动化加固工具,注意:
- 修改AndroidManifest.xml里application标签中的android:name为自己的壳application全名,其它地方不要改动。
- 将源application的名字存到某地方,协定到壳application中获取作为加载依据
- 加固工具应当加密源dex,将源dex提取到某处,协定到壳application中解密加载
- 替换全部dex为壳dex,只暴露壳代码
- 壳代码逐步native化,采取不落地加载。
- 源代码逐步native化,采取不落地加载。
- 前期壳代码变动较为频繁,因此先稳定下一个简单的自动加壳工具,不进行加密,只是替换dex与打包,方便测试开发。注意dex先放到应用自己的目录下,要不然总忘记加sd卡的权限。
- 基本的壳native完成之后,开发的重点为源dex的native化,此部分完成代码主要在自动加壳工具中,此时应保障壳代码的稳定
功能实现
功能实现设计在后续文章中
开发流程
暂无,学到啥加啥,不过因为涉及到俩部分比较独立又互有牵制的代码,应该保持一方基本功能稳定的情况下扩展开发。切记先做能提高开发效率的部分。
项目管理
SourceTree+github
备份频率一周一次
地址:https://github.com/imbaya2466/apksec
java壳dex的初步实现
本次先实现壳dex的通用demo
将目标dex置于sd下,修改AndroidManifest.xml中application name与sd权限,其它部分不变
删除全部java代码,新建自己的壳application代码,相当于替换dex,可以借用AS强大的java与so代码调试方便开发。
实际导出壳dex时切记反编译下清理其中无用类,若不清除其按表单自动添加的类会覆盖源dex的实现,之前启动成功但c++与按钮全失效的原因就是默认添加的activity实现覆盖了源的。
源码分析
可知如下启动过程:
ActivityThread是应用的开始,向AMS注册之后进入循环接收返回的应用信息用于启动应用。启动的大部分过程都在handleBindApplication之中。
接下来分析需要的部分:
handleBindApplication之中:
ActivityThread
data.info.makeApplication之中:
LoadedApk
newApplication:
|
|
Application
attach:
ContextWrapper
attachBaseContext:
执行顺序:
欺骗世界
接下来要做的就是欺骗了,欺骗系统使系统认为这是本来的应用,欺骗应用使应用以为这是它第一次进系统。有俩个插入点:attachBaseContext与onCreate其它的调用attach是final的。
针对这俩处最先运行的可控制代码,有俩个可行的方案:
方案一
attachBaseContext中
- 在壳attachBaseContext之中创建新的classloader,因为之前ClassLoader创建时便赋予了LoadedApk中的mClassLoader,因此此处可以获取到作为父。父会加载原lib,因此自己的dexpath与lib可以随意定
- 替换classloader,临时变量cl仅在加载application中使用了一下,因此替换了LoadedApk中的mClassLoader就保证了加载器的正常。
- 替换ApplicationName,将拥有ApplicationName的对象内application名字全部替换,保证之后加载正确
onCreate中:
- 删除mApplication,为了第二次调用make正常
- 删除mAllApplications,抹掉存在,这俩步其实为了恢复make为执行前状态
- 执行makeApplication此时用的cl会自动返回之前替换好的,加载目标的名字也是之前替换好的,ContextImpl是新的,之后正常过程
- 注意,此时make中的过程已经全部替换了,但是make到oncreate之间的步骤还是旧的,因此替换mInitialApplication为新的
- 恢复Providers的操作
- 调用原onCreate
方案二
attachBaseContext中
- 替换cl
- 替换ApplicationName
- 执行makeApplication
onCreate中
将attachBaseContext之后的操作全部替换为新的
编码
对比分析方案1的代码实现较少,意味着bug可能更少,因此实现方案一。
欺骗完成,接下来实现自动化加固
自动化加固
因为易于与java服务器结合且跨平台开发便于个人部署,因此采用java开发。
因为assets资源不会自动解压出来,lib只解压需要的,因此决定开发阶段先暂时实现为:壳dex替换,源dex放于assets中,只改变application name,原名直接修改smali重新打包签名。
因此壳代码一开始就是smali态的,步骤为
- apktool壳,导出smali与so,置于自动化加固要求位置
- 自动化接受源apk,将源dex提出
- 解包源apk,修改application name,替换全部smali为壳smali,修改smali中的application名,导入so,导入assets源dex
- 打包apk,签名。
- 临时实现:壳中从assets中提出dex到data/data/自己/ls下,并作为目标dex路径
注意点:必须为smali替换,再打包,不能直接用压缩工具替换dex。原因未知
使用目录结构:
代码详见github
注意术业有专攻,文件格式解析用c、服务用java、目录等操作就用脚本