从害怕到真香警告——Android代码混淆大法真是好!

本文深入探讨了Android代码混淆的原理和实践,从混淆的日常使用,如混淆前后APK的对比和Crash日志定位,到混淆规则的详解,包括保持不被混淆的设置和资源压缩。此外,文章还介绍了ProGuard、D8、R8在混淆过程中的角色,以及如何在项目中选择使用ProGuard或R8。最后,讨论了自定义混淆字典和模块化混淆,提供视频学习资源帮助开发者更好地掌握混淆技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章较长,有“阅读障碍”的同学可以直接滑至文末有视频学习通道(狗头保命)!

一、引言

Android代码混淆,老生常谈了,不过大部分Android仔对它的认识可能还处于这样的阶段(比如:写这篇文章前的我):

  • 1、日常开发Debug包时不用混淆,正式发布Release包前开启代码混淆;
  • 2、混淆好处① → 类、方法、变量名变成短且无意义的名字,提高反编译后代码的阅读成本;
  • 3、混淆好处② → 删除无用的类、方法与属性,缩减了APK包的大小;
  • 4、混淆好处③ → 对字节码进行优化,移除无用指令,应用运行更快;
  • 5、怎么混淆 → 主项目的 build.gradle 设置 minifyEnabled trueproguard-rules.pro 加入混淆规则;
  • 6、混淆规则哪里来 → 网上搜索通用混淆模板复制粘贴,项目依赖到的第三方库官方文档复制粘贴;

大都止步于此,好一点的还知道下 ProGuard 听过 R8,了解 混淆配置语法,会 自定义混淆规则

会上面这些,日常开发已经 很够用了,但是现在IT行业这么 “卷”,面试时,面试官问下:

混淆具体做了啥?有看过混淆源码吗?说下底层原理…

也说得过去吧 (手动狗头保命~)

所以本节稍微深入点探索下Android中的代码混淆~


二、日常使用

Tips:照惯例,写下简单例子演示日常使用,走走过场,只对混淆原理感兴趣的可以跳过这Part~

1. 混淆前后的APK对比

新建项目,引下Kotlin相关依赖,协程等 (app层级的build.gradle):

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.0'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7'
}

新建MainActivity.kt,请求URL,加载内容:

class MainActivity : AppCompatActivity(), CoroutineScope by MainScope()  {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        et_url.setText("https://www.baidu.com")
        bt_load.setOnClickListener {
            launch {
                tv_content.text = "开始加载请求..."
                tv_content.text = "加载完毕,网页内容如下:\n\n\n ${loadUrl(et_url.text.toString())}"
            }
        }
    }

    private suspend fun loadUrl(url: String) = withContext(Dispatchers.IO) {
        var content = ""
        (URL(url).openConnection() as HttpURLConnection).apply {
            requestMethod = "GET"
            content = dealResponse(inputStream)
            disconnect()
        }
        return@withContext content
    }

    private fun dealResponse(inputStream: InputStream): String {
        val reader = BufferedReader(InputStreamReader(inputStream))
        return StringBuffer().apply {
            var str = reader.readLine()
            while (null != str) {
                append(str)
                str = reader.readLine()
            }
        }.toString()
    }

    override fun onDestroy() {
        super.onDestroy()
        cancel()
    }
}

运行下康康

app层级的build.gradle加下release的签名和编译配置:

signingConfigs {
    release {
        storeFile file('test.jks')
        storePassword '123456'
        keyAlias 'test'
        keyPassword '123456'
    }
}

buildTypes {
    release {
        // 启用代码压缩、优化及混淆
        minifyEnabled true
        // 启用资源压缩,需配合 minifyEnabled=true 使用
        shrinkResources true
        // 指定混淆保留规则
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        // 包签名
        signingConfig signingConfigs.release
    }
}

执行 gradle assemble 打下包,静待打包完毕,先康康Debug包:

23333,裸奔,把APK直接拖到反编译工具 jadx-gui 里,看代码无压力:

再康康Release包:

体积着实少了一些,而且变量名都变成了abcd,顺带提下这个**Load Proguard mappings**,点击加载混淆文件(mapping.txt)后可以去掉代码混淆:

同样拖到jadx-gui里康康:

可读性明显降低~


2. 混淆后App Crash日志定位问题

不知道你有没有想过:混淆后的APK如果报错,日志信息会是怎样的呢

改下代码验证下,直接在点击处抛出一个空指针异常试试康:

单凭这里的b.b.a.a.onClick(Unknow Srouce:2),似乎很难直接定位到错误代码的真实位置。

一种低效的解决方法:自行对照混淆后生成的 mapping.txt 文件,比如直接搜上面的b.b.a.a:

顺着往下看不难发现问题所在,但日常开发不建议用此法,这里好找只是因为示例代码简单,推荐另一种方法:

去混淆,如果你的应用有发布到Google Play的话,可以照着官方文档走:

对崩溃堆栈轨迹进行去混淆处理或符号化解析

没有上传到Googl

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值