Android混淆步骤,混淆结果查看以及混淆后出现的问题和衍生问题

今天说说Android原生的混淆,原生项目即将上线,伴随着整体功能的完成和完善,一些涉及安全和自我保护以及优化的问题被提到日程上。混淆恰恰能解决的就是我们的APP代码的保护以及优化的问题。

前言:
因为互联网环境的开放性和工具的便捷,我们很容易能将代码进行反编译以获取线上项目的源码。秉承对知识产权的尊重,我们在不侵犯他人权益的情况下,也需要对自己的劳动果实进行有效的保护,所以混淆能够帮助我们将自己的源码打散打乱,从而让外部的反编译获取的源码不知所云以达到自我保护的目的。同时因为混淆的过程是将源代码进行简化,比如方法名称会变成a、b、c等,以及一些其他的优化手段,所以也是能将项目缩小精炼已到达优化目的的工具。

环境:

Android SDK: compileSdkVersion 25   buildToolsVersion "25.0.1" 
IDE:Android Studio 使用Gradle

Android混淆步骤:
这部分涉及的内容网上一抓一大把,我只说说需要注意的地方:

  1. 因为使用的是Gradle,所以在混淆文件中类似这样的配置:
-libraryjars  libs/android-support-v4.jar 

不需要出现在你的混淆配置文件中,这个就是引入jar包,以便在混淆的时候通过keep排除第三方jar不被混淆以保证功能的正常使用。Gradle中是依靠build.gradle文件中的配置:

 compile 'com.android.support:appcompat-v7:25.0.1'

来指定依赖jar文件的,因此这一步其实已经做了,而且你在打开的IDE中是找不到jar的路径的,Gradle是默认下载到/Users/(用户名)/.gradle/caches/modules-2/files-2.1路径下的(MAC)。

  1. 加上屏蔽警告信息的配置,以免在Android Studio的Message中输出过多的黄色警告影响你的判断:
# 微信不混淆
-dontwarn com.tencent.**
-keep class com.tencent.** { *; }
  1. 第三方引用的类库,实体类,以及一些核心的底层组件都不要混淆,以免功能不能正常使用。

  2. 混淆文件的放置位置我展示一下,因为网上贴出开发目录的资料较少,这个容易让你晕,因为在你混淆不起作用或者出问题的情况下你会怀疑自己的混淆文件是不是放错地方了,所以我贴出来给你精神上的支持,你放的位置没错的!

这里写图片描述

  1. build.gradle文件中打开混淆:
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

附上我的混淆文件的片段供参考:

## ------------------------------------- 混淆基础配置 ---------------------------------------------
-optimizationpasses 5                               # 指定代码的压缩级别
-dontusemixedcaseclassnames                         # 混淆时不会产生形形色色的类名
-dontskipnonpubliclibraryclasses                    # 指定不去忽略非公共的库类
-dontskipnonpubliclibraryclassmembers               # 指定不去忽略包可见的库类的成员
-dontpreverify                                      # 不预校验
-ignorewarnings                                     # 屏蔽警告
-verbose                                            # 混淆时记录日志
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*    #优化
-keepattributes *Annotation*                        # 保护代码中的Annotation不被混淆
-keepattributes Signature                           # 避免混淆泛型, 这在JSON实体映射时非常重要
-keepattributes SourceFile,LineNumberTable          # 抛出异常时保留代码行号


# ------------------------------------- 不需要混淆的第三方类库 -------------------------------------
# Alipay过滤出去(来源于Alipay官网)
-keep class com.alipay.android.app.IAlixPay{*;}
-keep class com.alipay.android.app.IAlixPay$Stub{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
-keep class com.alipay.sdk.app.PayTask{public *;}
-keep class com.alipay.sdk.app.AuthTask{public *;}

# 微信过滤出去
-dontwarn com.tencent.**
-keep class com.tencent.** { *; }

# 不需要混淆android-support-v4.jar
-dontwarn android.support.v4.**
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment

##保留一个完整的包
-dontwarn org.jdom2.**
-keep class org.jdom2.** { *; }

#
# ------------------------------------- 不需要混淆的系统组件和本地实体类 ----------------------------
# 不混淆bean,** 换成具体的类名则表示不混淆某个具体的类
-dontwarn com.yourpackage.entity.**
-keep class com.yourpackage.entity.** { *; }

## Android底层组件和类不要混淆
-keep class **.R$*{*;}

# ------------------------------- 保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在------------
-keepclasseswithmembers class * {
    protected void init(android.content.Context, android.util.AttributeSet);
    protected abstract void init(android.content.Context, android.util.AttributeSet);
}

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
    public String getValue();
}

