Android混淆规则

简介

Java代码是非常容易反编译的。为了很好的保护Java源代码,我们往往会对编译好的class文件进行混淆处理。
ProGuard是一个混淆代码的开源项目。它的主要作用就是混淆,当然它还能对字节码进行缩减体积、优化等,但那些对于我们来说都算是次要的功能。
官网地址:http://proguard.sourceforge.net/

原理

Java 是一种跨平台的、解释型语言,Java 源代码编译成中间”字节码”存储于 class 文件中。由于跨平台的需要,Java 字节码中包括了很多源代码信息,如变量名、方法名,并且通过这些名称来访问变量和方法,这些符号带有许多语义信息,很容易被反编译成 Java 源代码。为了防止这种现象,我们可以使用 Java 混淆器对 Java 字节码进行混淆。

混淆就是对发布出去的程序进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能,而混淆后的代码很难被反编译,即使反编译成功也很难得出程序的真正语义。被混淆过的程序代码,仍然遵照原来的档案格式和指令集,执行结果也与混淆前一样,只是混淆器将代码中的所有变量、函数、类的名称变为简短的英文字母代号,在缺乏相应的函数名和程序注释的况下,即使被反编译,也将难以阅读。同时混淆是不可逆的,在混淆的过程中一些不影响正常运行的信息将永久丢失,这些信息的丢失使程序变得更加难以理解。

混淆器的作用不仅仅是保护代码,它也有精简编译后程序大小的作用。由于以上介绍的缩短变量和函数名以及丢失部分信息的原因, 编译后 jar 文件体积大约能减少25% ,这对当前费用较贵的无线网络传输是有一定意义的。

Android 混淆

Android 混淆代码直接在Android Studio 中配置

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

由于混淆会增加编译时间,并且debug时编译器无法定位到具体位置,所以debug环境下不建议打开。

  • 代码混淆
    配置如 minifyEnabled true
    代码混淆包括四个步骤,代码压缩,优化,混淆,预校验。

    1. 压缩(shrink) : 移除无效的类,类成员,方法,属性等。
    2. 优化(optimize) : 分析和优化二进制代码,根据proguard-android-optimize.txt中的描述,优化可能会造成一定的风险,不能保证在所有版本的Dalvik上正常运行。因此android项目建议关闭该项。
    3. 混淆(obfuscate) : 把类名,属性名, 方法名替换为简短且无意义的名称。
    4. 预校验(previrfy) : 添加预校验信息。 这个预校验是作用在java 平台的, Android 平台上不需要这项功能,去掉之后还可以加快混淆速度。因此android 项目关闭该项。

    这四个流程默认是开启的, 我们需要在Android混淆配置文件中关闭代码优化,和预校验功能,即-dontoptimize, -dontpreverify. 当然proguard-android.txt中默认已经关闭这两项了。

    项目混淆工作流程图

  • 资源混淆
    配置如 shrinkResources true
    压缩资源将移除项目及依赖库中未被使用的资源,可以减少apk的大小,一般建议开启。
    资源处理包括合并资源,移除资源两个流程。

    1. Android Studio 打包时会自动进行merge资源,不受该参数控制。
    2. 移除资源,需要配置该参数。不过建议可以使用lint先自行过一遍。

    还有一种为资源名称的混淆。由facebook开发的redex。
    参考 基于 Facebook Redex 实现 Android APK 的压缩和优化

java 混淆

如果要将输出的jar包进行混淆,可以用proguard工具进行操作。分为命令行式和图像界面工具。可以去网上下载或者 android sdk提供了一个图形化工具proguard,可以进行混淆。路径如下 /sdk/tools/proguard/lib/proguardgui.jar 双击打开即可。
在其中配置相关的混淆参数即可。

代码混淆规则

Android项目中配置如下proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 。 由android sdk 中提供的默认混淆规则proguard-android.txt 和 用户自定义的混淆规则proguard-rules.pro 两部分组成。

命令 作用
-keep 防止类和成员被移除或者被重命名
-keepnames 防止类和成员被重命名
-keepclassmembers 防止成员被移除或者被重命名
-keepclassmembersname 防止成员被重命名
-keepclasseswithmembers 防止拥有该成员的类和成员被移除或者被重命名
-keepclasseswithmembernames 防止拥有该成员的类和成员被重命名
类通配符 * 匹配任意长度字符,但不含包名分隔符(.)
类通配符 ** 匹配任意长度字符,并且包含包名分隔符(.)
类extends 即可以指定类的基类
类implements 匹配实现了某接口的类
类$ 内部类
成员(方法)通配符 * 匹配任意长度字符,但不含包名分隔符(.)
成员(方法)通配符 ** 匹配任意长度字符,并且包含包名分隔符(.)
成员(方法)通配符 *** 匹配任意参数类型
成员(方法)通配符 ... 匹配任意长度的任意类型参数
成员(方法)通配符 <> 匹配方法名,eg. <init>
  • 常见混淆命令
# 代码混淆压缩比,在0~7之间,默认为5,一般不下需要修改
-optimizationpasses 5

