补齐Android技能树 - 从害怕到玩转Android代码混淆(下)

简介: 本节稍微深入点探索下Android中的代码混淆~

0x6、R8又干了啥?


不知道,细心你的有没有发现,D8竟在r8的包里:


网络异常,图片无法展示
|


接着在 TaskManager.java 搜下D8,经过各种跳转,来到源头:createPostCompilationTasks(),可以看到在创建D8相关的Transform前还做了一些其他的操作~


网络异常,图片无法展示
|


① R8 - 执行前


从注释不难看出,这些Tasks就是用来把.class转dex文件的,还加了一些可选步骤如混淆、jacoco(代码覆盖率工具),还创建了一个 TransformManager 实例,用来管理各种TransformManager。继续:


网络异常,图片无法展示
|


这里引起了我的好奇,可能创建脱糖Task?脱糖不是在D8里进行的吗?跟一下代码:


网络异常,图片无法展示
|


网络异常,图片无法展示
|


这里的DESUGAR,猜测是旧版本的D8兼容,AS 3.0引入的,而现在默认是D8,所以这里其实不会创建脱糖Task。继续:


网络异常,图片无法展示
|


获取外部扩展,合并Java资源,对合并算法感兴趣的可以点进去 MergeJavaResourcesTransform.transform() 看下,这里不展开讲~


网络异常,图片无法展示
|


继续往下走:


网络异常,图片无法展示
|


② R8 - Java代码压缩


再往下走,就碰到关键词R8了:


网络异常,图片无法展示
|


这里有下述三个maybe:


  • maybeCreateJavaCodeShrinkerTransform → Java代码压缩
  • maybeCreateResourcesShrinkerTransform → 资源压缩
  • maybeCreateDexSplitterTransform → dex分割


先看第一个:


网络异常,图片无法展示
|


网络异常,图片无法展示
|


这里区分PROGUARD和R8,创建不同的混淆TransformTask,关注 createR8Transform() 核心代码如下:


// 前面获取dex的文件列表、混淆列表等,初始化R8Transform实例时传入
R8Transform transform =
        new R8Transform(
                variantScope,
                userMainDexListFiles,
                userMainDexListProguardRules,
                inputProguardMapping,
                variantScope.getOutputProguardMappingFile());
// 处理混淆规则,callback用于在混淆后执行后续操作
return applyProguardRules(
        variantScope,
        inputProguardMapping,
        variantScope.getOutputProguardMappingFile(),
        testedVariantData,
        transform,
        callback);


跟下 applyProguardRules(),关注下述核心代码(前面的是和测试相关的):


// This is a "normal" variant in an app/library.
applyProguardConfigForNonTest(transform, variantScope);


跟下此方法:


网络异常,图片无法展示
|


补充其他混淆规则的来源,如AAPT生成的混淆文件,判断如果是AAR的话,直接keep。


网络异常,图片无法展示
|


跟下 R8Transform.transform(),又是参数,最后调用下述方法:


网络异常,图片无法展示
|


最后在 r8Tool.kt 中定位到了此方法,核心代码如下:


// 初始化了一个r8CommandBuilder实例
val r8CommandBuilder = CompatProguardCommandBuilder(!useFullR8, D8DiagnosticsHandler(messageReceiver))
// 然后调用一系列方法,如混淆相关
addMainDexRules()
setMainDexListConsumer()
addProguardConfigurationFiles()
addProguardConfiguration()
setProguardMapOutputPath()
// 配置相关:是否禁用缩小、摇树、脱糖、编译模式
setDisableMinification(toolConfig.disableMinification)
setDisableTreeShaking(toolConfig.disableTreeShaking)
setDisableDesugaring(toolConfig.disableDesugaring)
setMode(compilationMode)
setProgramConsumer(programConsumer)
...
// 初始化r8ProgramResourceProvider实例,用来给R8提供所有资源
val r8ProgramResourceProvider = R8ProgramResourceProvider()
// 各种传参设置
// 最后调用R8.run()
ClassFileProviderFactory(libraries).use { libClasspath ->
    r8CommandBuilder.addLibraryResourceProvider(libClasspath.orderedProvider)
    R8.run(r8CommandBuilder.build())
}


具体的逻辑,可以追溯到 R8.class → run(),做了这些事:


  • 代码删除:通过语法树静态分析技术、发现并删除未使用的代码,如未实例化的Class等;
  • 代码优化:对运行时代码进行优化,删除死代码、未使用的参数,选择性内联、类合并等;
  • 代码混淆:优化标识符名字,减少代码量,会判断混淆规则中是否允许修改标识符名字;
  • 行号重新映射 等。


③ R8 - 资源压缩


接着跟下第二个 maybeCreateResourcesShrinkerTransform()


网络异常,图片无法展示
|


