为了防止安卓应用程序被恶意破解,植入黑客病毒或修改代码用于商业竞争等,对应用程序进行加固必不可少。接下來,本篇文章会主要讲加固的过程以及一些注意事项。html
首先,了解一下何为加固,加固的原理是怎样的,这有利于后面分析问题。java
简单来讲,加固就是对源Apk进行加密,而后再套上一层壳。用加密算法对源Apk进行加密,再将壳Apk进行合并获得新的Dex文件,最后替换壳程序中的dex文件获得新的Apk,这个新的Apk已经不是一个完整意义上的Apk程序了,它的主要工做是负责解密源Apk,而后加载Apk,让其正常运行起来。git
目前,各大互联网公司都会本身的应用程序进行加固保护,像360公司,腾讯都有对外开放本身的服务。另外,市场上还有一些专门加固的产品,好比爱加密和梆梆加固等。好好利用这些“轮子”,专一于业务开发,來提升工做效率。程序员
加固工具的选择:这次使用的是360加固github
第一,从调研加固结果可见,360加固在兼容性、启动速度、体积变化上都占有优点,总体上加固效果比较好;算法
第二,360公司是一家安全起家的公司,在业界的影响力也很大,加固技术仍是值得信赖的!shell
第三,看了不少加固工具的官网,加固的过程都是上传签名的APK包到官网页面或使用相应的桌面程序进行上传,这个过程须要人工进行上传,而360加固提供了一个加固工具包,咱们能够编写脚本來调用其中的加固程序进行自动化加固。如今不少公司都是用Jenkins线上自动化打包,加固也是打包过程的一部分,最好也能是自动化的,这样整个打包流程是“一条龙”,没有人工干预,至关于在黑匣子中进行,程序员不用关心打包过程,也减小人工成本和出错概率。windows
使用gradle脚本实现自动化加固和多渠道打包安全
整个过程分红三个步骤:加固——重签名——多渠道打包bash
加固过程: 浏览了360加固官网,整个加固过程其实很简单,主要有如下的三个步骤:
1)输入360加固平台的账号、密码
2)将签名文件上传到加固平台
3)上传须要加固的apk文件进行加固
关键加固命令行代码以下:
commandLine "{命令执行符号}", "-c" ,"java -jar {加固jar包的位置} -login {360加固平台账号} {360加固平台密码}"
commandLine "{命令执行符号}", "-c" ,"java -jar {加固jar包的位置} -importsign {签名文件的位置} {签名文件存储的密码} {alias别名} {alias密码}"
commandLine "{命令执行符号}", "-c" ,"java -jar {加固jar包的位置} -jiagu {所要加固的apk文件路径} {加固后的apk输出路径} -autosign"
复制代码
说明:
1)系统环境不一样,命令执行符号也会不一样(Linux系统:sh ;Mac系统:bash ;windows系统:powershell);
2)第二行上传签名文件信息是非必要的,加固平台加固后能够进行自动从新签名,而自动签名所须要的信息正是以前上传的签名信息。为了保证签名文件的保密性和安全性,不对第三方加固平台公开,那么不能执行第二行代码便可,由于加固时将原签名抹除,而第三方此时没办法获取到咱们的签名信息,因此加固后须要咱们本地从新签名,下文将会介绍对加固包重签名;
3)当选择本地加固时,第三行代码不须要加上参数-autosign,由于加固平台没办法获取到签名信息进行加固;
4)更多有关加固的命令行,请参考360官网.官网介绍中,还有关于加固后导入渠道信息的功能,这次多渠道打包并无使用该功能,第一,项目中原先使用多渠道打包方式的是美团walle;第二,暂时不知道如何获取到360加固打包后的渠道信息,而该渠道信息会在项目中普遍被使用到,好比数据埋点,渠道统计等。
基于上面的说明和项目的具体状况,整理一下代码,以Linux系统为例:
/** * 360加固 * @param apk 加固的原始apk File * @param outputPath 输出目录 */
def reinforceApk(File apk,outputPath) {
println "--- 360 reinforceApk start! ---"
println "reinforce apk:" + apk
if(apk == null || !apk.exists()) {
throw new FileNotFoundException('apk is not exists and cannot reinforce')
println "---360 reinforceApk throw exception and forced stop!---"
}
exec {
commandLine "sh", "-c", "java -jar ${REINFORCE_JAR} -login ${REINFORCE_NAME} ${REINFORCE_PASSWORD}"
commandLine "sh", "-c", "java -jar ${REINFORCE_JAR} -showsign"
commandLine "sh", "-c", "java -jar ${REINFORCE_JAR} -jiagu ${apk} ${outputPath}"
}
println "--- 360 reinforce end! ---"
}
复制代码
加固工做已经完成差很少了,剩下的工做就是对加固包从新签名
重签名的方法主要是调用AndroidSDK中的build-tools,使用工具包中对齐工具和签名工具完成签名。具体步骤以下:
1)对齐,对Apk文件进行存档对齐优化,确保全部的未压缩数据都从文件的开始位置以指定的对齐方式排列
2)签名,选择Signature V2
commandLine "{命令执行符号}","-c", "{zipalign工具的文件路径} -v -p 4 {已加固的apk文件路径} {对齐后输出的apk文件路径}"
commandLine "{命令执行符号}", "-c", "{apksigner工具的文件路径} sign --ks {签名文件的位置} --ks-key-alias {alias别名} --ks-pass pass:{签名文件存储的密码} --key-pass pass:{alias密码} --out {签名后输出的apk文件} {对齐后输出的apk文件路径}"
复制代码
最后,使用walle美团的多渠道打包工具
平时使用walle多渠道打包,只须要在app/build.gradle下配置插件,指定渠道包的输出路径和渠道配置文件便可,最后在Android studio的Terminal中输入./gradlew assembleReleaseChannels,任务执行完成后在指定的输出路径下生成多个对应的渠道包。具体的流程和细节可参考官方介绍。
这种多渠道打包方式是全自动化构建,很难去干涉到构建流程,不符合咱们的需求:
1)在app/build.gradle配置插件时,在官方介绍中并无找到指定源APK输入路径的方式,估计打包插件默认使用的是app/build/outputs/apk/release下的apk文件,这样就没办法对不一样文件路径下的已加固apk包进行多渠道打包。
2)打包任务设置在assembleRelease以后执行,这个执行依赖封装在插件内部,外部很难修改打包任务依赖于加固任务,在加固任务以后执行。
除了上面的多渠道打包方式以后,walle还提供了另一种多渠道打包方式,用命令行执行walle提供的walle-cli-all.jar执行打包操做,只须要一条打包命令便可完成打包。
commandLine "sh", "-c", "java -jar {walle-cli-all.jar文件路径} batch -f {渠道文件路径} {要加渠道的apk文件路径} {渠道包的输出路径}"
复制代码
walle-cli-all.jar文件下载地址:walle-cli-all.jar
至此,360加固+walle多渠道打包的基本工做完成了!剩下就是构建总体流程和优化代码。
首先,将加固和打包操做封装成自动化操做,利用gradle脚本构建加固任务。为了代码解耦,咱们不在app/build.gradle里面实现加固任务,而是从新建一个gradle文件來实现具体的加固和多渠道打包过程,在app/build.gradle只须要经过apply from: '×××.gradle'
引用这个gradle文件便可,当须要修改加固的一些代码逻辑时,只须要在这个gradle文件里面修改。
引入工具包。根据本身的系统环境,在加固助手网页选择对应的加固助手工具,下载后将里面的jiagu文件夹拷贝到本身项目的根目录下;在walle-cli-jar下载连接下载jar包到本身项目中。
肯定加固任务的时机。加固任务时机应该在release包生成以后,那么加固任务应该依赖于assembleRelease这个任务,而且设置在这个任务以后执行。
接下来就是咱们的基本流程了
1)找到release包,通常在app/build/outputs/apk/release/路径下
2)执行加固命令,将release包路径设置到命令中,并指定加固apk文件的输出路径
3)找到已加固的apk文件,对已加固apk文件进行对齐、重签名。(360已加固的apk文件会在原有的release文件名后面加上"_jiagu")
4)找到从新签名的apk文件,执行多渠道打包命令。(重签名后的文件名是在原有文件名后面加上"_sign")
/** * 360加固 + 美团walle渠道打包 */
task assembleReinforceRelease() {
group '360reinforce'
dependsOn("assembleRelease")
doLast {
cleanFilesPath(CHANNEL_APKS_PATH) //清空上一次生成的渠道包
def releaseApkFile = findApkFile(SOURCE_APK_PATH,"release") //遍历文件,寻找release包
if(releaseApkFile != null) {
reinforceApk(releaseApkFile, DEFAULT_APK_PATH) //执行加固
def reinforceApk = findApkFile(DEFAULT_APK_PATH, "_jiagu") //寻找已加固的apk包
if(reinforceApk != null) {
signApkV2(reinforceApk) //使用V2重签名
def signatureApk = findApkFile(DEFAULT_APK_PATH, "sign")
if(signatureApk != null) {
buildChannelApks(signatureApk,CHANNEL_APKS_PATH) //执行多渠道打包
renameChannelApkFiles(CHANNEL_APKS_PATH) //重命名渠道包
}
}
}
}
}
复制代码
整个流程肯定后,差很少接近尾声了。
代码优化:
1)将流程中每一个步骤封装成一个方法,使代码更加简洁易懂;
2)任务中涉及360加固平台账号密码等敏感信息,能够将这部分信息放到签名信息所在的文件(eg:keystore.properties)中统一管理,而后将这些信息加载到gradle文件中;
3)各类输入输出的文件路径定义为常量,便于修改和管理;
加固方法,重命名和渠道打包的方法相似:
/** * 360加固 * @param apk 加固的原始apk File * @param outputPath 输出目录 */
def reinforceApk(File apk,outputPath) {
println "--- 360 reinforceApk start! ---"
println "reinforce apk:" + apk
if(apk == null || !apk.exists()) {
throw new FileNotFoundException('apk is not exists and cannot reinforce')
println "---360 reinforceApk throw exception and forced stop!---"
}
exec {
commandLine "sh", "-c", "java -jar ${REINFORCE_JAR} -login ${REINFORCE_NAME} ${REINFORCE_PASSWORD}"
commandLine "sh", "-c", "java -jar ${REINFORCE_JAR} -showsign"
commandLine "sh", "-c", "java -jar ${REINFORCE_JAR} -jiagu ${apk} ${outputPath}"
}
println "--- 360 reinforce end! ---"
}
复制代码
任务中涉及到的各类常量,各类密钥名、路径都要根据本身的实际状况修改:
/*加载keystore.properties信息到该gradle文件中*/
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
ext {
/*加固*/
REINFORCE_JAR = "${project.rootDir}/jiagu/jiagu.jar"
REINFORCE_NAME = keystoreProperties['360_NAME'] //360加固帐号
REINFORCE_PASSWORD = keystoreProperties['360_PASSWORD'] //360加固密码
KEY_PATH = keystoreProperties['storeFile'] //密钥路径
KEY_PASSWORD = keystoreProperties['storePassword'] //密钥密码
ALIAS = keystoreProperties['keyAlias'] //密钥别名
ALIAS_PASSWORD = keystoreProperties['keyPassword'] //别名密码
SOURCE_APK_PATH = "${project.buildDir}/bakApk" //源apk文件路径
DEFAULT_APK_PATH = "${project.buildDir}/outputs/apk/release" //默认release文件路径
/*多渠道打包*/
WALLE_JAR = "${project.rootDir}/walle-cli-all.jar"
WALLE_CHANNELS_CONFIG = "../app/channel" //渠道配置文件
CHANNEL_APKS_PATH = "${project.buildDir}/outputs/channels" //渠道Apk输出路径
}
复制代码
1)对比加固前release包的签名和加固后apk的签名是否一致,二者相同说明新apk可以覆盖安装
2)用反编译工具对加固包进行反编译,看可否看到Activity这些类
3)验证是否能够获取到渠道包,代码中获取渠道号是经过WalleChannelReader.getChannel(application);这个方法
。。。
1)网上传闻,360加固后没法获取到walle打包的渠道号?
是的,360加固过程会抹去已签名release包的签名信息,假如在加固前用walle打渠道包就会形成渠道号丢失,因此咱们采用的方法是先加固再多渠道打包,因为加固会破坏掉原有的签名信息,因此加固后须要从新签名。
(新开的公众号,请你们多多支持!)
若是以为对你有帮助,麻烦点个赞,谢谢!同时,欢迎你们评论,互相讨论问题。