转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/69388246
本文出自【赵彦军的博客】

文章目录

  • 混淆的基本概念
  • 开启混淆
  • 混淆规则理解
    • 1、有一些固定的混淆规则不需要更改:
    • 2、理解通配符
    • 3、保证指定包名下的所有类及子包中所有的类不被混淆
    • 4、保证指定的类不被混淆
    • 5、不混淆指定类的子类
    • 6、指定接口不混淆
    • 7、指定接口的实现类不混淆
    • 8、指定类的内部类不混淆
    • 9、构造函数不混淆
    • 10、指定类的属性和方法不被混淆
    • 11、不混淆类中所有的 public 方法
    • 12、不混淆类中所有的 public 字段
    • 13、不混淆构造函数
    • 13、不混淆 bean 对象里面的 set 、get 方法
  • 常见不混淆的类和属性
  • 混淆后的项目目录资源
  • 实战1
  • aar自动混淆
  • 参考资料

混淆的基本概念

  • 什么是混淆?

代码混淆亦称花指令,是将计算机程序的代码,转换成一种功能上等价,但是难于阅读和理解的形式的行为。

  • 混淆的目的

1、混淆的目的是为了加大反编译的成本,但是并不能彻底防止反编译.

2、压缩apk 资源文件

开启混淆

一般我们做项目的时候,都是分为 release 和 debug 版本,release 版本混淆,debug 版本不混淆。设置如下:

 buildTypes {release {minifyEnabled trueproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}debug {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}

混淆规则理解

1、有一些固定的混淆规则不需要更改:

#指定代码的压缩级别
-optimizationpasses 5#包明不混合大小写
-dontusemixedcaseclassnames#不去忽略非公共的库类
-dontskipnonpubliclibraryclasses#优化  不优化输入的类文件
-dontoptimize#预校验
-dontpreverify#混淆时是否记录日志
-verbose# 混淆时所采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*#保护注解
-keepattributes *Annotation*

2、理解通配符

-keep class cn.hadcn.test.**
-keep class cn.hadcn.test.*

很多同学都搞不懂下面两个的区别,一颗星表示只是保持该包下的类名,而子包下的类名还是会被混淆;两颗星表示把本包和所含子包下的类名都保持;用以上方法保持类后,你会发现类名虽然未混淆,但里面的具体方法和变量命名还是变了,这时如果既想保持类名,又想保持里面的内容不被混淆,我们就需要以下方法了:

-keep class cn.hadcn.test.* {*;}

3、保证指定包名下的所有类及子包中所有的类不被混淆

  • 例子1:com.lib.manager 包下面的类不混淆
-keep class com.lib.manager.**{*;}
  • 例子2:v4包下面的所有类不混淆
-keep class android.support.v4.** { *; }

4、保证指定的类不被混淆

  • 例子1:YiBaWiFiActivity 类不被混淆, YiBaWiFiActivity 完整包名:com.yiba.wifi.sdk.lib.activity.YiBaWiFiActivity
-keep class com.yiba.wifi.sdk.lib.activity.YiBaWiFiActivity{*;}
  • 例子2:v4 包下面的 ActivityCompat 类不被混淆
-keep class android.support.v4.app.ActivityCompat{*;}

5、不混淆指定类的子类

-keep class * extends pp.lib.User { *; }

User 类的子类不混淆

6、指定接口不混淆

Callback 是一个接口,完整包名为:com.lib.impl.Callback

-keep interface com.lib.impl.Callback{ * ; }

7、指定接口的实现类不混淆

Callback 是一个接口,完整包名为:com.lib.impl.Callback . 所有的实现类都不会混淆。

-keep class * implements com.lib.impl.Callback { *; }

8、指定类的内部类不混淆

PhoneUtil 的源码如下:

package com.lib.manager;
import android.content.Context;
import android.os.AsyncTask;/*** Created by yiba_zyj on 2017/4/5.*/public class PhoneUtil {public final static int APPID = 100 ;public PhoneUtil( Context context ){}class MyTask extends AsyncTask {@Overrideprotected Object doInBackground(Object[] params) {return null;}}
}

PhoneUtil 有一个常量、一个构造函数、一个内部类.

混淆规则:

-keep class com.lib.manager.PhoneUtil$* {* ;
}

混淆后的结果如下:

可以看到混淆后的 PhoneUtil ,常量和 构造函数 都被移除了,只留下了 内部类 MyTask 。

9、构造函数不混淆

PhoneUtil 的源码如所示:


package com.lib;/*** Created by yiba_zyj on 2017/4/7.*/public class PhoneUtil {public static final String aa = "1222" ;public void run(){}}

混淆规则:PhoneUtil 的无参构造函数不混淆 。PhoneUtil 里面没有用到的 属性和方法将会被移除。

-keep class com.lib.PhoneUtil {public <init>();
}

那么有参的构造函数的混淆规则是怎么样的?

混淆规则:PhoneUtil 的 参数为 Context 的构造函数不混淆

-keep class com.lib.PhoneUtil {
public <init>( android.content.Context );
}

10、指定类的属性和方法不被混淆

  • 原始类 PhoneUtil 代码如下,完整的包名为:com.lib.manager.PhoneUtil
package com.lib.manager;import android.content.Context;
import android.util.DisplayMetrics;
import android.view.WindowManager;/*** Created by yiba_zyj on 2017/4/5.*/public class PhoneUtil {public static final int AppID = 100 ;/*** 获得屏幕高度* @param context* @return*/public static int getScreenWidth(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);DisplayMetrics outMetrics = new DisplayMetrics();wm.getDefaultDisplay().getMetrics(outMetrics);return  outMetrics.widthPixels;}/*** 获得屏幕宽度* @param context* @return*/public static int getScreenHeight(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);DisplayMetrics outMetrics = new DisplayMetrics();wm.getDefaultDisplay().getMetrics(outMetrics);return  outMetrics.heightPixels;}}

