Android混淆大法

Android混淆大法

本文涉及内容:
1.混淆的基本介绍,混淆的基本配置及示例
2.如何进行多模块的混淆
3.实际项目中混淆时会遇到的问题
4.混淆后如何进行debug和日志查看



前言

混淆,主要作用就是对把项目的原本清晰的类名、方法名等转换为难易理解的a/b/c名字,不会改变原有的代码逻辑,但是会增大阅读难度。如果是为了应用安全,通常直接使用应用加固处理。但是,有些情况下,无法使用加固,但为了防止程序主要逻辑泄露,混淆还是很有必要的。

Android SDK 自带了混淆工具Proguard。它位于SDK根目录\tools\proguard下面。如果开启了混淆,Proguard默认情况下会对所有代码,包括第三方包都进行混淆,但并不是所有的文件都可以进行混淆,有些文件混淆后,会影响程序运行,这些是需要避免混淆的,配置文件的主要作用也即是如此。


一、gradle 混淆开启

Proguard是android Studio自带的混淆工具,通过gradle可以进行配置,主要作用是对java代码的混淆优化和对资源文件的压缩,这些配置都是默认开启的。

android {
    ......
    defaultConfig {
        ......
    }
    buildTypes {
        release {
            minifyEnabled true      // 开启代码混淆
            zipAlignEnabled true    // 开启Zip压缩优化
            shrinkResources true    // 移除未被使用的资源
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//代码混淆的主配置文件
        }
    }
    ......
}

二、混淆规则

1.模板示例

模板可以直接使用,想要深入理解的可以继续向下看

# --------------------------------------------基本指令区--------------------------------------------#
-ignorewarning                                      # 忽略警告
-optimizationpasses 5                               # 指定代码的压缩级别(0~7之间,默认为5)
-dontusemixedcaseclassnames                         # 不使用大小写混合(windows大小写不敏感,建议加入)
-dontskipnonpubliclibraryclasses                    # 不混淆非公共的库的类
-dontskipnonpubliclibraryclassmembers               # 不混淆非公共的库的类的成员
-dontpreverify                                      # 混淆时不做预校验(Android不需要预校验,去掉可以加快混淆速度)
-verbose                                            # 混淆时记录日志(混淆后会生成映射文件)

#混淆时所采用的算法(谷歌推荐算法)
-optimizations !code/simplification/arithmetic,!field,!class/merging,!code/allocation/variable

#添加支持的jar(引入libs下的所有jar包)
-libraryjars libs(*.jar;)

#将文件来源重命名为“SourceFile”字符串
-renamesourcefileattribute SourceFile

#保持注解不被混淆
-keepattributes *Annotation*
-keep class * extends java.lang.annotation.Annotation {*;}

#保持泛型不被混淆
-keepattributes Signature
#保持反射不被混淆
-keepattributes EnclosingMethod
#保持异常不被混淆
-keepattributes Exceptions
#保持内部类不被混淆
-keepattributes Exceptions,InnerClasses
#抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable

#------------------------------------默认保留区--------------------------------------#
#保持四大组件不被混淆
-keep public class * extends android.app.Fragment
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-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

#保持 Google 原生服务需要的类不被混淆
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService

# 保留support下的所有类及其内部类,AndroidX
-keep class android.support.** {*;}
-dontwarn android.support.**
-keep interface android.support.** { *; }
-keep class androidx.** {*;}
-keep interface androidx.** {*;}
-keep public class * extends androidx.**
-dontwarn androidx.**

# 保留继承的
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
-keep class com.google.android.material.** {*;}
-dontwarn com.google.android.material.**
-dontnote com.google.android.material.**


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

# 保留我们自定义控件(继承自View)不被混淆
-keep public class * extends android.view.View{
    *** get*();
    void set*(***);
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

#保留指定格式的构造方法不被混淆
-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

#保留在Activity中的方法参数是view的方法(避免布局文件里面onClick被影响)
-keepclassmembers class * extends android.app.Activity {
    public void *(android.view.View);
}

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

#保持R(资源)下的所有类及其方法不能被混淆
-keep class **.R$* { *; }


#保持 Parcelable 序列化的类不被混淆(注:aidl文件不能去混淆)
-keep class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
}

#需要序列化和反序列化的类不能被混淆(注:Java反射用到的类也不能被混淆)
-keepnames class * implements java.io.Serializable

#保持 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();
}

#保持 BaseAdapter 类不被混淆
-keep public class * extends android.widget.BaseAdapter { *; }
#保持 CusorAdapter 类不被混淆
-keep public class * extends android.widget.CusorAdapter{ *; }

#-------------------------------------webView区---------------------------------------#

#保护代码中的jsinterface注解不被混淆
-keepattributes *JavascriptInterface*