#-------------------------------  额外增加的 -----------------------------------------------------
# 保留所有的本地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.content.BroadcastReceiver
-keep public class * extends android.view.View

-keepclasseswithmembernames class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembernames class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

# 关闭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(...);
}

-keepclassmembers class * {
    void *(android.view.View);
        *** *Click*(...);
        *** *Event(...);
}

#高德地图(来源于高德地图开发平台)
#3D 地图
-dontwarn com.amap.api.**
-dontwarn com.a.a.**
-dontwarn com.autonavi.**
-keep class com.amap.api.**  {*;}
-keep class com.autonavi.**  {*;}
-keep class com.a.a.**  {*;}
-keep class com.amap.api.mapcore.**{*;}
-keep class com.amap.api.maps.**{*;}
-keep class com.autonavi.amap.mapcore.*{*;}

#定位
-keep class com.amap.api.location.**{*;}
-keep class com.loc.**{*;}
-keep class com.amap.api.fence.**{*;}
-keep class com.autonavi.aps.amapapi.model.**{*;}

# 搜索
-keep class com.amap.api.services.**{*;}

# 导航

-keep class com.amap.api.navi.**{*;}
-keep class com.autonavi.**{*;}

混淆后,可以使用key_store文件打出混淆的签名release包,通过AndroidStudio(以下简称AS)的图形化界面可以实现:

这里写图片描述

这里写图片描述

这里写图片描述

release包打出后会在AS右上角显示查看链接:

这里写图片描述

混淆结果查看:
通过上面的链接可以看到你已经打出了release的APK包,也就是带着自己签名的并且已经混淆后的能够正式上线的APK包,如下图:

这里写图片描述

下面说说混淆后APK包的查看,之所以要有意识查看这里面的内容,是因为如果混淆包安装在手机上后,一些功能如果出现问题,你可以到这里面来反推,是不是因为被混淆了而无法使用,因为这里面是最终代码的样子,你可以判断一些底层类或者你的实体类是否被混淆而导致功能异常。另外你也可以借此查看是不是达到了混淆的效果。

使用RAR在MAC上就可以解压APK包,解压后的目录是这个样子的:

这里写图片描述

可以看到classes.dex吗,这个文件中就存放了项目中所有混淆后的类,但是我们如果想将其反编译看到里面的具体内容需要用dex2jar工具先将dex转换成jar然后就能用反编译工具进行解压了。

dex2jar我用的是0.0.9.15,大小是1.7MB,(网上有一个大的,不好使,需要的同学可以留言,我会直接发给你),这是一个zip文件,在MAC上解压后通过命令:

sudo sh d2j-dex2jar.sh /Users/yourPath/app-release/classes.dex

执行后会在dex2jar工具的根目录下找到生成的jar包:

这里写图片描述

通过MAC下的反编译工具就能查看里面的内容。

混淆后出现的问题和衍生问题:
先抛出两个问题,这两个问题是我遇到的,可能有自己的独特性。其实混淆配置文件就是排除不能混淆的,遇到的问题应该不会太难,暂且以这两个问题抛砖引玉吧,其他的问题我暂时没有遇到。

  1. 登录页面的登录和注册按钮都不能点击了,点击之后没反应。原因应该就是混淆后,点击监听事件不能成功调用了。
    可以加入如下配置:
-keepclassmembers class * {
    void *(android.view.View);
        *** *Click*(...);
        *** *Event(...);
}
  1. 遇到的这个问题涉及到对’.so’文件的理解,因为项目中引用了高德地图,登录问题解决后,显示的主页面上应该有地图,但是结果是一片空白。证明地图引入已经出现了问题。几经反复和追忆,发现从输出中找问题是最直接的方法。由于生成加签名的APK包在手机中安装是不能显示地图的,所以就想到不打release包,打debug包生成的APK是什么样子的,如果地图能够正常显示,那么用两个APK包进行比对看看无法显示地图的正式签名APK包缺少了什么就可以了。于是我们打开生成的最终的APK包来看一下,显示如下:

