每次都要手动使用工具去手动加固,非常麻烦,所以自己搞一个加固插件来提高生产力

开发环境使用的AGP7.0.2 ,相比较之前的版本,改动还是蛮大的,自己也踩了不少坑。

我们分为以下几步去完成:

  1. 获取apk产物
  2. 获取签名
  3. 获取加固工具
  4. 进行加固

获取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() 注册回调

  1. finalizeDsl:您可以通过此回调在 DSL 对象因组件(变体)创建而被锁定之前对其进行更改。VariantBuilder 对象是基于 DSL 对象中包含的数据创建的。
  2. beforeVariants此回调可通过 VariantBuilder影响系统会创建哪些组件以及所创建组件的部分属性。它还支持对 build 流程和生成的工件进行修改。
  3. 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 有so加固功能 安卓提高应用加固插件_android

多个实现类我们并不知道找哪个,接着往上一层看,下面的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中可以动态配置 ,这里也是参考官方的方案:

https://github.com/gradle/kotlin-dsl-samples/blob/3c977388f7/samples/project-with-buildSrc/build.gradle.kts

先创建一个实体类,同样是要抽象类,需要的是工具地址,用户名和密码

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中看到加固插件已经存在了

Android 有so加固功能 安卓提高应用加固插件_gradle_02

最后看下效果:

Android 有so加固功能 安卓提高应用加固插件_android_03

其他

除此之外,如果想在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