android 防破解, 代码混淆,代码保护

代码混淆

一般情况下,Android 的 gradle 中都会默认写着:

1 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

默认 Proguard 文件:官方自带的混淆规则文件路径:proguard-android.txt。

这个默认文件中帮我们声明了许多混淆规则内容,包括:keep 所有继承自 View 的类,keep 所有继承自 Activity 的类,keep 所有 JavascriptInterface、native 方法声明,以及 keep 一些注解了 @Keep 的内容。

所以默认情况下,即使一条规则都没有加入,自定义 View 和 Activity 都被保留下来,至少类名都没有被混淆。

那么为什么官方默认会帮我们写下这些?为什么 View 和 Activity 默认情况下应该被保留呢?

简单来说,因为 Proguard 原本是为 Java 打造的,它无法搜索到我们 AndroidManifest、布局等文件中引用了哪些 Java 类,因此如果 Java 代码变了而 XML 文件中的引用没变,就会造成反射失败。所以这些被 XML 使用到的类需要 keep 住。

(1)对于View 和 Activity混淆问题:

饿了么 的团队提供了一个鲜为人知的 gradle 插件 用来无伤混淆 Activity 和 View,这个项目叫 Mess:https://github.com/eleme/Mess

具体内容各位可以稍后自行去阅读其文档和教程,链接最后都还会附于末尾。简单来说,Mess 弥补了 Proguard 不能检索 XML 文件的缺点,帮 Proguard 完成了 Activity 和 View 的改名及 mapping。

(2)资源文件的混淆问题:

Proguard 完全不会管我们的资源文件,因此如果资源文件名没有做保护的话,很容易被顺藤摸瓜找到关联的 Java 代码,对此,微信团队提供了一个好用的资源混淆工具,它不仅能帮你全面混淆资源文件,还能帮你缩减资源文件的整体体积,工具叫 AndResGuard,开源地址:https://github.com/shwenzhang/AndResGuard

开启混淆:

 1 release {
 2     debuggable false
 3     //对齐
 4     zipAlignEnabled true
 5     //移除无用的resource 文件
 6     shrinkResources true
 7     // 混淆
 8     minifyEnabled true
 9     signingConfig signingConfigs.release
10     proguardFile getDefaultProguardFile('proguard-android.txt')
11     proguardFile 'proguard-rules.pro'
12 }

(3)Proguard rules

-repackageclasses

-repackageclasses + 包名 这种做法存在混淆 bug,而默认 -repackageclasses 不加包名不会出现 bug,所以初次使用此法需要进行测试。

-obfuscationdictionary

-obfuscationdictionary 后面加一个纯文本文件路径,它的作用是指定一个字典文件作为混淆字典。默认情况下我们的代码命名会被混淆成 abcdefg... 字母组合的内容,需要修改可以使用这个配置项将字典修改成更加可怕的内容,乱花渐欲迷人眼的效果。

Android 混淆字典文件

-assumenosideeffects

在 release 混淆过程中删除 Log 代码,使用 -assumenosideeffects 这个配置项可以帮我们在编译成 APK 之前把日志代码全部删掉,这么做有助于提升性能,而且日志代码往往会保留很多我们的意图和许多可被反编译的字符串:

1 -assumenosideeffects class android.util.Log {
2     public static boolean isLoggable(java.lang.String, int);
3     public static int d(...);
4     public static int w(...);
5     public static int v(...);
6     public static int i(...);
7 }

注:assumenosideeffects 需要启用代码优化才能生效

Proguard File:

 1 # 代码混淆压缩比,在0~7之间,默认为5,一般不下需要修改
 2 -optimizationpasses 5
 3 
 4 # 混淆时不使用大小写混合,混淆后的类名为小写
 5 # windows下的同学还是加入这个选项吧(windows大小写不敏感)
 6 -dontusemixedcaseclassnames
 7 
 8 # 指定不去忽略非公共的库的类
 9 # 默认跳过,有些情况下编写的代码与类库中的类在同一个包下,并且持有包中内容的引用,此时就需要加入此条声明
