Android 混淆(obfuscation)详细回答
一、基本
1. 什么是代码混淆?
代码混淆是一种技术,通过对源代码进行修改,使得最终生成的代码变得难以理解和逆向,同时尽量不影响应用的正常运行,混淆会修改类名、方法名、变量名等符号,使它们变得没有意义,这样,即使攻击者能够获取到 APK 文件或反编译代码,也很难了解代码的实际逻辑。
2. 为什么需要代码混淆?
保护知识产权:防止他人通过反编译 APK 文件来窃取代码逻辑和商业秘密。
提高安全性:增加逆向工程的难度,使攻击者更难找到漏洞和利用点。
减小 APK 大小:通过去除未使用的代码和资源,可以减小 APK 的大小,提高应用的性能和下载速度。
二、混淆工具
1. ProGuard
ProGuard 是一个独立的代码混淆工具,主要作用是对代码进行优化、缩小和混淆,它通过字节码优化和混淆,减少应用的大小,并混淆掉不需要的代码。
2. R8
R8 是 Google 开发的替代工具,默认集成在 Android Gradle Plugin 中,相比 ProGuard 提供更高效的混淆和优化,R8 是 Android Studio 3.4 版本以后推出的默认工具。
三、混淆配置步骤
1. 启用混淆
在项目的build.gradle
文件中,找到android
块,并在buildTypes
下配置release
类型的minifyEnabled
属性为true
,以启用代码混淆功能。
android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } }
2. 编写混淆规则
在项目的proguard-rules.pro
文件中定义混淆规则,常见的规则包括保留特定的类、方法或字段,以避免它们被混淆或删除。
保留所有公有类和方法 -keep public class { public ; } 忽略特定包的警告 -dontwarn com.example.unusedpackage. 保留带有特定注解的类 -keep @interface com.example.MyAnnotation 保留继承特定父类的所有子类 -keep class extends com.example.BaseClass 保留类名和方法名,但不保护方法的内部实现 -keepclassmembers class com.example.MyClass { public <methods>; }
3. 构建项目
配置完成后,使用以下命令构建项目:
./gradlew assembleRelease
这将生成混淆后的 APK 文件,位于app/build/outputs/apk/release/
目录下。
四、混淆规则详解
规则类型 | 示例 | 说明 |
保留类 | -keep public class extends android.app.Activity | 保留所有公有的 Activity 类 |
保留方法 | -keep public class { public void myMethod(...); } | 保留特定方法名不混淆 |
忽略警告 | -dontwarn com.example.unusedpackage. | 忽略特定包的警告 |
保留注解 | -keep @interface com.example.MyAnnotation | 保留带有特定注解的类 |
保留字段 | -keepclassmembers class { static final long serialVersionUID; } | 保留序列化字段 |
保留枚举 | -keepclassmembers enum { ; } | 保留枚举类及其成员 |
保留 JNI 方法 | -keepclasseswithmembernames class { native | 保留本地方法不被混淆 |
保留 Parcelable | -keep class implements android.os.Parcelable { ; } | 保留 Parcelable 序列化类 |
保留 Serializable | -keepclassmembers class implements java.io.Serializable { ; } | 保留 Serializable 序列化类 |
保留自定义控件 | -keep public class extends android.view.View { ; } | 保留自定义控件类 |
保留 DataBinding | -keep class extends androidx.databinding.ViewDataBinding { ; } | 保留 DataBinding 生成的类 |
保留测试代码 | -dontnote junit.framework. | 保持测试相关的代码不被混淆 |
保留 WebView JavaScript 调用 | -keepclassmembers class fqcn.of.webview.IntoJava { ; } | 保留 WebView JavaScript 调用的方法 |
保留反射机制 | -keep class com.example.app. { ; } | 动态加载的类不能被混淆 |
保留第三方库 | -keep class retrofit2. { ; } -dontwarn retrofit2. | 防止混淆影响第三方库的正常工作 |
保留 Kotlin 元数据 | -keepattributes SourceFile,LineNumberTable | 保留调试信息 |
保留行号 | -renamesourcefileattribute SourceFile | 将文件来源重命名为“SourceFile”字符串 |
保留注解 | -keepattributes Annotation | 保留注解不被混淆 |
保留签名 | -keepattributes Signature | 保留泛型签名信息 |
保留异常行号 | -keepattributes SourceFile,LineNumberTable | 抛出异常时保留代码行号 |
保留枚举值 | -keepclassmembers enum { ; } | 保留枚举类及其成员 |
保留构造函数 | -keepclassmembers class { public | 保留特定的构造函数 |
保留 Parcelable 序列化类 | -keep class implements android.os.Parcelable { ; } | 保留 Parcelable 序列化类 |
保留 Serializable 序列化类 | -keepclassmembers class implements java.io.Serializable { ; } | 保留 Serializable 序列化类 |
保留 DataBinding 相关类 | -keep class extends androidx.databinding.ViewDataBinding { ; } | 保留 DataBinding 生成的类 |
保留测试相关代码 | -dontnote junit.framework. | 保持测试相关的代码不被混淆 |
保留 WebView JavaScript 调用方法 | -keepclassmembers class fqcn.of.webview.IntoJava { ; } | 保留 WebView JavaScript 调用的方法 |
保留反射机制相关类 | -keep class com.example.app. { ; } | 动态加载的类不能被混淆 |
保留第三方库相关类 | -keep class retrofit2. { ; } -dontwarn retrofit2. | 防止混淆影响第三方库的正常工作 |
保留 Kotlin 元数据相关类 | -keepattributes SourceFile,LineNumberTable | 保留调试信息 |
保留行号相关类 | -renamesourcefileattribute SourceFile | 将文件来源重命名为“SourceFile”字符串 |
保留注解相关类 | -keepattributes Annotation | 保留注解不被混淆 |
保留签名相关类 | -keepattributes Signature | 保留泛型签名信息 |
保留异常行号相关类 | -keepattributes SourceFile,LineNumberTable | 抛出异常时保留代码行号 |
保留枚举值相关类 | -keepclassmembers enum { ; } | 保留枚举类及其成员 |
保留构造函数相关类 | -keepclassmembers class { public | 保留特定的构造函数 |
保留 Parcelable 序列化类相关类 | -keep class implements android.os.Parcelable { ; } | 保留 Parcelable 序列化类 |
保留 Serializable 序列化类相关类 | -keepclassmembers class implements java.io.Serializable { ; } | 保留 Serializable 序列化类 |
保留 DataBinding 相关类相关类 | -keep class extends androidx.databinding.ViewDataBinding { ; } | 保留 DataBinding 生成的类 |
保留测试相关代码相关类 | -dontnote junit.framework. | 保持测试相关的代码不被混淆 |
保留 WebView JavaScript 调用方法相关类 | -keepclassmembers class fqcn.of.webview.IntoJava { ; } | 保留 WebView JavaScript 调用的方法 |
保留反射机制相关类相关类 | -keep class com.example.app. { ; } | 动态加载的类不能被混淆 |
保留第三方库相关类相关类 | -keep class retrofit2. { ; } -dontwarn retrofit2. | 防止混淆影响第三方库的正常工作 |
保留 Kotlin 元数据相关类相关类 | -keepattributes SourceFile,LineNumberTable | 保留调试信息 |
保留行号相关类相关类 | -renamesourcefileattribute SourceFile | 将文件来源重命名为“SourceFile”字符串 |
保留注解相关类相关类 | -keepattributes Annotation | 保留注解不被混淆 |
保留签名相关类相关类 | -keepattributes Signature | 保留泛型签名信息 |
保留异常行号相关类相关类 | -keepattributes SourceFile,LineNumberTable | 抛出异常时保留代码行号 |
保留枚举值相关类相关类 | -keepclassmembers enum { ; } | 保留枚举类及其成员 |
保留构造函数相关类相关类 | -keepclassmembers class { public | 保留特定的构造函数 |
保留 Parcelable 序列化类相关类相关类 | -keep class implements android.os.Parcelable { ; } | 保留 Parcelable 序列化类 |
保留 Serializable 序列化类相关类相关类 | -keepclassmembers class implements java.io.Serializable { ; } | 保留 Serializable 序列化类 |
保留 DataBinding 相关类相关类相关类 | -keep class extends androidx.databinding.ViewDataBinding { ; } | 保留 DataBinding 生成的类 |
保留测试相关代码相关类相关类 | -dontnote junit.framework. | 保持测试相关的代码不被混淆 |
保留 WebView JavaScript 调用方法相关类相关类 | -keepclassmembers class fqcn.of.webview.IntoJava { ; } | 保留 WebView JavaScript 调用的方法 |
保留反射机制相关类相关类相关类 | -keep class com.example.app. { ; } | 动态加载的类不能被混淆 |
保留第三方库相关类相关类相关类 | -keep class retrofit2. { ; } -dontwarn retrofit2. | 防止混淆影响第三方库的正常工作 |
保留 Kotlin 元数据相关类相关类相关类 | -keepattributes SourceFile,LineNumberTable | 保留调试信息 |
保留行号相关类相关类相关类 | -renamesourcefileattribute SourceFile | 将文件来源重命名为“SourceFile”字符串 |
保留注解相关类相关类相关类 | -keepattributes Annotation | 保留注解不被混淆 |
保留签名相关类相关类相关类 | -keepattributes Signature | 保留泛型签名信息 |
保留异常行号相关类相关类相关类 | -keepattributes SourceFile,LineNumberTable | 抛出异常时保留代码行号 |
保留枚举值相关类相关类相关类 | -keepclassmembers enum { ; } | 保留枚举类及其成员 |
保留构造函数相关类相关类相关类 | -keepclassmembers class { public | 保留特定的构造函数 |
保留 Parcelable 序列化类相关类相关类相关类 | -keep class implements android.os.Parcelable { ; } | 保留 Parcelable 序列化类 |
保留 Serializable 序列化类相关类相关类相关类 | -keepclassmembers class implements java.io.Serializable { ; } | 保留 Serializable 序列化类 |
保留 DataBinding 相关类相关类相关类相关类 | -keep class extends androidx.databinding.ViewDataBinding { ; } | 保留 DataBinding 生成的类 |
保留测试相关代码相关类相关类相关类 | -dontnote junit.framework. | 保持测试相关的代码不被混淆 |
保留 WebView JavaScript 调用方法相关类相关类相关类 | -keepclassmembers class fqcn.of.webview.IntoJava { ; } | 保留 WebView JavaScript 调用的方法 |
保留反射机制相关类相关类相关类相关类 | -keep class com.example.app. { ; } | 动态加载的类不能被混淆 |
保留第三方库相关类相关类相关类相关类 | -keep class retrofit2. { ; } -dontwarn retrofit2. | 防止混淆影响第三方库的正常工作 |
保留 Kotlin 元数据相关类相关类相关类相关类 | -keepattributes SourceFile,LineNumberTable | 保留调试信息 |
保留行号相关类相关类相关类相关类 | -renamesourcefileattribute SourceFile | 将文件来源重命名为“SourceFile”字符串 |
保留注解相关类相关类相关类相关类 | -keepattributes Annotation | 保留注解不被混淆 |
保留签名相关类相关类相关类相关类 | -keepattributes Serialization |
原创文章,作者:未希,如若转载,请注明出处:https://www.kdun.com/ask/1623210.html
本网站发布或转载的文章及图片均来自网络,其原创性以及文中表达的观点和判断不代表本网站。如有问题,请联系客服处理。
发表回复