美文网首页
快速搞定 Android Library 工程混淆问题

快速搞定 Android Library 工程混淆问题

作者: 鱿鱼鉄板燒 | 来源:发表于2019-08-03 22:22 被阅读0次

    最近公司项目的库需要发布给第三方使用,代码安全的问题就暴露出来,原来都是交由内部的其他安卓团队处理,但是处理方式非常暴力就是直接不混淆我们的库工程,这样造成代码很容易就被反编译了。我只好硬上研究了一波。

    本文记录如何进行安卓Libray工程混淆经验。安卓混淆上的肯定是大名鼎鼎的 ProGuard, 那我们开始吧。

    1. ProGuard基础

    1.1 ProGuard基本配置

    网上关于混淆的学习记录文章已经很多了,这边我整理出了一些基本的配置选项

    # 指定代码的压缩级别,值在0-7之间。一般设置5足矣
    -optimizationpasses 5
    
    # 打印混淆信息
    -verbose
    
    # 代码优化选项,不加该行会将没有用到的类删除,发布的是代码库这个选项需要
    # 在做混淆之前最开始会默认对代码进行压缩,为了增加反编译的难度可以选择不压缩 
    -dontshrink
    
    # 保留参数的名称和方法,该选项可以保留调试级别的属性。
    -keepparameternames
    
    # 过滤泛型,出现类型转换错误时再启用这个。目前的项目暂时无泛型类型,我先注释了
    #-keepattributes Signature
    
    # 保护代码中的Annotation不被混淆
    -keepattributes *Annotation*
    
    # 指定不去忽略非公共的库的类的成员
    -dontskipnonpubliclibraryclassmembers
    
    # 指定不去忽略非公共的库类(不跳过library中的非public的类)
    -dontskipnonpubliclibraryclasses
    
    

    如果有不合理或更好的选项记得告诉我哟😄非常感谢

    1.2 Proguard 关于Keep的关键字

    关键字 描述
    keep 保留类和类中的成员,防止被混淆或移除
    keepnames 保留类和类中的成员,防止被混淆,成员没有被引用会被移除
    keepclassmembers 只保留类中的成员,防止被混淆或移除
    keepclassmembernames 只保留类中的成员,防止被混淆,成员没有引用会被移除
    keepclasseswithmembers 保留类和类中的成员,防止被混淆或移除,保留指明的成员
    keepclasseswithmembernames 保留类和类中的成员,防止被混淆,保留指明的成员,成员没有引用会被移除

    这边值得注意的是第一种关键字 keep

    如果你只想保留类的名字我们可以直接配置如下:

    # 如下配置只保留了类的名字MyClass,类的所有成员依然会被混淆
    -keep class com.your.class.name.MyClass
    
    # 只有写明具体类成员的匹配规则才能让身体里面的混淆规则生效。
    -keep class com.your.class.name.MyClass {*;}
    
    # 举个🌰(例子)如下配置是我用来处理库工程类,期望所有public方法都开放出来,不希望被混淆的配置
    -keep class com.your.class.name.MyClass {
          public <fields>;
        public <methods>;
        public static final <fields>;
    }
    
    

    1.3 Proguard通配符

    通配符 描述
    <field> 匹配类中的所有字段
    <method> 匹配类中所有的方法
    <init> 匹配类中所有的构造函数
    * 匹配任意长度字符,不包含包名分隔符(.)
    ** 匹配任意长度字符,包含包名分隔符(.)
    *** 匹配任意参数类型
    ... 其他

    1.4 Android 特有的 @Keep 标签

    默认情况下就算写了@Keep还是会被混淆的,因为默认情况下 Android Studio 并没有开启该选项
    在混淆的配置文件 proguard-rules.pro 中加入以下配置:

    -keep class android.support.annotation.Keep
    
    -keep @android.support.annotation.Keep class *
    
    -keepclasseswithmembers class * {
        @android.support.annotation.Keep <methods>;
    }
    
    -keepclasseswithmembers class * {
        @android.support.annotation.Keep <fields>;
    }
    
    -keepclasseswithmembers class * {
        @android.support.annotation.Keep <init>(...);
    }
    

    ⚠️注意:
    目前从网上的一些资料显示如果将 @Keep 添加到类上逻辑上是只不混淆类的名称,但是从实际使用结果上看,如果@Keep 添加到类名上,则整个类无法被混淆。

    1.5 保留Native方法

    JNI层相关的方法理论上就算没有调用到,也是不可以混淆后直接被移除的,否则 NDK 中采用注册模式的JNI方法的时候会出现注册失败的问题。

    -keepclasseswithmembers class * {
        native <methods>;
    }
    

    结合Keep关键字说明,通过以上配置可以保留类和类中被指名的native方法成员函数。

    1.6 保留Library工程的UI相关类

    二次封装的库工程不免有些UI相关的工具类,这部分如果被混淆了,引入方就变成无法正常使用。我们需要对这部分也进行不混淆的控制。

    -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
    -keep public class * extends android.view.View
    -keepclassmembers class * extends android.app.Activity{
        public void *(android.view.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);
    }
    # 不混淆资源部分的配置
    -keep class **.R$* {*;}
    

    2. 启用混淆

    默认在Android Studio的配置下是没有启用的我们需要手动开启。

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

    但是这个只有在每次Release打包之后才生效,我们有时候需要在Debug的时候就进行混淆测试,这时我们可以添加一个新的buildType

    android {
            ...
        buildTypes {
            release {
                minifyEnabled true // open ProGuard
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
            debugMini {
                initWith debug
                minifyEnabled true
                shrinkResources true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                matchingFallbacks = ['debug']
            }
    
        }
    }
    

    这样我们就可以在默认Run的时候选择buildVariants指定我们刚刚新加入的debugMini

    3. 总结

    这篇短短的文章应该可以帮助不少小伙伴跳出深坑,但是更多使用还是需要在工作中去积累。一些小小的体会不求一条混淆命令吃了全部的混淆配置,我们需要精细的配置,通用的配置总会有些不如意地方,那我们就针对单个Class进行一对一的配置,我相信可以得到更好的混淆结果。

    本次的分享大概就这些内容了欢迎大家留言一起讨论学习与进步。

    谢谢大家。

    相关文章

      网友评论

          本文标题:快速搞定 Android Library 工程混淆问题

          本文链接:https://www.haomeiwen.com/subject/xtisdctx.html