关于Android混淆的一些经验

一些关于混淆的好文章

Android混淆从入门到精通

写给Android开发者的混淆使用手册

读懂 Android 中的代码混淆

一些经验

1. 哪些不应该混淆

反射中使用的元素

如果一些被混淆使用的元素(属性,方法,类,包名等)进行了混淆,可能会出现问题,如NoSuchFiledException或者NoSuchMethodException等

Jni接口和java的native方法

因为这个方法需要和native方法保持一致,Android工程默认的混淆配置默认混淆,见下方附录。

数据模型

与服务端交互时,使用GSON、fastjson等框架解析服务端数据时,所写的JSON对象类不混淆,否则无法将JSON解析成对应的对象.

抽象内部类
2018.3.28日更新
近期混淆时遇到了java.lang.AbstractMethodError
这个错误是由于一些类中的抽象类匿名对象的方法名被混淆
了。
比如:

abstract class Flying{
  void fly();
}
class Animal{
   Flying f=new Flying(){
      void fly(){
          //fly
      }
  }
}

如果你只是keep了Animal类

-keep class com.ditclear.demo.Animal{*;}

那么混淆之后就会如下所示

class Animal{
   Flying f=new Flying(){
      void a(){
      //方法名被混淆了
          //fly
      }
  }
}

方法名被混淆了,就会出现java.lang.AbstractMethodError
解决的方法和内部类一样

-keep class com.ditclear.demo.Animal{*;}
-keep class com.ditclear.demo.Animal$*{*;}

四大组件不混淆

  • 四大组件声明必须在manifest中注册,如果混淆后类名更改,而混淆后的类名没有在manifest注册,是不符合Android组件注册机制的.
  • 外部程序可能使用组件的字符串类名,如果类名混淆,可能导致出现异常
-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
-keep public class com.android.vending.licensing.ILicensingService

使用第三方开源库或者引用其他第三方的SDK包

如果有特别要求,也需要在混淆文件中加入对应的混淆规则,优秀的开源库一般都会附上自己的混淆规则

枚举

有反射相关的东西 values()valueOf(),Android工程默认的混淆配置默认混淆,见下方附录。

注解

很多场景下注解被用作在运行时反射确定一些元素的特征.为了保证注解正常工作,我们不应该对注解进行混淆。

Android工程默认的混淆配置默认混淆

-keepattributes *Annotation*

js调用java的方法

同native,在proguard.pro中有这样一段话

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface class:
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
   public *;
}

Parcelable的子类和Creator静态成员变量

否则会产生Android.os.BadParcelableException异常,Android工程默认的混淆配置默认混淆

-keepclassmembers class * implements android.os.Parcelable {
    public static final ** CREATOR;
}

2. 解决混淆之后的bug

混淆完之后,需要进行详细的测试,确保没有bug,遇到bug就解决它。所以了解混淆之后的代码就很有必要

进行混淆打包后在 /build/outputs/mapping/release/ 目录下会输出以下文件:

  • dump.txt
    描述APK文件中所有类的内部结构

  • mapping.txt
    提供混淆前后类、方法、类成员等的对照表(很有用)

    如果你的代码混淆后会产生bug的话,log提示中是混淆后的代码,希望定位到源代码的话就可以根据mapping.txt反推。

    每次发布都要保留它方便该版本出现问题时调出日志进行排查,它可以根据版本号或是发布时间命名来保存或是放进代码版本控制中。

  • seeds.txt
    列出没有被混淆的类和成员

  • usage.txt
    列出被移除的代码

例如:
现在有一个NoSuchFieldException(一般是反射引起的)

NoSuchFieldException.png

通过混淆之后可以看到com.google.gson.b,我们不知道到底是哪个类引起的,这个时候我们就可以到mapping.txt文件去查找,如下图:

mapping.png

然后就知道了com.google.gson.b对应的是com.google.gson.ExclusionStrategy,只要保证它们不被混淆就可以了

-keep class com.google.**{ *; }

这只是一个简单的例子,大多数情况都可以依样画葫芦。

在Dubug模式下开启混淆

一般混淆是用在正式打包的时候的,但我们在配置混淆的时候需要不断的尝试混淆的配置,不可能打一个正式包,去验证一下对不对,那就太麻烦了,所以我一般都在debug的时候开启,调试好了再关闭。

android {
    ...
    buildTypes {
        release {
            minifyEnabled true
            debuggable false
            shrinkResources true /*压缩资源文件*/
            /*zipAlign可以让安装包中的资源按4字节对齐,这样可以减少应用在运行时的内存消耗*/
            zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            minifyEnabled true
            debuggable true
            shrinkResources true
            zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

使用@keep注解
除了在自定义的混淆配置中写-keepxxx之外,也可以使用@keep注解来使文件避免被混淆,在Android默认的混淆配置下有这样的代码

# Understand the @Keep support annotation.
-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
  public class LoginEvent {    
          @Keep
          ···
          ···
  }

资源混淆

一些替代资源,例如多语言支持的 strings.xml,多分辨率支持的 layout.xml 等,在我们不需要使用又不想删除掉时,可以使用资源压缩将它们移除。

我们使用 resConfig 属性来指定需要支持的属性,例如

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

其他未显式声明的语言资源将被移除。蛮有用的。

一些专业的网站

总结

混淆的话,我觉得在配置好通用的规则之后,就是不断尝试,遇到异常,就去了解原因,避免混淆文件。

附录

  • Android工程默认的混淆配置 proguard-android.txt,在自己的混淆文件中的一样的东西就不必再写一遍了
# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html
#
# Starting with version 2.2 of the Android plugin for Gradle, this file is distributed together with
# the plugin and unpacked at build-time. The files in $ANDROID_HOME are no longer maintained and
# will be ignored by new version of the Android plugin for Gradle.
#
# Optimizations: If you don't want to optimize, use the
# proguard-android.txt configuration file instead of this one, which
# turns off the optimization flags.  Adding optimization introduces
# certain risks, since for example not all optimizations performed by
# ProGuard works on all versions of Dalvik.  The following flags turn
# off various optimizations known to have issues, but the list may not
# be complete or up to date. (The "arithmetic" optimization can be
# used if you are only targeting Android 2.0 or later.)  Make sure you
# test thoroughly if you go this route.
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5
-allowaccessmodification
-dontpreverify

# The remainder of this file is identical to the non-optimized version
# of the Proguard configuration file (except that the other file has
# flags to turn off optimization).

-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose

# Preserve some attributes that may be required for reflection.
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod

-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
-keep public class com.google.android.vending.licensing.ILicensingService
-dontnote com.android.vending.licensing.ILicensingService
-dontnote com.google.vending.licensing.ILicensingService
-dontnote com.google.android.vending.licensing.ILicensingService

# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
-keepclasseswithmembernames class * {
    native <methods>;
}

# Keep setters in Views so that animations can still work.
-keepclassmembers public class * extends android.view.View {
    void set*(***);
    *** get*();
}

# We want to keep methods in Activity that could be used in the XML attribute onClick.
-keepclassmembers class * extends android.app.Activity {
    public void *(android.view.View);
}

# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keepclassmembers class * implements android.os.Parcelable {
    public static final ** CREATOR;
}

-keepclassmembers class **.R$* {
    public static <fields>;
}

# Preserve annotated Javascript interface methods.
-keepclassmembers class * {
    @android.webkit.JavascriptInterface <methods>;
}

# The support libraries contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
-dontnote android.support.**
-dontwarn android.support.**

# Understand the @Keep support annotation.
-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>(...);
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容