这里写图片描述

左边是正式签名的APK包,无法显示高德地图;右边是debugAPK包可以正常显示地图;结果怎么样,不仅没有少而且出现问题的APK包还多了东西,这是怎么回事?

我们看到文件夹的名称是“arm64-v8a”等等,貌似是跟系统有关的东东。查找资料后发现,现在的Android系统支持七种不同的CPU架构,正是:ARMv8、ARMv5、ARMv7(从2010年起)、MIPS(从2012年起)、MIPS64、x86(从2011年起)和x86_64(从2014年起),而每一个CPU架构都会对应着一个ABI(Application Binary Interface)应用程序二进制接口,由它来定义二进制文件(.so文件)如何运行在相应的系统平台上,从使用的指令集、内存对齐到函数库等。

原生项目会使用NDK(Native Development Kit),它将生成.so文件。

当然这篇文章不是深入研究系统级开发的方方面面,而且这部分的东西相信如果深入理解会有很多意想不到的收获,但是就目前来讲,我还是先简单直接的解决眼前的问题,我想了解到这一步应该勉强够用了,我们来打开每个文件夹看看什么内容来提示我们解决这个问题:

这里写图片描述

我打开了armeabi-v7a文件夹,看到了两个没什么印象的so文件;
然后我又打开了arm64-v8a,因为高德地图在去年年底的时候支持了64位Anroid系统,当时引入过so文件,所以有些印象:

这里写图片描述

打开后发现:

这里写图片描述

我找到了我要的东西。
所以可以得到一个结论,by the way,我的测试手机是Galaxy On5(SM-G5500),所以测试机默认是要去到x86文件夹目录下查找要加载的.so文件,我打开x86文件夹如下:

这里写图片描述

同armeabi-v7a文件夹一样,没有我需要的高德地图的.so文件。
对于x86来说,当应用安装到这样的设备上时,它会先加载x86文件夹下的.so文件,如果不存在会找armeabi-v7a文件夹的.so文件,如果还不存在会找armeabi文件夹下的.so文件,因为x86回同时支持这两种架构。OK,在我的case中,x86文件夹下只有libjcore100.so和libwebpbackport.so,所以我需要的高德地图的.so没有在这里面,自然也不会被加载,而且它也不会继续向它所支持的另外两个文件夹下加载.so文件了,所以,地图无法显示。

说了这么多,大家应该大概了解了Android的这个底层工作方式了。其实,回看这个问题,跟混淆没有太大关系,只是说由于不了解Android的一些系统级别的常识,导致一些配置只知其然不知其所以然,对比我的build.gradle文件就可以发现:

buildTypes {
    debug {
        ndk{
            abiFilters "armeabi","arm64-v8a"
        }
        signingConfig signingConfigs.release
    }
    release {
        minifyEnabled true
        shrinkResources true
        signingConfig signingConfigs.release
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }

}

NDK只在debug中配置了,release中并没有配置。这块配置就是显示的指定支持的ABI,x86不支持arm64-v8a,但是它支持armeabi啊,打开armeabi文件夹可以看到:

这里写图片描述

完美的展现了我需要的高德地图的两个.so文件。所以加入NDK配置到release打包配置后,进行打包,文件夹只剩下两个指定的了:

这里写图片描述

这里写图片描述

运行APP,地图可以展现,没有问题。
不过大家不要忘记高德地图官网放出的混淆配置,见上面的配置片段。这些配置才是真正的避免高德地图被混淆而无法正常在APP中使用的保证。

以上就是我做混淆过程中遇到的问题,混淆不难,但是编写的过程中确实遇到了一些问题,以及一些衍生的问题,程序开发需要我们静下心来研究琢磨,但是往往时间资源的稀缺,需要我们短平快的解决问题,但是不要忘记“日理万机”之后一定要回过头来弄清楚,至少能比表面更深入一个档位,以便逐步提高自己的能力和技术水平。

OK,有时间再接着聊,谢谢大家能看到这里。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值