PhoneUtil 类中包含1个常量及2个方法.

  • 类中的变量不被混淆:
-keep class com.lib.manager.PhoneUtil{
public static final int AppID ;
}
  • 混淆后的效果如下:
package com.lib.manager;public class PhoneUtil {public static final int AppID = 100;public PhoneUtil() {}
}

可以看到混淆后的类中,AppID 常量被保存下来了,getScreenWidthgetScreenHeight 这两个方法被移除了。 为什么会有方法被移除? 因为在混淆时,Android 会默认移除没有使用的的方法和资源,保证apk 合理的被压缩。

  • 保留类中的方法不被混淆
-keep class com.lib.manager.PhoneUtil{public static int getScreenWidth(android.content.Context);}
  • 混淆后的方法如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//package com.lib.manager;import android.content.Context;
import android.util.DisplayMetrics;
import android.view.WindowManager;public class PhoneUtil {public PhoneUtil() {}public static int getScreenWidth(Context var0) {WindowManager var1 = (WindowManager)var0.getSystemService("window");DisplayMetrics var2 = new DisplayMetrics();var1.getDefaultDisplay().getMetrics(var2);return var2.widthPixels;}
}

11、不混淆类中所有的 public 方法

# <methods> 匹配所有的方法-keep class cn.hadcn.test.One {public <methods>;
}

表示One类下的所有public方法都不会被混淆

12、不混淆类中所有的 public 字段

-keep class pp.lib.PhoneUtil{public <fields> ;
}

表示 PhoneUtil 类中所有的 public 属性将保留,其他类型的字段

13、不混淆构造函数


-keep class pp.lib.PhoneUtil{<init>(***) ;<init>(*** , *** ) ;
}

不混淆 PhoneUtil 类中所有 一个参数 和 两个参数的 的构造函数。

13、不混淆 bean 对象里面的 set 、get 方法

User 对象如下图所示:

package pp.lib;/*** Created by ${zhaoyanjun} on 2017/4/10.*/public class User {private String name ;private String age ;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}
}

混淆规则:不混淆 User 类中所有的 set 和 get 方法,*** 代表 通配符

-keep class pp.lib.User{void set*( *** ) ;*** get*() ;
}

常见不混淆的类和属性

  • 不混淆四大组件 和 Application

因为四大组件和 Application 需要在 AndroidManifest.xml 中注册,不能混淆,否则就会报类找不到异常。

-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

特殊情况:

如果 BroadcastReceiver 是动态注册的,则是可以加入混淆的。

  • 不混淆任何包含native方法的类的类名以及native方法名
-keepclasseswithmembernames class * {native <methods>;
}
  • 不混淆任何一个View中的setXxx()和getXxx()方法,因为属性动画需要有相应的setter和getter的方法实现,混淆了就无法工作了。
-keepclassmembers public class * extends android.view.View {void set*(***);*** get*();
}
  • 不混淆Activity中参数是View的方法,因为有这样一种用法,在XML中配置android:onClick=”buttonClick”属性,当用户点击该按钮时就会调用Activity中的buttonClick(View view)方法,如果这个方法被混淆的话就找不到了。
-keepclassmembers class * extends android.app.Activity {public void *(android.view.View);
}
  • 不混淆枚举中的values()和valueOf()方法