# 混淆时不使用大小写混合,混淆后的类名为小写
# windows下的同学还是加入这个选项吧(windows大小写不敏感)
-dontusemixedcaseclassnames

# 指定不去忽略非公共的库的类
# 默认跳过,有些情况下编写的代码与类库中的类在同一个包下,并且持有包中内容的引用,此时就需要加入此条声明
-dontskipnonpubliclibraryclasses

# 指定不去忽略非公共的库的类的成员
-dontskipnonpubliclibraryclassmembers

# 不做预检验,preverify是proguard的四个步骤之一
# Android不需要preverify,去掉这一步可以加快混淆速度
-dontpreverify

# 有了verbose这句话,混淆后就会生成映射文件
# 包含有类名->混淆后类名的映射关系
# 然后使用printmapping指定映射文件的名称
-verbose
-printmapping priguardMapping.txt

# 指定混淆时采用的算法,后面的参数是一个过滤器
# 这个过滤器是谷歌推荐的算法,一般不改变
-optimizations !code/simplification/artithmetic,!field/*,!class/merging/*

# 保护代码中的Annotation不被混淆
# 这在JSON实体映射时非常重要,比如fastJson
-keepattributes *Annotation*

# 避免混淆泛型
# 这在JSON实体映射时非常重要,比如fastJson
-keepattributes Signature

# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
  • keep 元素不进行混淆规则
# 保留所有的本地native方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}

# 保留了继承自Activity、Application这些类的子类
# 因为这些子类有可能被外部调用
# 比如第一行就保证了所有Activity的子类不要被混淆
-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

# 如果有引用android-support-v4.jar包,可以添加下面这行
-keep public class com.null.test.ui.fragment.** {*;}

# 保留Activity中的方法参数是view的方法,
# 从而我们在layout里面编写onClick就不会影响
-keepclassmembers class * extends android.app.Activity {
    public void * (android.view.View);
}

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

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

# 保留Parcelable序列化的类不能被混淆
-keep class * implements android.os.Parcelable{
    public static final android.os.Parcelable$Creator *;
}

# 保留Serializable 序列化的类不被混淆
-keepclassmembers class * implements java.io.Serializable {
   static final long serialVersionUID;
   private static final java.io.ObjectStreamField[] serialPersistentFields;
   !static !transient <fields>;
   private void writeObject(java.io.ObjectOutputStream);
   private void readObject(java.io.ObjectInputStream);
   java.lang.Object writeReplace();
   java.lang.Object readResolve();
}

# 对R文件下的所有类及其方法,都不能被混淆
-keepclassmembers class **.R$* {
    *;
}

# 对于带有回调函数onXXEvent的,不能混淆
-keepclassmembers class * {
    void *(**On*Event);
}
  • 其他自定义混淆规则
#内部类
-keep class com.null.test.MainActivity$* {
    *;
}
#webview
-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.WebViewClient {
    public void *(android.webkit.WebView, java.lang.String);
}

#第三方库
-libraryjars ./libs/android-support-v4.jar
-dontwarn android.support.v4.** 
-dontwarn **CompatHoneycomb
-dontwarn **CompatHoneycombMR2
-dontwarn **CompatCreatorHoneycombMR2
-keep interface android.support.v4.app.** { *; }
-keep class android.support.v4.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment

资源保持规则

用shrinkResources true开启资源压缩后,所有未被使用的资源默认被移除。假如你需要定义哪些资源必须被保留,在 res/raw/ 路径下创建一个 xml 文件,例如 keep.xml。
通过一些属性的设置可以实现定义资源保持的需求,可配置的属性有:

  • tools:keep 定义哪些资源需要被保留(资源之间用“,”隔开)

  • tools:discard 定义哪些资源需要被移除(资源之间用“,”隔开)

  • tools:shrinkMode 开启严格模式

mapping文件

混淆过的包必须进行检查,避免因混淆引入的bug。
混淆后会在 路径/build/outputs/mapping/release/ 目录下会输出以下文件:

  • dump.txt 描述apk文件中所有类的内部结构
  • mapping.txt 提供混淆前后类,方法,类成员等的对照表
  • seeds.txt 列出没有被混淆的类和成员
  • usage.txt 列出被移除的代码

一般发布包的时候都要保留mapping.txt 文件。 目的有有两个, 1. 当出现线上crash时候,可以根据该mapping文件对照源码找到对应的位置。2. 当发布补丁包时候,也会指定该mapping文件,已避免补丁包无法使用。

  • 解出混淆栈
    sdk/tools/proguard路径下附带了反解工具proguardgui.sh 或者 proguardgui.jar.打开即可,看到左侧的retrace, 选择混淆包对应的mapping.txt 文件, 再将crash 的 stack trace 粘贴到输入框中, 点击retrace 混淆前的堆栈信息即可显示。
    命令行操作为:
    retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]
    eg. retace.sh -verbose mapping.txt log.txt

  • 指定mapping文件
    增加proguard-path-rules.pro , 在其中指定-applymapping mapping.txt.

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

推荐阅读更多精彩内容