史上最全Android代码混淆详解

本文详细介绍了代码混淆在Android开发中的重要性,包括其目的、混淆的好处(如保护代码、减小APK大小和提升性能)、如何在Gradle中设置混淆,以及混淆参数的语法和混淆字典的使用。还提到在开发Flutter插件时混淆字典的作用,以解决命名冲突问题。

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

一、为什么代码要混淆

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

二、如何开启混淆

在app层下找到build.gradle文件,找到buildTypes下的release设置minifyEnabled为true即可
在这里插入图片描述
proguard-rules.pro为混淆规则文件

三、混淆参数语法介绍

-optimizationpasses 5                       # 代码混淆的压缩比例,值介于0-7,默认5
-verbose                                    # 混淆时记录日志
-dontoptimize                               # 不优化输入的类文件
-dontshrink                                 # 关闭压缩
-dontpreverify                              # 关闭预校验(作用于Java平台,Android不需要,去掉可加快混淆)
-dontoptimize                               # 关闭代码优化
-dontobfuscate                              # 关闭混淆
-ignorewarnings                             # 忽略警告
-dontwarn com.squareup.okhttp.**            # 指定类不输出警告信息
-dontusemixedcaseclassnames                 # 混淆后类型都为小写
-dontskipnonpubliclibraryclasses            # 不跳过非公共的库的类
-printmapping mapping.txt                   # 生成原类名与混淆后类名的映射文件mapping.txt
-useuniqueclassmembernames                  # 把混淆类中的方法名也混淆
-allowaccessmodification                    # 优化时允许访问并修改有修饰符的类及类的成员
-renamesourcefileattribute SourceFile       # 将源码中有意义的类名转换成SourceFile,用于混淆具体崩溃代码
-keepattributes SourceFile,LineNumberTable  # 保留行号
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod # 避免混淆注解、内部类、泛型、匿名类
-optimizations !code/simplification/cast,!field/ ,!class/merging/   # 指定混淆时采用的算法

首先我们先来看一下 keep 关键字

关键字描述
keep保留类和类中的成员,防止被混淆或者移除
keepnames保留类和类中的成员,防止被混淆,但是当成员没有被引用时会被移除
keepclassmembers只保留类中的成员,防止他们被混淆或者移除
keepclassmembersnames只保留类中的成员,防止他们被混淆或者移除,但是当类中的成员没有被引用时还是会被移除
keepclasseswithmembers保留类和类中的成员,前提是指明的类中必须含有该成员,没有的话还是会被混淆
keepclasseswithmembersnames保留类和类中的成员,前提是指明的类中必须含有该成员,没有的话还是会被混淆。需要注意的是没有被引用的成员会被移除

接下来我们一起来看一下通配符

关键字描述
<filed>匹配类中的所有字段
<method>匹配类中的所有方法
<init>匹配类中的所有构造函数
*匹配任意长度字符,但不含包名分隔符(.)。比如说我们的完整类名是com.example.test.MyActivity,使用com.,或者com.exmaple.都是无法匹配的,因为无法匹配包名中的分隔符,正确的匹配方式是com.exmaple..,或者com.exmaple.test.,这些都是可以的。但如果你不写任何其它内容,只有一个*,那就表示匹配所有的东西。
**匹配任意长度字符,并且包含包名分隔符(.)。比如proguard-android.txt中使用的-dontwarn android.support.**就可以匹配android.support包下的所有内容,包括任意长度的子包。
***匹配任意参数类型。比如void set*()就能匹配任意传入的参数类型, get*()就能匹配任意返回值的类型。
匹配任意长度的任意类型参数。比如void test(…)就能匹配任意void test(String a)或者是void test(int a, String b)这些方法。

下面是我使用的混淆模板大家可以进行参考

# 混淆后类型都为小写
-dontusemixedcaseclassnames
# 表示不跳过library中的非public的类
-dontskipnonpubliclibraryclasses
 # 混淆时记录日志
-verbose
#表示不进行优化,建议使用此选项
-dontoptimize
#表示不进行预校验。这个预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度
-dontpreverify
# 避免混淆注解、内部类、泛型、匿名类
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod
#保持所有类的 native 方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}
#表示不混淆 View 的子类中的 set 和 get 方法,因为 View 中的属性动画需要 setter 和 getter,混淆了就无法工作了
-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();

}
#表示不混淆 Activity 及 Activity 子类中方法参数是 (android.view.View) 的方法
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}
#表示不对美剧类中的 calues 和 valueOf(java.lang.String) 进行混淆
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
#表示不混淆 Parcelable 的实现类中的 CREATOR,我们知道序列化与反序列化的过程都需要 CREATOR, 混淆了就无法工作了
-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

#表示不混淆 R 类中 的 static 变量,在 R 类中。这些资源 ID 是系统自动帮我们生成的,混淆了就无法找到相应的资源。
-keepclassmembers class **.R$* {
    public static <fields>;
}
#表示混略 android.support 包下代码的警告
-dontwarn android.support.**
#表示不混淆 Keep 类
-keep class androidx.annotation.Keep
-keep class com.kl.tdoalocalization.mqtt.MqttManager{
public <methods>;
public <fields>;
}
#添加自定义混淆规则,以及第三方库的混淆规则

四、混淆字典的使用

1、为什么要讲混淆字典?