-keepclassmembers enum * {public static **[] values();public static ** valueOf(java.lang.String);
}
  • 不混淆Parcelable实现类中的CREATOR字段,毫无疑问,CREATOR字段是绝对不能改变的,包括大小写都不能变,不然整个Parcelable工作机制都会失败。
-keepclassmembers class * implements android.os.Parcelable {public static final android.os.Parcelable$Creator CREATOR;
}
  • 不混淆R文件中的所有静态字段,我们都知道R文件是通过字段来记录每个资源的id的,字段名要是被混淆了,id也就找不着了。
-keepclassmembers class **.R$* {public static <fields>;
}

混淆后的项目目录资源

混淆完以后会在 build/outputs/mapping/release 目录下生成4个文件。如下图所示:

  • mapping.txt :代表源码的 包名/类名/变量名/方法名 和混淆后 的转换关系。

混淆规则

-keep class com.lib.manager.PhoneUtil{public static int getScreenWidth(android.content.Context);}

混淆后的 mapping 文件如下图所示:

从上面的 mapping 文件可以看出:

PhoneUtil 转换为 PhoneUtil , 名字没有发生变化,相当于没有混淆。

getScreenWidth 转换为 getScreenWidth , 名字没有发生变换,相当于没有混淆。

getScreenHeight 转换位 a , 名字已经变换了,增加了反编译的难度。

混淆后 PhontUtil 源码如下图所示:

  • usage.txt : 代表本次混淆过程中被移除的类或者方法。

举例说明

从上图中可以看出,本次混淆过程中 , 移除了很多没用的类。

com.lib.BuildConfig   //移除了 com.lib 包下的 BuildConfig 类
com.lib.activity.A    //移除了 com.lib.activity 包下的 A 类
com.lib.manager.PhoneUtil:public static final int AppID  //移除了 com.lib.manager 包下的 PhoneUtil 类 中的 AppID 常量 com.lib.manager 包下的 PhoneUtil 类 中的 getScreenHeight 方法 。
com.lib.manager.SS       //移除了 com.lib.manager 包下的 SS 类
com.lib.manager.Util     //移除了 com.lib.manager 包下的 Util 类
com.lib.manager.hj.PP    //移除了 com.lib.manager.hj 包下的 PP 类
com.lib.manager.hj.PP$1

实战1

User 类源码:

package pp.lib;/*** Created by ${zhaoyanjun} on 2017/4/10.*/public class User {private String name ;private String age ;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}
}

User1 类源码:

package pp.lib;/*** Created by ${zhaoyanjun} on 2017/4/10.*/public class User1 extends  User {private String id ;public void run(){}}

混淆规则:

-keep class * extends pp.lib.User { *; }

混淆结果: User 类的 类名已经被混淆了,字段没有混淆

User1 类,类名和字段都没有混淆

aar自动混淆

解决第三方SDK的混淆配置管理问题.

尽管有些SDK提供方非常靠谱的给了混淆规则, 但是毕竟要添加到自己项目的proguard配置里, 以后如果混淆规则改了, 配置还得同步改, 对于那些用maven仓库管理的SDK, 非常不友好.

也许有人注意到了, Android归档文件, 也就是俗称的aar文件里可以携带一个proguard.txt文件, 这似乎表明aar文件可以自带混淆配置.

事实也是如此, aar在构建时, 可以通过consumerProguardFiles属性指定一个proguard配置, 这个配置会被打入aar, 它和proguardFiles属性指定的proguard配置不同, proguardFiles是用于构建aar的混淆规则, consumerProguardFiles则是aar的接入方在构建时会使用的混淆规则.

第一步定义两个 .pro 文件 proguard-rules.pro, consumer-rules.pro

proguard-rules.pro : 输出aar 时需要的混淆规则,一般是 keep 暴露给接入方的类、方法、接口

consumer-rules.pro:接入方输出 apk 时需要的混淆规则,一般是 keep 一些不能混淆的 bean 对象。

最后一步:在 aar 的 build.gradle 中添加 consumerProguardFiles 属性

