Android 混淆机制详解

前言

混淆是增加逆向工程和破解的难度,防止App知识产权被窃取的一个有力手段,高级的代码混淆甚至可以有效地保护存储在客户端的密钥,同时混淆也有很多要注意的地方。

从广义上讲,Android 中的混淆包括三种类型

  • java 代码的混淆
  • Native (C & C++) 代码的混淆
  • 资源文件的混淆

java 代码的混淆

java 代码的混淆在Android 中是最为常见的一种混淆方式,一般依赖于Proguard或者 DexGuard 工具,其中Progurad 是免费且开源的,DexGuard 是付费的。

Proguard 的特性
在Android 中使用 Proguard,它不仅仅为我们提供代码混淆的功能,事实上Proguard 主要提供了4个功能特性

  • 压缩:java源代码通常被编译为字节码,虽然字节码比源代码更简洁,但它本身依然会包含很多无用的代码,Proguard 的压缩功能通过分析字节码,能够检测并移除没有使用到的类,字段,方法和属性。
  • 优化:优化Java 字节码,同时移除没有使用到的指令。
  • 混淆:使用无意义的简短字母组合对类名,字段名、和方法名进行重命名。
  • 预检验:对上述处理后的代码进行预检验(Android 不需要此项)
 buildTypes {
        release {
            minifyEnabled true //true 表示进行Proguard 混淆
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

其中,proguard-android.txt是 Proguard 默认的混淆配置文件,位于Androdi SDK 的 tools/proguard 目录中,该文件是对Proguard 基本的配置,几乎是每个App 都要用到的配置,当然这个默认文件的配置是远远不够的。我们需要自身App 的特殊性增加或者减少相关的配置。

proguard-rules.pro 文件的编写

混淆文件的规则可以大致分为三种类型:

  • 公共的混淆规则:每个App 都通用的,主要是对Proguard 的基本配置,以及Android SDK 中 API 设置的规则,例如Avtivity 、Parcelable 等。

  • App 特有的混淆规则:根据App自身的特点进行设置,例如某些类会被反射调用,如果被混淆,那么反射就找不到了。

  • 第三方函数库或者SDK 的混淆规则:如果App 引入了第三方开源库或者SDK ,那么需要查看这些函数库或者SDK 的使用说明,将需要去混淆的地方加上去。

下面是一个比较完整的 proguard-rules.pro 文件的内容

#代码混淆的压缩比例,取值范围在0-7之间,默认为5
-optimizationpasses 5
#混淆时不使用大小写混合的方式,这样混淆后都是小写字母的组合
-dontusemixedcaseclassnames

#混淆时不做预校验,在Android 中一般不需要预校验,这样可以加快混淆的速度
-dontpreverify
#混淆时记录日志,同时会生成映射文件,
#Android Studio 中,生成的默认映射文件是
‘build/outputs/mapping/release/mapping.txt’
-verbose
#指定生成的映射文件的路劲和名称(生成原类名和混淆后的类名的映射文件)
-printmapping proguardMapping.txt
#指定混淆是采用的算法 ,参数是Google 官方推荐的过滤器算法
-optimizations !code/simplification/cast,!field/*,!class/merging/*
#如果项目中使用到注解,需要保护注解属性
-keepattributes *Annotation*
#不混淆泛型
-keepattributes Signature
#保留代码行号,这在混淆后代码运行中抛出异常信息时,有利于定位出错的代码
-keepattributes SourceFile,LineNumberTable
#保持 Android SDK 相关API 类不被混淆
-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 com.android.vending.licensing.ILicensingService
-keep public class * extends android.app.Fragment
-keep public class * extends android.view.View
#如果引用了v4或者v7包
-keep class android.support.** {*;}
#保持 native 方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}
#保留R类
-keep class **.R$* {
 *;
}
#保持自定义控件类不被混淆
-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
}
#保持自定义控件不被混淆
-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);
}
#保持 Activity 中参数是View 类型的函数,
#保证在 layout XML 文件中配置的 onClick 属性的值能够正常调用到
-keepclassmembers class * extends android.app.Activity{
    public void *(android.view.View);
}
#保持枚举 enum类不被混淆 如果混淆报错,
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
#保持 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;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}
#对于待有回调函数 onXXEvent 的,不能被混淆
-keepclassmembers class * {
    void *(**On*Event);
}

1.保留实体类和成员不被混淆

对于实体类,要保留它们的setget方法,对于boolean 型的get 方法,有人喜欢命名为isXXX的方式,所以不要遗漏了。

这是保留一个类和其成员不被混淆
这里写图片描述

一种好的做法是把所有实体都放在一个包下进行管理,这样只写一次混淆就够了(如下)。避免以后在别的包中新增的实体而忘记保留,代码在混淆后因为找不到相应的实体类而崩溃。

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值