安卓进阶_安卓中高级工程师(3/15)之代码混淆

牛客高级系列专栏:

安卓

嵌入式

本人是2020年毕业于广东工业大学研究生:许乔丹,有国内大厂CVTE和世界500强企业安卓开发经验,该专栏整理本人对安卓进阶必备知识点的理解;

网上安卓资料千千万,笔者将继续维护专栏,一杯奶茶价格不止提供答案解析,更有专栏内容免费技术答疑,15大安卓进阶必备知识点包您懂,助您提高安卓进阶技术能力,为您高薪面试保驾护航!

正文开始⬇

1、为什么需要使用代码混淆

安卓应用中的代码可以被反编译、重组和重新打包,这导致了一些安全问题,如:攻击者可能会利用反编译得到的代码,找到应用中的漏洞或密钥,从而做出恶意行为。

代码混淆不是说可以防止他人反编译你的APK,而是可以通过修改变量名、方法名、类名等来改变代码的结构和逻辑,使得反编译后得到的源码难以理解和阅读,增加攻击者分析代码的难度,从而提高应用的安全性。

此外,代码混淆还可以减小应用安装包的大小。当代码被混淆时,重复和无用代码会被标记并在混淆后被删除,从而减小应用的大小。

我们来看看没有代码混淆的情况:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

    }


    private fun importantFun() : Int{
        return 0
    }

    private fun importantFunTwo() : Int{
        return 0
    }
}

我们在MainActivity定义了2个函数,编译APK后,直接在Android Studio双击apk文件,可以看到a apk文件里面有1个dex文件,找到MainActivity的代码,importantFun()和 importantFunTwo()两个方法;

alt

2、怎么使用代码混淆

在app模块里面的build.gradle的minifyEnabled默认为false,现在设置为true:

    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

接着重新编译重新看看apk文件:

alt

  • importantFun()和 importantFunTwo()不见了,这是因为这两个方法被系统识别为无用代码直接优化掉了;
  • 出现简单而无意义的名字,如‘d','h'等,别人看到这种命名,也无法分析出代码的含义;

3、代码混淆配置

上面第2小节里面的:

buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            consumerProguardFiles "consumer-rules.pro"
        }
    }
  • getDefaultProguardFile('proguard-android-optimize.txt') 返回了 Android SDK 中默认的 ProGuard 规则文件路径,这个文件包含优化过的 ProGuard 规则,可以帮助最大化地压缩 APK 文件大小和优化应用程序性能。使用这个默认规则是通常情况下推荐的。我们可以看到代码混淆后类名:MainActivity的命名不会混淆,这就是该文件里配置了不要混淆Activity类的子类。
  • proguard-rules.pro: 是我们自己定义的 ProGuard 规则文件,这个文件包含自定义的混淆规则。我们可以在这里自定义类、方法、字段等的混淆规则。
  • consumer-rules.pro :用于保留特定类和方法,以确保公共接口对消费者可见。

通过这几个文件的组合,可以实现对代码的混淆和最小化,提高应用程序的安全性并减小 APK 文件的大小。

如果希望不混淆importantFun()方法,就可以在proguard-rules.pro文件里添加:

-keepclasseswithmembers class  com.example.myapplication.MainActivity {
    private int importantFun();
}

如下图所示,就可以看到importantFun()方法了。 图片替换文本

以下是一些常见的 ProGuard 配置方法,可以在 proguard-rules.pro 文件中添加这些规则来进行混淆和优化:

  1. 保持特定的类或方法不被混淆:
-keep class com.example.MyClass { *; } #保留 com.example.MyClass 类和其所有成员
-keepclassmembers class com.example.MyClass { *; } #保留 com.example.MyClass 类所有成员
-keepclassmembers enum com.example.MyEnum { *; } #保留 com.example.MyEnum 枚举的所有成员

# 保留枚举类不被混淆
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keepclassmembers interface com.example.MyInterface { *; } #保留 com.example.MyInterface 接口的所有成员
-keep class * extends java.lang.Exception #保持所有继承自 java.lang.Exception 的类不被混淆 
-keep class * implements com.core.IReq #保持所有实现了 com.core.IReq 接口的类不被混淆
  • -keep 指令用于防止类、方法等被混淆掉,指定保留的粒度比较大;
  • -keepclassmembers 指令则用于保留某个类成员(如方法、字段)的名称或特性,并且指定保留的粒度比较小。在实际使用中,可以灵活组合和使用,以达到最佳混淆效果。
  1. 保持特定的代码库不被混淆(如支持库和其他第三方库):
-keep class android.support.v7.** { *; } #保留 android.support.v7 包下的所有类和成员
-keep interface android.support.v7.** { *; } #保留 android.support.v7 包下的所有接口和其所有成员
-keep class com.google.android.gms.** { *; } #保留 com.google.android.gms 包下的所有类和成员
  1. 将某个类标记为入口点,防止被混淆:
# 保留 com.example.MyEntryPointClass 类及其 public static main 方法
-keep public class com.example.MyEntryPointClass { 
    public static void main(java.lang.String[]);
}
  1. 禁止混淆某些类、方法、字段、注解:
-dontobfuscate #禁用代码混淆
-dontwarn kotlin.** #禁用警告,不提示包含 kotlin 的警告信息
-dontwarn com.squareup.okhttp.** #禁用警告,不提示包含 com.squareup.okhttp 的警告信息
-dontwarn org.apache.http.** #禁用警告,不提示包含 org.apache.http 的警告信息
-keepattributes *Annotation*, Signature #保留注解 @ 和签名信息
-keepclasseswithmembers class *{
     public <init>(android.content.Context,android.util.AttributeSet);
     public <init>(android.content.Context,android.util.AttributeSet,int);
}
-keepclassmembers class * {
    @com.example.annotations.MyAnnotation *;
}

# 保留Parcelable序列化类不被混淆
-keep class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
}

# 保留Serializable序列化的类不被混淆
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient <fields>;
    !private <fields>;
    !private <methods>;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

# 保留R下面的资源
-keep class **.R$* {*;}

# 保留本地native方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}

# 保留在Activity中的方法参数是view的方法,
# 这样以来我们在layout中写的onClick就不会被影响
-keepclassmembers class * extends android.app.Activity{
    public void *(android.view.View);
}

#禁止android-X混淆
-keep class com.google.android.material.** {*;}
-keep class androidx.** {*;}
-keep public class * extends androidx.**
-keep interface androidx.** {*;}
-dontwarn com.google.android.material.**
-dontnote com.google.android.material.**
-dontwarn androidx.**

# 保留四大组件,自定义的Application等等这些类不被混淆
# 因为这些子类都有可能被外部调用
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Appliction
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicen

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Android进阶知识体系解析 文章被收录于专栏

#提供免费售后答疑!!花一杯奶茶的钱获得安卓知识体系答疑服务,稳赚不赔# 当你已经掌握了Android基础知识,你可能会想要进一步深入学习安卓进阶知识。在这个专栏中,我们将探讨一些高级安卓开发技术,无论你是初学者还是有经验的开发者,这个专栏都将为你提供有价值的知识和经验。让我们一起开始探索安卓进阶知识的奇妙世界吧!

全部评论

相关推荐

3 7 评论
分享
牛客网
牛客企业服务