每次都要手动使用工具去手动加固,非常麻烦,所以自己搞一个加固插件来提高生产力
开发环境使用的AGP7.0.2 ,相比较之前的版本,改动还是蛮大的,自己也踩了不少坑。
我们分为以下几步去完成:
- 获取apk产物
- 获取签名
- 获取加固工具
- 进行加固
获取APK
AGP7获取apk 的方式跟以前也大不相同了 ,我们需要借助Variant API apk 来进行获取
Variant API 是 Android Gradle 插件中的扩展机制,可以操纵各种选项,这些选项通常使用可影响 Android build 的 build 配置文件中的 DSL 进行设置。还可以通过 Variant API 访问 build 期间创建的中间工件和最终工件,例如类文件、合并后的清单或 APK/AAB 文件。
这里贴一下官方的示例
https://github.com/android/gradle-recipes/blob/agp-7.0/Kotlin/getApksTest/app/build.gradle.kts
AGP引入了 AndroidComponentsExtension ,可以为 finalizeDsl()
、beforeVariants()
和 onVariants()
注册回调
- finalizeDsl:您可以通过此回调在 DSL 对象因组件(变体)创建而被锁定之前对其进行更改。
VariantBuilder
对象是基于 DSL 对象中包含的数据创建的。- beforeVariants此回调可通过 VariantBuilder影响系统会创建哪些组件以及所创建组件的部分属性。它还支持对 build 流程和生成的工件进行修改。
- onVariants:在此回调中,您可以访问已创建的
Variant
对象,您还可以为它们包含的Property
值设置值或提供程序,以进行延迟计算。
在我们自定义的插件里面 获取到 AndroidComponentsExtension,通过 AndroidComponentsExtension 的onVariant回调注册 Task
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.onVariants {
project.tasks.register<ApkTask>("${it.name}DisplayApks") {
apkFolder.set(it.artifacts.get(SingleArtifact.APK))
builtArtifactsLoader.set(it.artifacts.getBuiltArtifactsLoader())
}
}
接下来看我们自定义的Task , 这些代码 在官方代码中都有实例
abstract class ApkTask : DefaultTask() {
@get:InputFiles
abstract val apkFolder: DirectoryProperty
@get:Internal
abstract val builtArtifactsLoader: Property<BuiltArtifactsLoader>
@TaskAction
fun taskAction() {
val builtArtifacts = builtArtifactsLoader.get().load(apkFolder.get())
?: throw RuntimeException("Cannot load APKs")
builtArtifacts.elements.forEach {
val apk = File(it.outputFile)
}
}
}
task的class 要是abstract 类型的,否则编译会提示final类型的错误。
获取签名
获取签名方面,我并没有在官方实例中找到资料,是在源码中发现了点踪迹。
先看下我们app下 build.gradle.kts中签名的配置
android {
signingConfigs {
create("release") {
storeFile = file("enjoy.keystore")
storePassword = "123456"
keyAlias = "enjoy"
keyPassword = "123456"
}
}
}
signingConfigs 是一个接口类型的,我们看下实现类:
多个实现类我们并不知道找哪个,接着往上一层看,下面的android 方法 的源码
fun org.gradle.api.Project.`android`(configure: Action<com.android.build.gradle.internal.dsl.BaseAppModuleExtension>): Unit =
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("android", configure)
可以看到它使用的是BaseAppModuleExtension,那我们获取到BaseAppModuleExtension 是不是就可以拿到签名了 ,在我尝试了一下果然可以 。
val baseAppModuleExtension = project.extensions.getByType(BaseAppModuleExtension::class.java)
val signingConfig = baseAppModuleExtension.signingConfigs.getByName("release")
这样就拿到了签名
获取加固工具
加固工具,我们是希望可以在app的gradle中可以动态配置 ,这里也是参考官方的方案:
先创建一个实体类,同样是要抽象类,需要的是工具地址,用户名和密码
abstract class JiaguExtension {
abstract val jarPath: Property<String>
abstract val username: Property<String>
abstract val pwd: Property<String>
}
然后在插件中进行获取
val jiaguExtension = project.extensions.create<JiaguExtension>("jiagu")
然后我们需要将配置信息传到task中进行加固任务。
进行加固
最后一步,我们开始进行加固 ,这里先介绍一下加固工具的使用:
-login <username> 首次使用必须先登录 <360用户名>
<password> <登录密码>
-importsign <keystore_path> 导入签名信息 <密钥路径>
<keystore_password> <密钥密码>
<alias> <别名>
<alias_password> <别名密码>
-importmulpkg <mulpkg_filepath> 导入多渠道配置信息,txt格式
-showsign 查看已配置的签名信息
-showmulpkg 查看已配置的多渠道信息
-deletemulpkg 清除已配置的多渠道信息
-help 显示帮助信息
-config 配置加固可选项
----------------------可选增强服务-------------------------------
[-crashlog] 【崩溃日志分析】
[-x86] 【x86支持】
[-analyse] 【加固数据分析】
[-nocert] 【跳过签名校验】
[-piracy] 【盗版监测】
----------------------高级加固选项-------------------------------
[-vmp] 【全VMP保护】
[-data] 【本地数据文件保护】
[-assets] 【资源文件保护】
[-filecheck] 【文件完整性校验】
[-ptrace] 【Ptrace防注入】
[-so] 【SO文件保护】
[-dex2c] 【dex2C保护】
[-string_obfus] 【字符串加密】
[-dex_shadow] 【DexShadow】
[-so_private] 【SO防盗用】
[-double_check] 【双开检测】
-----------------------------------------------------------------
-config_so 配置需要加固的SO文件,以空格分隔
-config_assets 配置需要忽略的资源文件,以空格分隔
-config_so_private 配置防盗用的SO文件,以空格分隔
-showconfig 显示已配置加固项
-version 显示当前版本号
-update 升级到最新版本
-jiagu <inputAPKpath> 加固命令 <APK路径>
<outputPath> <输出路径>
[-autosign] 【自动签名】
[-automulpkg] 【自动多渠道】
[-pkgparam mulpkg_filepath] 【自定义文件生成多渠道】
然后我们通过project提供的commandLine方法执行加固命令
project.exec {
登录
commandLine("java", "-jar", jiagu.jarPath.get(), "-login", jiagu.username.get(), jiagu.pwd.get())
配置签名
commandLine("java", "-jar", jiagu.jarPath.get(), "-importsign", signingConfig.storeFile?.absolutePath,
signingConfig.storePassword, signingConfig.keyAlias, signingConfig.keyPassword)
进行加固
commandLine("java", "-jar", jiagu.jarPath.get(), "-jiagu", apk.absolutePath, apk.parent, "-autosign")
}
至此就完成了插件的加固功能。
使用
最后我们使用 maven-publish 插件推到本地进行引入使用
我们先需要在插件moudle的gradle中进行插件配置
gradlePlugin{
plugins {
register("jiaguPlugin") {
id = "jiagu-plugin"
implementationClass = "jiagu.JiaguPlugin"
}
}
}
这个id 是我们引入的时候所用的id,implementationClass 指向的是我们自定义的Plugin
最后我们发布到自己的本地仓库
group = "jiagu"
version = "1.0"
publishing {
repositories {
maven(url = "../repository")
}
}
然后就是正常使用引入插件:
Project 的build.gradle.kts中:
repositories {
maven { url = uri("./repository/") }
}
classpath("jiagu:jiagu-plugin:1.0")
app 的build.gradle.kts中:
plugins {
id("com.android.application")
id("kotlin-android")
id("jiagu-plugin")
}
jiagu{
jarPath.set("/Users/wyl/Downloads/360jiagubao_mac/jiagu/jiagu.jar")
username.set("182XXXX0810")
pwd.set("xiao")
}
最后可以看到在Gradle中看到加固插件已经存在了
最后看下效果:
其他
除此之外,如果想在assembleDebug或者 assembleDebug 中插入加固task,可以采用这种方案
val apkProducer = project.tasks.register("apk${it.name}Task", ApkTask2::class.java)
it.artifacts.use(apkProducer).wiredWithDirectories(
ApkTask2::inputApk,
ApkTask2::outputApk
).toTransform(SingleArtifact.APK)
abstract class ApkTask2 : DefaultTask() {
@get:InputDirectory
abstract val inputApk: DirectoryProperty
@get:OutputDirectories
abstract val outputApk: DirectoryProperty
@TaskAction
fun taskAction() {
val apks = File(inputApk.get().toString())
apks.listFiles()?.forEach {
if (it.name.endsWith(".apk")) {
val outPutFile = File(outputApk.get().toString() + "/out.apk")
//执行加固任务
}
}
}
}
这样每次执行assembleDebug或者 assembleDebug 任务的时候都会进行apk的加固。
结尾
至此完结撒花。
地址:https://github.com/WngYilei/Jiagu
欢迎大家一起交流分享,提出宝贵意见。
参考:
官方文档:https://github.com/gradle/kotlin-dsl-samples
官方文档:https://github.com/android/gradle-recipes
官方文档:https://developer.android.google.cn/studio/build/extend-agp?hl=zh_cn