#WebView处理,项目中没有使用到webView忽略即可
#保持AndroidJavaScript进行交互的类不被混淆
-keep class **.AndroidJavaScript { *; }
-keepclassmembers class * extends android.webkit.WebViewClient {
     public void *(android.webkit.WebView,java.lang.String,android.graphics.Bitmap);
     public boolean *(android.webkit.WebView,java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebChromeClient {
     public void *(android.webkit.WebView,java.lang.String);
}

#网络请求相关
-keep public class android.net.http.SslError

#-------------------------------------删除代码区(可不配置)--------------------------------------#
删除代码中Log相关的代码
-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int v(...);
    public static int i(...);
    public static int w(...);
    public static int d(...);
    public static int e(...);
}

#---------------------------------1.实体类---------------------------------
#--------------------------------------------------------------------------
#---------------------------------2.与JS交互的类-----------------------------
#--------------------------------------------------------------------------
#---------------------------------3.反射相关的类和方法-----------------------
#--------------------------------------------------------------------------
#---------------------------------4.内部类--------------------------------
#--------------------------------------------------------------------------
#---------------------------------5.第三方依赖----------------------------------------

2.重点问题注意

(1)混淆后webview无法使用或是JSBridge接口无法使用

注意,查看是否配置了webview的免混淆

#保护代码中的jsinterface注解不被混淆
-keepattributes *JavascriptInterface*

#WebView处理,项目中没有使用到webView忽略即可
#保持AndroidJavaScript进行交互的类不被混淆
-keep class **.AndroidJavaScript { *; }
-keepclassmembers class * extends android.webkit.WebViewClient {
     public void *(android.webkit.WebView,java.lang.String,android.graphics.Bitmap);
     public boolean *(android.webkit.WebView,java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebChromeClient {
     public void *(android.webkit.WebView,java.lang.String);
}

(2)AIDL接口无法调用

所有的aidl文件都需要免混淆

-keep class "你的aidl文件类名"{*;}

例如:

-keep class com.demo.superfan.testAidl{*;}

(3)内部类导致的错误

排查下是否有内部类,内部类需要单独配置,无论这个内部类所在的类是否免混淆,都需要配置

-keep class "内部类所在类的文件类名"$*{*;}

例如:

-keep class com.demo.DemoUtil$DemoInner{*;}-keep class com.demo.DemoUtil$*{*;}//保持DemoUtil中的所有内部类免混淆或移除

(4)实体类导致的问题

通常的实体类转json时,如果实体类混淆后,都会出现问题。 需要将所有实体类进行配置

(5)多个modle的混淆问题

多个modle的混淆有两种方式:

  • 一、直接把所有的混淆配置都写在主modle中;
  • 二、每个modle配置各自的混淆配置
    推荐使用对每个model进行各自的混淆配置,即使是各个modle被其他项目引用,混淆配置也会直接跟随。

但是,对每个modle进行配置时,需要注意:

  • 主modle的gradle中按上述方式进行配置;
  • 子modle的gradle中按以下方式进行配置:
plugins {
    id 'com.android.library'
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        ... ...
    }

    buildTypes {
        release {
            minifyEnabled true
            consumerProguardFiles 'proguard-rules.pro'
        }
        debug {
            minifyEnabled false
            consumerProguardFiles 'proguard-rules.pro'
        }
    }
    ... ...
}

子Model的混淆配置使用consumerProguardFiles 'proguard-rules.pro'

(6)混淆后打包,错误日志也是混淆后的,找不到类

混淆后,由于class的类名变成了abc,导致日志文件输出也变成了com.demo.a.b.c类似这种,对于排查问题造成了很大的困难。不过,不用担心,早在我们混淆时,编译器就为我们生成了一个混淆关系对应文件。在这里:
在这里插入图片描述
内容如下:

 com.catchai.superfan.proguardtestdemo.IAIDLCallback -> a.a.a.a.a:

即是,将com.catchai.superfan.proguardtestdemo.IAIDLCallback混淆为a.a.a.a.a
因此,如果是正式发包,最好打包时,将release包和mapping一起保存好,方便后期排查线上日志。

三、深入理解

1.混淆的输出文件

有心的童鞋可能会发现,开启了混淆的项目,在androidStudio打包时,在modle的build/outputs下有一个mapping文件夹,这里面便是混淆的输出文件。
一般有四个:configuration.txt、mapping.txt、seeds.txt、usage.txt

(1)configuration.txt

通常是混淆的配置文件,基本和modle下的proguard-rules.pro文件一致。

(2)mapping.txt

混淆打包后的关系对应文件,混淆的名字映射到原始代码的名字。此文件对于后期排查日志很重要。

(3)seed.txt

keep保持的内容,文件内是proguard-rules.pro配置文件中要保持的不被混淆的内容。此文件有助于排查我们的 proguard-rules.pro 配置文件中的配置内容是否生效。

(4)usage.txt

没有引用,被移除的代码


那么这四个文件是如何生成的呢?
在这里插入图片描述
其实简单来说就是:

  • 编译器首先读取proguard-rules.pro查看哪些是需要保持的(不需要混淆),生成seeds.txt文件;
  • 然后进行压缩,删减哪些没有被引用的类,生成usage.txt文件;
  • 接着开始混淆,把对应关系保存到mapping.txt文件中。

2.混淆的规则

关键字

在这里插入图片描述
理解:

  • 虽然keep是保留类和类中的成员不被混淆和移除。但是内部类不输入类和类成员的范畴。

通配符

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安静的码字猴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值