10 -dontskipnonpubliclibraryclasses
11 
12 # 指定不去忽略非公共的库的类的成员
13 -dontskipnonpubliclibraryclassmembers
14 
15 # 不做预检验,preverify是proguard的四个步骤之一
16 # Android不需要preverify,去掉这一步可以加快混淆速度
17 -dontpreverify
18 
19 # 有了verbose这句话,混淆后就会生成映射文件
20 # 包含有类名->混淆后类名的映射关系
21 # 然后使用printmapping指定映射文件的名称
22 -verbose
23 -printmapping priguardMapping.txt
24 
25 # 指定混淆时采用的算法,后面的参数是一个过滤器
26 # 这个过滤器是谷歌推荐的算法,一般不改变
27 -optimizations !code/simplification/artithmetic,!field/*,!class/merging/*
28 
29 # 保护代码中的Annotation不被混淆
30 # 这在JSON实体映射时非常重要,比如fastJson
31 -keepattributes *Annotation*
32 
33 # 避免混淆泛型
34 # 这在JSON实体映射时非常重要,比如fastJson
35 -keepattributes Signature
36 
37 # 抛出异常时保留代码行号
38 -keepattributes SourceFile,LineNumberTable

要Keep的:

 1 # 保留所有的本地native方法不被混淆
 2 -keepclasseswithmembernames class * {
 3     native <methods>;
 4 }
 5 
 6 # 保留了继承自Activity、Application这些类的子类
 7 # 因为这些子类有可能被外部调用
 8 # 比如第一行就保证了所有Activity的子类不要被混淆
 9 -keep public class * extends android.app.Activity
10 -keep public class * extends android.app.Application
11 -keep public class * extends android.app.Service
12 -keep public class * extends android.content.BroadcastReceiver
13 -keep public class * extends android.content.ContentProvider
14 -keep public class * extends android.app.backup.BackupAgentHelper
15 -keep public class * extends android.preference.Preference
16 -keep public class * extends android.view.View
17 -keep public class com.android.vending.licensing.ILicensingService
18 
19 # 如果有引用android-support-v4.jar包,可以添加下面这行
20 -keep public class com.null.test.ui.fragment.** {*;}
21 
22 # 保留Activity中的方法参数是view的方法,
23 # 从而我们在layout里面编写onClick就不会影响
24 -keepclassmembers class * extends android.app.Activity {
25     public void * (android.view.View);
26 }
27 
28 # 枚举类不能被混淆
29 -keepclassmembers enum * {
30     public static **[] values();
31     public static ** valueOf(java.lang.String);
32 }
33 
34 # 保留自定义控件(继承自View)不能被混淆
35 -keep public class * extends android.view.View {
36     public <init>(android.content.Context);
37     public <init>(android.content.Context, android.util.AttributeSet);
38     public <init>(android.content.Context, android.util.AttributeSet, int);
39     public void set*(***);
40     *** get* ();
41 }
42 
43 # 保留Parcelable序列化的类不能被混淆
44 -keep class * implements android.os.Parcelable{
45     public static final android.os.Parcelable$Creator *;
46 }
47 
48 # 保留Serializable 序列化的类不被混淆
49 -keepclassmembers class * implements java.io.Serializable {
50    static final long serialVersionUID;
51    private static final java.io.ObjectStreamField[] serialPersistentFields;
52    !static !transient <fields>;
53    private void writeObject(java.io.ObjectOutputStream);
54    private void readObject(java.io.ObjectInputStream);
55    java.lang.Object writeReplace();
56    java.lang.Object readResolve();
57 }
58 
59 # 对R文件下的所有类及其方法,都不能被混淆
60 -keepclassmembers class **.R$* {
61     *62 }
63 
64 # 对于带有回调函数onXXEvent的,不能混淆
65 -keepclassmembers class * {
66     void *(**On*Event);
67 }
针对App的量身定制

1.保留实体类和成员不被混淆
  对于实体,要保留它们的get和set方法,对于boolean型get方法有的命名是isXXX类型,不要遗漏

 1 -keep class com.null.test.entities.** {
 2     //全部忽略
 3     *;
 4 }
 5 -keep class com.null.test.entities.** {
 6     //忽略get和set方法
 7     public void set*(***);
 8     public *** get*();
 9     public *** is*();
10 }
11 //以上两种任意一种都行

2.内嵌类
  内嵌类经常容易被混淆,结果调用的时候为空就崩溃了。最好的办法就是不用内嵌类(有点扯淡),如果MainActivity中使用了,就用如下代码

1 -keep class com.null.test.MainActivity$* {
2     *;
3 }

注:$这个符号就是用来分割内嵌类与其母体的标志

3.对于自定义类库的混淆处理
  对于自定义的类库,我们一般是保留类和类的成员。

4.对WebView的处理
  如果项目中用到了WebView的复杂操作,请加入以下代码:

1 -keepclassmembers class * extends android.webkit.WebViewClient {
2     public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
3     public boolean *(android.webkit.WebView, java.lang.String);
4 }
5 -keepclassmembers class * extends android.webkit.WebViewClient {
6     public void *(android.webkit.WebView, java.lang.String);
7 }

5.对JavaScript的处理

1 -keepclassmembers class com.null.test.MainActivity$JSInterfacel {
2     <methods>;
3 }

 

Smali:

其实我们主要关注的是 smali 这个目录,里面是按照 android 程序编写的时候 java 文件的目录格式生成的,但是里面的文件并不是 java 格式的,而是 smali 格式的,类似 MainActivity.smali。

那么什么是 smali 文件呢?

  1. Smali 是 Android 的 Dalvik 虚拟机所使用的一种 dex 格式的中间语言

  2. 可以理解为,C 语言和汇编语言的编译与反编译,把 smali 理解为一种汇编语言

我们可以打开一个 smali 文件看看,我们可以使用 notepad++ 打开,然后定一下 smali 语法的高亮显示

可以参看 http://www.ourunix.org/post/1... 操作。

打开 MainActivity.smali 文件,头三行代码大致如下:

1 .class public Lcom/example/hacktest/MainActivity;
2 .super Landroid/app/Activity;
3 .source "MainActivity.java"

smali 语法这里就不介绍了,自己查资料就好 smali 文件语法参考 http://my.oschina.net/xiahuaw

google 最早给的就是代码混淆的方案,其实一般的混淆只是降低了代码的可读性,让你对反编译出来的函数命名等不知道什么意思,不过解读出来只是时间问题。后来还有资源混淆的,但是意义不大。

后来有了核心代码用 C 实现,写成 SO,加花指令的办法,这个办法确实会阻止一大部分人的继续破解,但是对于经常做逆向的工程师来说也不是什么难题。

其实做这么多大多数软件的初衷就是不想软件被盗版,然后被注入乱七八糟的广告,或者被盗取信息等,后来就有了盗版检测机制。比如:JAVA 层的签名校验,NDK 层校验,分段存放签名 Hash 串,服务器校验等等,但是这些方法我都在上面说了破解方法

现在国内的一般应用市场都有对 APP 签名的检测,在你下载的时候会告诉你这个 APP 是不是盗版的,从而让用户区分出来。但是应用市场自己本身又被盗版了怎么办呢?

再后来,就有了加固等产品,so 做了加密,真正的 dex 也藏起来了,不过个人觉得,就算真正的 dex 也需要变成 odex 了,root 的手机取到 odex,再转回 dex,就能拿到真正的 dex(虽然我没试过,但是我觉得可能是一个思路),所以这个方法就更难破解了

虽然加固产品很厉害,但是也会有他的缺陷,Android 系统不断的更新升级,也许就换了某些模式等等,比如 ART 刚出来的时候,加固保加固后的 Apk,在 ART 模式运行下就会 Crash。这些加固产品要不断的适配各种型号的手机,CPU 类型,运行模式等等,所以很多 APP 为了考虑兼容性,他们也不会轻易去加固自己的产品。

应用加固:

1. 阿里聚安全 链接:http://jaq.alibaba.com/

  • 上传应用
  • 提供安全扫描(漏洞扫描、恶意代码扫描、仿冒应用扫描)
  • 可以从结果知道漏洞总数,如果是认证过的开发者,可以直接得知漏洞的具体位置。其中还有部分漏洞需要付费扫描。
  • 然后我们可以进行应用加固,其中分快速加固和多渠道加固,可以按需选择
  • 加固包下载(应用需要重新签名)

2. 腾讯云应用乐固 链接:https://www.qcloud.com/product/cr

  • 上传应用
  • 默认服务类型:
    1. 应用加固
    2. 漏洞检测
    3. 渠道监控
  • 可选服务类型:
    1. 适配分析(每天可以有一次)
    2. 质量跟踪(即接入腾讯的bugly进行异常追踪)
  • 之后我们可以得到应用的基本信息、加固包、缺陷分析、应用检测
  • 选择下载加固包(应用需要重新签名)的同时还提供了签名及多渠道打包工具、自动加固工具的下载使用

3. 360加固保 链接:http://jiagu.360.cn/

  • 上传应用
  • 提供加固基础服务:
    1. DEX文件加密
    2. 防二次打包
    3. APK大小优化
    4. 防DEX内存截取
    5. 应用盗版检测
    6. 加固数据分析服务
  • 可选增强服务:
    1. 崩溃日志分析(即接入360的bug追踪)
    2. 支持x86框架(约增大apk大小400k左右)
    3. 应用升级通知(一键接入增量更新,约增大apk大小20k左右,详情地址:http://jiagu.360.cn/qcmshtml/details.html#update)
  • 选择下载加固包(应用需要重新签名)的同时还提供了签名工具、加固工具(链接:http://jiagu.360.cn/qcmshtml/details.html#helper)的下载使用

4. 梆梆加固 链接:https://dev.bangcle.com/

  • 上传应用
  • 提供功能:安全评估(更多定制化的评估,需联系客户并且收费)、应用加固(认证过的开发者还提供:报告下载,多渠道打包)
  • 评估结果提供了风险的详情,位置及解决方案
  • 提供对加固后的包再进行快速评估
  • 选择下载加固包(应用需要重新签名)的同时还提供了加固工具的下载使用

5. 爱加密 链接:http://safe.ijiami.cn/

  • 上传应用并直接执行加密

    需要注意其中有一个选项:防止二次打包,请按需选择方式
    1:加密后的apk不允许任何人修改方式
    2: 加密后的apk只允许应用所有和修改

  • 可以对加固后的包申请渠道检测,除了加固包的下载,还提供了签名工具的下载

  • 使用智游爱加密服务的开发者APP,并已成功提交到Google 官方应用市场并上架的,可得到智游官方网站应用的下载推荐

总结:

体积(体积小的为优):360 > 腾讯 > 爱加密 > 阿里 > 梆梆

兼容性: 阿里 > 腾讯 > 360 = 梆梆 > 爱加密

启动速度(时间短为优): 阿里 > 爱加密 > 360 = 梆梆 > 腾讯

漏洞: 腾讯 > 爱加密 > 360 > 梆梆 > 阿里

app加固的好处是进一步保护了自己的核心代码,提升了盗版的难度,但同时也影响的应用的兼容性,程序的执行效率,还有部分的市场会拒绝加壳后的应用上架。所以请大家结合自身的情况来选择。

引用:

逆向:

手把手教你逆向分析 Android 程序

Android逆向随笔之遇见MultiDex

Android反编译工具的使用-Android Killer

混淆保护

美团Android资源混淆保护实践

安装包立减1M--微信Android资源混淆打包工具

Android 混淆那些事儿(Bugly)

微信Android资源混淆工具(Github ReadMe)

Android 高级混淆和代码保护技术

Android 密钥保护和 C/S 网络传输安全理论指南

posted @ 2018-01-25 11:47  晕菜一员  阅读(2912)  评论(0编辑  收藏