跟下 ShrinkResourcesTransform.transform()


网络异常,图片无法展示
|


跟下 SplitterRunnable.run(),看下具体是怎么压缩资源的,核心代码如下:


// ① 创建资源优化记录文件
File reportFile = null;
if (params.mappingFile != null) {
    File logDir = params.mappingFile.getParentFile();
    if (logDir != null) {
        reportFile = new File(logDir, "resources.txt");
    }
}
// ② 分析资源及使用情况
analyzer.analyze();
// ③ 重写.ap_文件(上面AAPT2生成的),去掉没有用到的资源,实际上是没有删除本地资源的!
analyzer.rewriteResourceZip(params.uncompressedResourceFile, params.compressedResourceFile);
// ③ 导出统计数据


解压AAPT生成的.ap_文件,然后判断是是否为未使用资源,是的话移除,感兴趣可以跟下 rewriteResourceZip()


④ R8 - 调D8拆Dex


只有使用了 Multidex 才会走这里,跟下代码:


网络异常,图片无法展示
|


跟下 DexSplitterTransform.transform()


网络异常,图片无法展示
|


跟下 DexSplitterTool.Builder


网络异常,图片无法展示
|


跟下 DexSplitter.run()


网络异常,图片无法展示
|


跟下 DexSplitterHelper.run()


网络异常,图片无法展示
|


所以最后还是用的D8打Dex,总算把大概的过程过完了~


0x7、自定义混淆字典


之前在反编译人家的APP时看到标识符竟然不是abcd,而是中文和特殊字符,怎么做到的呢?其实不难,自定义一个混淆字典就好,在app的proguard-rules的同级目录创建一个文件,比如 dictionary,内容示例如下:


×
÷
...太长省略


接着在 proguard-rules 添加下述配置:


-obfuscationdictionary ./dictionary
-classobfuscationdictionary ./dictionary
-packageobfuscationdictionary ./dictionary


再接着 gradle assemble 打下包,用jadx打开生成的Release APK康康:


网络异常,图片无法展示
|


0x8、模块化混淆


混淆的开启由app模块控制,与子模块无关


建议 在app模块设置公共混淆规则,子模块设置专属混淆规则,子模块区分project和aar:


# Project类型,配置方法同app模块
buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}
# AAR类型
android {
    defaultConfig {
        consumerProguardFiles 'lib-proguard-rules.txt'
    }
    ...
}


当然,你想让混淆规则都由app模块控制也是可以的,移除模块时记得删掉对应的混淆就好~


小结


(gàn),看源码到吐血,Gradle相关的东西真的是无底洞啊,学完一个又一个,还有著名的ASM字节码插桩,基于APK构建过程Task的HOOK的各种开源性能优化/检测工具等。


参考文献:



相关文章
|
1月前
|
Ubuntu 网络协议 Java
【Android平板编程】远程Ubuntu服务器code-server编程写代码
【Android平板编程】远程Ubuntu服务器code-server编程写代码
|
3月前
|
人工智能 IDE 开发工具
Studio Bot - 让 AI 帮我写 Android 代码
Studio Bot - 让 AI 帮我写 Android 代码
161 1
|
9月前
|
IDE 数据可视化 Java
Android自动生成代码,可视化脚手架之基础信息配置
今天的内容比较简单,大致过一下Electron一些基本用法,虽然说这些比较简单,但又是不得不去了解的,正如做Android的我们,也不是一上来就会的,需要一个循序渐进的过程,下一章,我们再去实际的开发功能。
135 0
|
24天前
|
Android开发
Android代码混淆
Android代码混淆
19 0
|
2月前
|
Ubuntu 网络协议 Linux
【Linux】Android平板上远程连接Ubuntu服务器code-server进行代码开发
【Linux】Android平板上远程连接Ubuntu服务器code-server进行代码开发
57 0
|
3月前
|
安全 算法 JavaScript
安卓逆向 -- 关键代码定位与分析技术
安卓逆向 -- 关键代码定位与分析技术
43 0
|
3月前
|
安全 Android开发 数据安全/隐私保护
代码安全之代码混淆及加固(Android)
代码安全之代码混淆及加固(Android)
42 0
|
4月前
|
安全 Java Android开发
Android App开发之安全加固中反编译、代码混淆、第三方加固以及重签名的讲解及实战(图文解释 简单易懂)
Android App开发之安全加固中反编译、代码混淆、第三方加固以及重签名的讲解及实战(图文解释 简单易懂)
74 0
|
6月前
|
XML Java Android开发
Android 解决使用CocosCreator开发产品上架应用市场代码重复问题
Android 解决使用CocosCreator开发产品上架应用市场代码重复问题
278 0
|
7月前
|
JSON dexposed Java
一文总结 Android 隐私合规代码思路
一文总结 Android 隐私合规代码思路