通常情况下,Android中的混淆有代码混淆和资源混淆。
代码混淆比较简单,只要打开 minifyEnabled开关,并且需要时指定一些混淆规则就可以了;
相比之下,资源混淆没有那么简单,因为不能简单的修改res文件夹下的资源文件的文件名,因为在开发时每一个资源文件都对应了R文件中的id, 打包后会生成resource.asrc文件,每一个资源文件都在resource.asrc文件中有对应记录。如果要修改资源文件名,对应的也需要修改resource.asrc文件中的记录,这样很麻烦,而且容易出错。如果我们了解了resource.asrc文件的格式,完全可以自己手写这样的操作。
resource.asrc文件的格式如下:
但是今天我要介绍的不是从头开始自己实现上面的原理,而是介绍一个已经实现了上面操作的开源库,它就是AndResGuard.
粘上github地址:
https://github.com/shwenzhang/AndResGuard
过程其实很简单:
1.在project的gradle中添加依赖:
//微信资源混淆工具,注意版本不要太低,太低的话在解析resource.arsc的时候会报错,是因为使用了低版本的appTool
classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.16'
2.在app的gradle中添加配置:
apply plugin: 'AndResGuard'
andResGuard {
// mappingFile = file("./resource_mapping.txt")
mappingFile = null
use7zip = true
useSign = true
// it will keep the origin path of your resources when it's true
keepRoot = false
//白名单,不混淆的资源
whiteList = [
// your icon
"R.drawable.icon",
// for fabric
"R.string.com.crashlytics.*",
// for google-services
"R.string.google_app_id",
"R.string.gcm_defaultSenderId",
"R.string.default_web_client_id",
"R.string.ga_trackingId",
"R.string.firebase_database_url",
"R.string.google_api_key",
"R.string.google_crash_reporting_api_key"
]
compressFilePattern = [
"*.png",
"*.jpg",
"*.jpeg",
"*.webp",
"*.gif",
"resources.arsc"
]
sevenzip {
artifact = 'com.tencent.mm:SevenZip:1.2.10'
//path = "/usr/local/bin/7za"
}
}
AndResGuard优化使用了7zzip工具。
配置好之后在as右侧的gradle选项中会出现andresguard的task:
我们可以选择需要打包的apk类型进行优化;这里直接打包release版本的, 双击resguardRelease
看一下task流程:
Executing tasks: [resguardRelease]
Parallel execution is an incubating feature.
> Configure project :app
normal
useNewCruncher has been deprecated. It will be removed in a future version of the gradle plugin. New cruncher is now always enabled.
WARNING: API 'variant.getJavaCompile()' is obsolete and has been replaced with 'variant.getJavaCompileProvider()'.
It will be removed at the end of 2019.
For more information, see https://d.android.com/r/tools/task-configuration-avoidance.
To determine what is calling variant.getJavaCompile(), use -Pandroid.debug.obsoleteApi=true on the command line to display a stack trace.
> Task :app:preBuild UP-TO-DATE
> Task :app:preBeta_AppReleaseBuild UP-TO-DATE
> Task :app:compileBeta_AppReleaseAidl SKIPPED
> Task :app:compileBeta_AppReleaseRenderscript UP-TO-DATE
> Task :app:checkBeta_AppReleaseManifest UP-TO-DATE
> Task :app:generateBeta_AppReleaseBuildConfig UP-TO-DATE
> Task :app:prepareLintJar UP-TO-DATE
> Task :app:generateBeta_AppReleaseSources UP-TO-DATE
> Task :app:javaPreCompileBeta_AppRelease UP-TO-DATE
> Task :app:mainApkListPersistenceBeta_AppRelease
> Task :app:generateBeta_AppReleaseResValues UP-TO-DATE
> Task :app:generateBeta_AppReleaseResources UP-TO-DATE
> Task :app:mergeBeta_AppReleaseResources UP-TO-DATE
> Task :app:createBeta_AppReleaseCompatibleScreenManifests
> Task :app:processBeta_AppReleaseManifest
> Task :app:processBeta_AppReleaseResources
> Task :app:compileBeta_AppReleaseJavaWithJavac UP-TO-DATE
> Task :app:compileBeta_AppReleaseNdk SKIPPED
> Task :app:compileBeta_AppReleaseSources UP-TO-DATE
> Task :app:mergeBeta_AppReleaseShaders UP-TO-DATE
> Task :app:compileBeta_AppReleaseShaders UP-TO-DATE
> Task :app:generateBeta_AppReleaseAssets UP-TO-DATE
> Task :app:mergeBeta_AppReleaseAssets UP-TO-DATE
> Task :app:validateSigningBeta_AppRelease UP-TO-DATE
> Task :app:signingConfigWriterBeta_AppRelease UP-TO-DATE
> Task :app:transformClassesWithDexBuilderForBeta_AppRelease UP-TO-DATE
> Task :app:transformClassesWithMultidexlistForBeta_AppRelease UP-TO-DATE
> Task :app:transformDexArchiveWithDexMergerForBeta_AppRelease UP-TO-DATE
> Task :app:mergeBeta_AppReleaseJniLibFolders UP-TO-DATE
> Task :app:transformNativeLibsWithMergeJniLibsForBeta_AppRelease UP-TO-DATE
> Task :app:transformNativeLibsWithStripDebugSymbolForBeta_AppRelease UP-TO-DATE
> Task :app:processBeta_AppReleaseJavaRes NO-SOURCE
> Task :app:transformResourcesWithMergeJavaResForBeta_AppRelease UP-TO-DATE
> Task :app:packageBeta_AppRelease
> Task :app:assembleBeta_AppRelease
> Task :app:preRelease_AppReleaseBuild UP-TO-DATE
> Task :app:compileRelease_AppReleaseAidl SKIPPED
> Task :app:compileRelease_AppReleaseRenderscript UP-TO-DATE
> Task :app:checkRelease_AppReleaseManifest UP-TO-DATE
> Task :app:generateRelease_AppReleaseBuildConfig UP-TO-DATE
> Task :app:generateRelease_AppReleaseSources UP-TO-DATE
> Task :app:javaPreCompileRelease_AppRelease UP-TO-DATE
> Task :app:mainApkListPersistenceRelease_AppRelease
> Task :app:generateRelease_AppReleaseResValues UP-TO-DATE
> Task :app:generateRelease_AppReleaseResources UP-TO-DATE
> Task :app:mergeRelease_AppReleaseResources UP-TO-DATE
> Task :app:createRelease_AppReleaseCompatibleScreenManifests
> Task :app:processRelease_AppReleaseManifest
> Task :app:processRelease_AppReleaseResources
> Task :app:compileRelease_AppReleaseJavaWithJavac UP-TO-DATE
> Task :app:compileRelease_AppReleaseNdk SKIPPED
> Task :app:compileRelease_AppReleaseSources UP-TO-DATE
> Task :app:mergeRelease_AppReleaseShaders UP-TO-DATE
> Task :app:compileRelease_AppReleaseShaders UP-TO-DATE
> Task :app:generateRelease_AppReleaseAssets UP-TO-DATE
> Task :app:mergeRelease_AppReleaseAssets UP-TO-DATE
> Task :app:validateSigningRelease_AppRelease UP-TO-DATE
> Task :app:signingConfigWriterRelease_AppRelease UP-TO-DATE
> Task :app:transformClassesWithDexBuilderForRelease_AppRelease UP-TO-DATE
> Task :app:transformClassesWithMultidexlistForRelease_AppRelease UP-TO-DATE
> Task :app:transformDexArchiveWithDexMergerForRelease_AppRelease UP-TO-DATE
> Task :app:mergeRelease_AppReleaseJniLibFolders UP-TO-DATE
> Task :app:transformNativeLibsWithMergeJniLibsForRelease_AppRelease UP-TO-DATE
> Task :app:transformNativeLibsWithStripDebugSymbolForRelease_AppRelease UP-TO-DATE
> Task :app:processRelease_AppReleaseJavaRes NO-SOURCE
> Task :app:transformResourcesWithMergeJavaResForRelease_AppRelease UP-TO-DATE
> Task :app:packageRelease_AppRelease
> Task :app:assembleRelease_AppRelease
> Task :app:preTest_AppReleaseBuild UP-TO-DATE
> Task :app:compileTest_AppReleaseAidl SKIPPED
> Task :app:compileTest_AppReleaseRenderscript UP-TO-DATE
> Task :app:checkTest_AppReleaseManifest UP-TO-DATE
> Task :app:generateTest_AppReleaseBuildConfig UP-TO-DATE
> Task :app:generateTest_AppReleaseSources UP-TO-DATE
> Task :app:javaPreCompileTest_AppRelease UP-TO-DATE
> Task :app:mainApkListPersistenceTest_AppRelease
> Task :app:generateTest_AppReleaseResValues UP-TO-DATE
> Task :app:generateTest_AppReleaseResources UP-TO-DATE
> Task :app:mergeTest_AppReleaseResources UP-TO-DATE
> Task :app:createTest_AppReleaseCompatibleScreenManifests
> Task :app:processTest_AppReleaseManifest
> Task :app:processTest_AppReleaseResources
> Task :app:compileTest_AppReleaseJavaWithJavac UP-TO-DATE
> Task :app:compileTest_AppReleaseNdk SKIPPED
> Task :app:compileTest_AppReleaseSources UP-TO-DATE
> Task :app:mergeTest_AppReleaseShaders UP-TO-DATE
> Task :app:compileTest_AppReleaseShaders UP-TO-DATE
> Task :app:generateTest_AppReleaseAssets UP-TO-DATE
> Task :app:mergeTest_AppReleaseAssets UP-TO-DATE
> Task :app:validateSigningTest_AppRelease UP-TO-DATE
> Task :app:signingConfigWriterTest_AppRelease UP-TO-DATE
> Task :app:transformClassesWithDexBuilderForTest_AppRelease UP-TO-DATE
> Task :app:transformClassesWithMultidexlistForTest_AppRelease UP-TO-DATE
> Task :app:transformDexArchiveWithDexMergerForTest_AppRelease UP-TO-DATE
> Task :app:mergeTest_AppReleaseJniLibFolders UP-TO-DATE
> Task :app:transformNativeLibsWithMergeJniLibsForTest_AppRelease UP-TO-DATE
> Task :app:transformNativeLibsWithStripDebugSymbolForTest_AppRelease UP-TO-DATE
> Task :app:processTest_AppReleaseJavaRes NO-SOURCE
> Task :app:transformResourcesWithMergeJavaResForTest_AppRelease UP-TO-DATE
> Task :app:packageTest_AppRelease
> Task :app:assembleTest_AppRelease
> Task :app:assembleRelease
> Task :app:resguardRelease
看到resguardRelease任务在最后执行,它是依赖于assembleRelease任务的,所以它所做的操作都是基于生成我们原始apk的基础之上的。
等待resguardRelease任务执行完成,可能会等待一段时间(7zip压缩耗时),我们看build文件夹的样子:
andResGuard生成了一个临时目录,在临时目录里保存过程中生成的临时文件,将最终的apk生成到release目录下。
再来看一下临时目录里究竟有什么:
肯定是在做解压原来的apk然后进行一系列的资源混淆,压缩,对齐,签名等操作,里面的后缀为aligned_signed的apk和我们最终的apk是一样的。
查看一下最终的apk里面是什么样子:
看,找不到res文件夹了,有一个r文件夹,应该是它,进去看下:
里面的文件都被混淆了,这里可以感叹一下它的强大。这不仅能减少apk的体积,还能增加保密性,让我们的apk更加难以破解。
再对比一下apk大小, 从57M 压缩到 54.7M 单单是通过混淆res和7zip压缩就收到了这样的效果。因为我使用的图片资源都是经过压缩过的,而且一些so库没有压缩(主要大小来自这里),所以这样的效果已经很显著了。
那么它的过程原理是什么呢? 我们先看一下run的执行流程:
> Task :app:resguardRelease
convertToPatternString typeName drawable format icon
convertToPatternString typeName string format com\.crashlytics\..*
convertToPatternString typeName string format google_app_id
convertToPatternString typeName string format gcm_defaultSenderId
convertToPatternString typeName string format default_web_client_id
convertToPatternString typeName string format ga_trackingId
convertToPatternString typeName string format firebase_database_url
convertToPatternString typeName string format google_api_key
convertToPatternString typeName string format google_crash_reporting_api_key
-->AndResGuard starting! Current thread# id: 149, name: Task worker for ':' Thread 3
unziping apk to D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Test_App\release\AndResGuard_事物链_Test_M.T.1.1.2.13_20190801150220\temp
decoding resources.arsc
parse to get the exist names in the resouces.arsc first
reading packagename com.dayouzc.e2eplatform.thingchain.x5.test
resources mapping file D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Test_App\release\AndResGuard_事物链_Test_M.T.1.1.2.13_20190801150220\resource_mapping_事物链_Test_M.T.1.1.2.13_20190801150220.txt done
resources filter mapping file D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Test_App\release\AndResGuard_事物链_Test_M.T.1.1.2.13_20190801150220\merge_duplicated_res_mapping_事物链_Test_M.T.1.1.2.13_20190801150220.txt done
writing new resources.arsc
resources.arsc Character Encoding: utf-8
[AndResGuard] buildApk signatureType: SchemaV2
General unsigned apk: 事物链_Test_M.T.1.1.2.13_20190801150220_unsigned.apk
add meta file D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Test_App\release\AndResGuard_事物链_Test_M.T.1.1.2.13_20190801150220\temp\META-INF\services
DestResDir 770 rawResDir 770
use 7zip to repackage: 事物链_Test_M.T.1.1.2.13_20190801150220_7zip_unsigned.apk, will cost much more time
[addStoredFileIn7Zip]rewrite the stored file into the 7zip, file count: 4
zipaligning apk: D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Test_App\release\AndResGuard_事物链_Test_M.T.1.1.2.13_20190801150220\事物链_Test_M.T.1.1.2.13_20190801150220_7zip_unsigned.apk, exists:true
signing apk: 事物链_Test_M.T.1.1.2.13_20190801150220_7zip_aligned_signed.apk
Backup Final APk(V2) to D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Test_App\release\事物链_Test_M.T.1.1.2.13_20190801150220.apk
<--AndResGuard Done! You can find the output in D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Test_App\release\AndResGuard_事物链_Test_M.T.1.1.2.13_20190801150220
convertToPatternString typeName drawable format icon
convertToPatternString typeName string format com\.crashlytics\..*
convertToPatternString typeName string format google_app_id
convertToPatternString typeName string format gcm_defaultSenderId
convertToPatternString typeName string format default_web_client_id
convertToPatternString typeName string format ga_trackingId
convertToPatternString typeName string format firebase_database_url
convertToPatternString typeName string format google_api_key
convertToPatternString typeName string format google_crash_reporting_api_key
-->AndResGuard starting! Current thread# id: 149, name: Task worker for ':' Thread 3
unziping apk to D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Beta_App\release\AndResGuard_事物链_Beta_M.D.1.1.1.1_20190801150220\temp
decoding resources.arsc
parse to get the exist names in the resouces.arsc first
reading packagename com.dayouzc.e2eplatform.thingchain.x5.beta
resources mapping file D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Beta_App\release\AndResGuard_事物链_Beta_M.D.1.1.1.1_20190801150220\resource_mapping_事物链_Beta_M.D.1.1.1.1_20190801150220.txt done
resources filter mapping file D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Beta_App\release\AndResGuard_事物链_Beta_M.D.1.1.1.1_20190801150220\merge_duplicated_res_mapping_事物链_Beta_M.D.1.1.1.1_20190801150220.txt done
writing new resources.arsc
resources.arsc Character Encoding: utf-8
[AndResGuard] buildApk signatureType: SchemaV2
General unsigned apk: 事物链_Beta_M.D.1.1.1.1_20190801150220_unsigned.apk
add meta file D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Beta_App\release\AndResGuard_事物链_Beta_M.D.1.1.1.1_20190801150220\temp\META-INF\services
DestResDir 770 rawResDir 770
use 7zip to repackage: 事物链_Beta_M.D.1.1.1.1_20190801150220_7zip_unsigned.apk, will cost much more time
[addStoredFileIn7Zip]rewrite the stored file into the 7zip, file count: 4
zipaligning apk: D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Beta_App\release\AndResGuard_事物链_Beta_M.D.1.1.1.1_20190801150220\事物链_Beta_M.D.1.1.1.1_20190801150220_7zip_unsigned.apk, exists:true
signing apk: 事物链_Beta_M.D.1.1.1.1_20190801150220_7zip_aligned_signed.apk
Backup Final APk(V2) to D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Beta_App\release\事物链_Beta_M.D.1.1.1.1_20190801150220.apk
<--AndResGuard Done! You can find the output in D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Beta_App\release\AndResGuard_事物链_Beta_M.D.1.1.1.1_20190801150220
convertToPatternString typeName drawable format icon
convertToPatternString typeName string format com\.crashlytics\..*
convertToPatternString typeName string format google_app_id
convertToPatternString typeName string format gcm_defaultSenderId
convertToPatternString typeName string format default_web_client_id
convertToPatternString typeName string format ga_trackingId
convertToPatternString typeName string format firebase_database_url
convertToPatternString typeName string format google_api_key
convertToPatternString typeName string format google_crash_reporting_api_key
-->AndResGuard starting! Current thread# id: 149, name: Task worker for ':' Thread 3
unziping apk to D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Release_App\release\AndResGuard_事物链-app-V1.1.2_20190801150220\temp
decoding resources.arsc
parse to get the exist names in the resouces.arsc first
reading packagename com.dayouzc.e2eplatform.thingchain.x5
resources mapping file D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Release_App\release\AndResGuard_事物链-app-V1.1.2_20190801150220\resource_mapping_事物链-app-V1.1.2_20190801150220.txt done
resources filter mapping file D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Release_App\release\AndResGuard_事物链-app-V1.1.2_20190801150220\merge_duplicated_res_mapping_事物链-app-V1.1.2_20190801150220.txt done
writing new resources.arsc
resources.arsc Character Encoding: utf-8
[AndResGuard] buildApk signatureType: SchemaV2
General unsigned apk: 事物链-app-V1.1.2_20190801150220_unsigned.apk
add meta file D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Release_App\release\AndResGuard_事物链-app-V1.1.2_20190801150220\temp\META-INF\services
DestResDir 770 rawResDir 770
use 7zip to repackage: 事物链-app-V1.1.2_20190801150220_7zip_unsigned.apk, will cost much more time
[addStoredFileIn7Zip]rewrite the stored file into the 7zip, file count: 4
zipaligning apk: D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Release_App\release\AndResGuard_事物链-app-V1.1.2_20190801150220\事物链-app-V1.1.2_20190801150220_7zip_unsigned.apk, exists:true
signing apk: 事物链-app-V1.1.2_20190801150220_7zip_aligned_signed.apk
Backup Final APk(V2) to D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Release_App\release\事物链-app-V1.1.2_20190801150220.apk
<--AndResGuard Done! You can find the output in D:\projects\test\x5-e2e-platform-thingchain\x5-e2e-platform-thingchain\app\build\outputs\apk\Release_App\release\AndResGuard_事物链-app-V1.1.2_20190801150220
很晕,而且里面有重复打印,我们整理一下它的执行流程:
1. 将apk解压到temp目录下;
2. 解析resources.arsc文件;
a). 获取resources中的已存在的文件名
b). 获取包名
c). 建立resource文件名映射,这里需要用到上面两步获取的res文件名和包名,映射关系保存进merge_duplicated_res_mapping_事物链_Test_M.T.1.1.2.13_20190801150220.txt
d). 使用映射关系创建新的resource.arsc文件
3. 生成未签名apk;
4. 使用7zip重新打包未签名的apk;
5. 使用zipalign生成对齐未签名的apk
6. 对apk进行签名,并生成签名apk
7. 备份最终的apk,即将签名apk从temp文件夹复制到release文件下,并重命名为自己项目中指定的apk名字。
至此资源混淆完成,如果项目中想使用资源混淆,AndResGuard这个插件还是值得一用的。