android {defaultConfig {...consumerProguardFiles 'consumer-rules.pro'}buildTypes {release {minifyEnabled trueproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}
}

最后输出的 aar 包会带有 consumer-rules.pro 文件,最后会作用于构建 apk 的混淆过程中。

参考资料

  • 腾讯 Bugly Android 混淆那些事儿

  • 常用的反编译工具 http://git.oschina.net/zyj1609/AndroidReverseProject)

  • Android安全攻防战,反编译与混淆技术完全解析(上)

  • Android安全攻防战,反编译与混淆技术完全解析(下)

  • 知乎:关于混淆的思考

Android 混淆详解相关推荐

  1. Android混淆详解

    综述 毫无疑问,混淆是打包过程中最重要的流程之一,在没有特殊原因的情况下,所有 app 都应该开启混淆. 首先,这里说的的混淆其实是包括了代码压缩.代码混淆以及资源压缩等的优化过程.依靠 ProGua ...

  2. Android签名详解(debug和release)

    Android签名详解(debug和release) 1. 为什么要签名 1) 发送者的身份认证 由于开发商可能通过使用相同的Package Name来混淆替换已经安装的程序,以此保证签名不同的包不被 ...

  3. android rotate 动画,Android RotateAnimation详解

    RotateAnimation旋转坐标系为以旋转点为坐标系(0,0)点.x轴为0度,顺时针方向旋转一定的角度. 1.RotateAnimation(fromDegrees, toDegrees) [默 ...

  4. 【转】Android菜单详解——理解android中的Menu--不错

    原文网址:http://www.cnblogs.com/qingblog/archive/2012/06/08/2541709.html 前言 今天看了pro android 3中menu这一章,对A ...

  5. Android菜单详解——理解android中的Menu

    前言 今天看了pro android 3中menu这一章,对Android的整个menu体系有了进一步的了解,故整理下笔记与大家分享. PS:强烈推荐<Pro Android 3>,是我至 ...

  6. Android LayoutInflater详解

    Android LayoutInflater详解 在实际开发中LayoutInflater这个类还是非常有用的,它的作用类 似于findViewById().不同点是LayoutInflater是用来 ...

  7. android Fragments详解

    android Fragments详解一:概述 android Fragments详解二:创建Fragment 转载于:https://my.oschina.net/liangzhenghui/blo ...

  8. android WebView详解,常见漏洞详解和安全源码(下)

    上篇博客主要分析了 WebView 的详细使用,这篇来分析 WebView 的常见漏洞和使用的坑.  上篇:android WebView详解,常见漏洞详解和安全源码(上)  转载请注明出处:http ...

  9. android WebView详解,常见漏洞详解和安全源码(上)

    这篇博客主要来介绍 WebView 的相关使用方法,常见的几个漏洞,开发中可能遇到的坑和最后解决相应漏洞的源码,以及针对该源码的解析.  由于博客内容长度,这次将分为上下两篇,上篇详解 WebView ...

最新文章

  1. SAP MM 盘点凭证上的posting block
  2. php笔记之echo/print比较
  3. nodejs 防宕机_pm2实战,让你的nodejs、koa2应用永远不会宕机
  4. python队列长度_[python模块]队列queue
  5. mini2440_x35 使用minicom进行连接,传送文件
  6. 【elasticsearch】跨集群搜索
  7. 自主招生计算机系面试,自主招生笔试和面试,你准备好了吗?
  8. 第八届蓝桥杯第一题购物单
  9. python是干嘛的-python到底拿来干什么
  10. 0-1背包问题(需要输出具体背包序号)
  11. ajax执行成功后,在success回调函数中把后台返回的list还原到html的table中
  12. 优雅降级实现IE8的transform平移属性
  13. JSON 在线格式化工具感觉挺好用的
  14. 期货市场倒挂什么意思(期货市场倒挂什么意思啊)
  15. 众筹系统源码 java_以太坊 众筹系统
  16. 计算机专业校招能去哪哪里,BAT三大互联网巨头最喜欢去哪些学校招人?
  17. 09_Pandas从多个条件(AND,OR,NOT)中提取行
  18. PyCharm SyntaxError: Non-UTF-8 code starting with \xbb 处理
  19. 配置属于你的iterm
  20. vue 渲染的list 数据交换顺序,简单就可以实现动画效果

热门文章

  1. linux xia 安装程序,linux更新或安装libzip
  2. kcbzps oracle_快速进行Oracle安装及配置
  3. python dicom 器官分割_图像识别 | 使用Python对医学Dicom文件的预处理(含代码)
  4. python typing typescript_将 python 数据转化为 TypeScript 格式
  5. 六十八、完成Vue项目推荐和周末游组件,并使用Ajax发起ajax请求
  6. 最后一篇,小白看的Python基础教程,详细得很(十一)
  7. 化工原理期中考,流体
  8. keras从入门到放弃(二十二)一维卷积处理 RNN文本分类
  9. node mysql limit_node中mysql连接池的connectionLimit指什么,它和mysql的最小连接数和最大连接数的关系是什么?...
  10. 微信小程序实现文件下载 以及微信小程序保存Excel