近期遇到一个问题,在开发flutter插件时需要用到自己开发的Android原生sdk,但在项目编译的时候却报如下异常。
在这里插入图片描述
原因是红色框标注的两个sdk发生了冲突,但是这两个sdk包名,类名都不一样怎么会冲突呢?后来通过jd-gui工具发现,两个sdk都是被混淆后的,混淆后的包名类名都变为a,b,c这样的字符,导致两个sdk都有同样的a.a.a.a类所以导致了冲突,此时就需要引入混淆字典来自定义混淆后的字符,不使用a,b,c这种默认字符

2、如何使用混淆字典

2.1、在proguard-rules.pro同级目录中创建一个filename.txt混淆字典文件

下面是字典的示例,建议可在每个关键字之前加上一些特殊的前缀,例如: xxdo、xxif

# 使用java中的关键字作字典:避免混淆后与其他包重名,而且混淆之后的代码更加不利于阅读
#
# This obfuscation dictionary contains reserved Java keywords. They can't
# be used in Java source files, but they can be used in compiled class files.
# Note that this hardly improves the obfuscation. Decent decompilers can
# automatically replace reserved keywords, and the effect can fairly simply be
# undone by obfuscating again with simpler names.
# Usage:
#     java -jar proguard.jar ..... -obfuscationdictionary filename.txt
#

do
if
for
int
new
try
byte
case
char
else
goto
long
this
void
break
catch
class
const
final
float
short
super
throw
while
double
import
native
public
return
static
switch
throws
boolean
default
extends
finally
package
private
abstract
continue
strictfp
volatile
interface
protected
transient
implements
instanceof
synchronized

注:filename.txt是用来指定混淆后生成的名字的字典文件,字典文件中的空格,标点符号,重复的词,还有以’#'开头的行都会被忽略。需要注意的是添加了字典并不会显著提高混淆的效果,添加字典有两个作用:一是避免与其他包混淆后重名;二是更不利与阅读;

2.2、在proguard-rules.pro文件中做如下设置:

-obfuscationdictionary filename.txt
-classobfuscationdictionary filename.txt
-packageobfuscationdictionary filename.txt

-obfuscationdictionary filename .txt:指定一个混淆类名、成员变量名、方法名的字典。
-classobfuscationdictionary filename .txt:指定一个混淆类名的字典,字典的格式与-obfuscationdictionary相同;
-packageobfuscationdictionary filename .txt:filename 指定一个混淆包名的字典,字典格式与-obfuscationdictionary相同。

上面配置好了混淆字典。

参考连接:https://blog.csdn.net/zhayunbiao/article/details/117693922

https://juejin.cn/post/6844903504801169422?searchId=20231116113200A17806C89AF4451902B5

https://blog.csdn.net/fengyulinde/article/details/106115757/

### 回答1: Linux内核是一个开源的操作系统内核,拥有非常丰富的配置选项。以下是对Linux内核配置选项的详细解释: 1. 进程管理:通过配置选项,可以选择支持多进程、多线程、多任务等特性。可以设置进程调度策略、锁定内存区域等。 2. 文件系统支持:Linux内核支持多种文件系统,包括Ext2、Ext3、Ext4、XFS等。配置选项中可以选择需要支持的文件系统类型。 3. 设备驱动支持:通过配置选项可以选择支持的硬件设备驱动,比如网卡驱动、声卡驱动、USB驱动等。 4. 内存管理:可以配置页面大小、内存映射方式、虚拟内存管理等相关选项,以提高内存的利用效率。 5. 网络支持:可以选择支持不同的网络协议栈,比如TCP/IP、UDP等。还可以通过配置选项设置网络参数,如MTU大小、网络连接数等。 6. 安全性配置:可以选择开启不同的安全特性,如SELinux、AppArmor等。还可以对访问控制进行细粒度的配置。 7. 调试支持:通过配置选项可以选择是否开启调试信息和调试功能,以便于开发和排查问题。 8. 电源管理:可以选择支持电源管理功能,以延长电池寿命或节约电能。 9. 定时器支持:可以配置内核定时器的精度和分辨率,以满足不同应用场景的要求。 10. 文件系统特性:可以选择开启各种文件系统的特性,如日志、快照、压缩等。 总而言之,Linux内核配置选项非常丰富,可以根据不同的需求和环境进行灵活配置,以获得最佳的性能和功能。 ### 回答2: Linux内核是一个自由开源的操作系统内核,可运行在各种计算机硬件平台上。内核配置是指根据特定需求对内核进行定制和编译,以满足用户对系统功能和性能的要求。 史上最全的Linux内核配置详解包括了众多的选项和参数,可以根据用户的需求进行选择。其中包括了文件系统支持、设备驱动、网络协议、性能优化等方面的配置。 在文件系统支持方面,内核提供了多个选项,如EXT4、XFS、Btrfs等,用户可以根据需要选择合适的文件系统。此外,还可以选择支持的文件系统功能,如日志系统、快照、压缩等。 设备驱动是Linux内核的一个重要组成部分,内核提供了大量的设备驱动选项,包括网络设备、声卡、USB设备、磁盘控制器等。用户可以根据自己的硬件配置选择相应的驱动。 网络协议是支持网络通信的关键,内核提供了TCP/IP、IPv6、IPSec等多种网络协议的支持。用户可以根据网络环境的需求选择启用相应的协议。 内核配置还包括了一些性能优化的选项,如预排定、缓存管理、中断处理等。用户可以根据系统的性能需求选择相应的优化选项。 另外,内核配置中还包括了调试和跟踪选项,可以帮助开发人员定位和解决问题。 总之,史上最全的Linux内核配置详解提供了众多选项和参数供用户选择和定制,以满足各种不同的需求。用户可以根据自己的需求选择适合自己的内核配置,以获得更好的系统性能和功能支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值