0.前言

在上一篇Androd安全——反编译技术完全解析中介绍了反编译方面的知识,因此我们认识到为了安全我们需要对代码进行混淆。

混淆代码并不是让代码无法被反编译,而是将代码中的类、方法、变量等信息进行重命名,把它们改成一些毫无意义的名字。因为对于我们而言可能Cellphone类的call()方法意味着很多信息,而A类的b()方法则没有任何意义,但是对于计算机而言,它们都是平等的。

所以说混淆代码可以在不影响程序正常运行的前提下让破解者很头疼,从而大大提升了程序的安全性。

1.   混淆的使用

在Android Studio中借助SDK中自带的Proguard工具,如下所示,只需要修改build.gradle中minifyEnabled的值为true即可,这样Build->Generate Signed APK打出来的APK包就是混淆过的,当然,Debug版的APK是不会混淆的。

  1. release {
  2. minifyEnabled true //设置是否启用混淆
  3. //用于选定混淆配置文件
  4. proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
  5. }

2.  默认的混淆规则

2.1  普通工具类

下面是一个非常普通的工具类,没有任何继承关系。Utils中有两个方法内部逻辑一样,唯一的据别是稍后methodNormal()方法会被调用,而methodUnused()方法不会被调用。

  1. public class Utils {
  2. public void methodNormal() {
  3. String logMessage = "this is normal method";
  4. logMessage = logMessage.toLowerCase();
  5. System.out.println(logMessage);
  6. }
  7.  
  8. public void methodUnused() {
  9. String logMessage = "this is unused method";
  10. logMessage = logMessage.toLowerCase();
  11. System.out.println(logMessage);
  12. }
  13. }

混淆结果如下:

总结:像Utils这样的普通类,不管是类名、方法名还是变量都会混淆。除了混淆之外反编译之后就只剩一个方法了,因为另外一个方法没有被调用,所以认为是多余的代码,在打包的时候就给移除掉了(没有被调用的资源同样也会被移除掉),因此minifyEnabled除了混淆代码之外,还起到压缩APK包的作用。

2.2  NativeUtils类

下面这个类中同样有两个方法,一个是native方法,一个是非native方法。

  1. public class NativeUtils {
  2. public static native void methodNative();
  3. public static void methodNotNative() {
  4. String logMessage = "this is not native method";
  5. logMessage = logMessage.toLowerCase();
  6. System.out.println(logMessage);
  7. }
  8. }

混淆结果如下:

总结:NativeUtils的类名没有被混淆,这是由于它有一个声明成native的方法。只要一个类中有存在native方法,它的类名就不会被混淆(因为C++代码要通过包名+类名+方法名来进行交互),其中声明成native的方法也没有被混淆。但是非native方法的方法名和局部变量都被混淆了。

2.3  MyFragment类

下面这个类继承自Fragment,并且有一个全局变量。onCreateView()方法是Fragment的生命周期函数,在onCreateView()方法中又调用了methodWithGlobalVariable()和methodWithLocalVariable()方法,这两个方法的内部分别引用了一个全局变量和一个局部变量。

  1. public class MyFragment extends Fragment {
  2. private String toastTip = "toast in MyFragment";
  3. @Nullable
  4. @Override
  5. public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
  6. View view = inflater.inflate(R.layout.fragment_layout, container, false);
  7. methodWithGlobalVariable();
  8. methodWithLocalVariable();
  9. return view;
  10. }
  11.  
  12. public void methodWithGlobalVariable() {
  13. Toast.makeText(getActivity(), toastTip, Toast.LENGTH_SHORT).show();
  14. }
  15.  
  16. public void methodWithLocalVariable() {
  17. String logMessage = "log in MyFragment";
  18. logMessage = logMessage.toLowerCase();
  19. System.out.println(logMessage);
  20. }
  21. }

混淆结果如下:

总结:这个类也是混淆的比较彻底的,基本没有任何保留。所有的方法名、全局变量、局部变量都被混淆了。 其中,onCreateView()这样的生命周期方法会不会被混淆和我们使用Fragment的方式有关,比如在本项目中使用的是android.support.v4.app.Fragment,support-v4包下的,就连Fragment的源码都一起混淆了,因此生命周期方法当然也不例外了。但如果你使用的是android.app.Fragment,这就是调用手机系统中预编译好的代码,混淆无法影响到系统内置的代码,因此这种情况下onCreateView()方法名就不会被混淆,但其它的方法以及变量仍然会被混淆。

2.4  MainActivity类

MainActivity和MyFragment类似,也是定义了methodWithGlobalVariable()和methodWithLocalVariable()这两个方法,然后MainActivity对MyFragment进行了添加,并在Button的点击事件里面调用了自身的、Utils的、以及NativeUtils中的方法。

  1. public class MainActivity extends AppCompatActivity {
  2. private String toastTip = "toast in MainActivity";
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. getSupportFragmentManager().beginTransaction().add(R.id.fragment, new MyFragment()).commit();
  8. Button button = (Button) findViewById(R.id.button);
  9. button.setOnClickListener(new View.OnClickListener() {
  10. @Override
  11. public void onClick(View v) {
  12. methodWithGlobalVariable();
  13. methodWithLocalVariable();
  14. Utils utils = new Utils();
  15. utils.methodNormal();
  16. NativeUtils.methodNative();
  17. NativeUtils.methodNotNative();
  18. }
  19. });
  20. }
  21. public void methodWithGlobalVariable() {
  22. Toast.makeText(MainActivity.this, toastTip, Toast.LENGTH_SHORT).show();
  23. }
  24. public void methodWithLocalVariable() {
  25. String logMessage = "log in MainActivity";
  26. logMessage = logMessage.toLowerCase();
  27. System.out.println(logMessage);
  28. }
  29. }

混淆结果如下:

总结:MainActivity的类名、onCreate()这种声明周期方法是没有混淆的(凡是需要在AndroidManifest.xml中去注册的所有类的类名以及从父类重写的方法名都自动不会被混淆,这份规则同样也适用于Service、BroadcastReceiver和ContentProvider),但是我们定义的方法、全局变量、局部变量都被混淆了。

最后,第三方的Jar包(包名、类名以及方法名)都是会被混淆的。

3.  认识混淆规则

这些混淆规则是在哪里定义的呢?其实就是刚才在build.gradle的release闭包下配置的proguard-android.txt文件,这个文件存放于<Android SDK>/tools/proguard目录下,我们打开来看一下,每句话的意义已经在注释里标明了:

  1. -dontusemixedcaseclassnames //表示混淆时不使用大小写混合类名
  2. -dontskipnonpubliclibraryclasses //表示不跳过library中的非public的类
  3. -verbose //表示打印混淆的详细信息
  4.  
  5. # Optimization is turned off by default. Dex does not like code run
  6. # through the ProGuard optimize and preverify steps (and performs some
  7. # of these optimizations on its own).
  8. -dontoptimize//不进行优化,建议使用此选项,因为优化可能不保证在所有版本的Dalvik上都正常运行。
  9. -dontpreverify//不进行预校验,预校验是作用在Java平台上的,Android平台上去掉可加快混淆速度
  10. # Note that if you want to enable optimization, you cannot just
  11. # include optimization flags in your own project configuration file;
  12. # instead you will need to point to the
  13. # "proguard-android-optimize.txt" file instead of this one from your
  14. # project.properties file.
  15.  
  16. -keepattributes *Annotation* //表示对注解中的参数进行保留
  17. //表示不混淆上面声明的两个类,这两个类基本用不上,用于接入Google原生的一些服务
  18. -keep public class com.google.vending.licensing.ILicensingService
  19. -keep public class com.android.vending.licensing.ILicensingService
  20.  
  21. # For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
  22. //不混淆任何包含native方法的类的类名以及native方法名,我们已验证过了
  23. -keepclasseswithmembernames class * {
  24. native <methods>;
  25. }
  26.  
  27. # keep setters in Views so that animations can still work.
  28. # see http://proguard.sourceforge.net/manual/examples.html#beans
  29. //不混淆任何一个View中的setXxx()和getXxx()方法
  30. //属性动画需要有相应的setter和getter的方法实现,混淆了就无法工作了
  31. -keepclassmembers public class * extends android.view.View {
  32. void set*(***);
  33. *** get*();
  34. }
  35.  
  36. # We want to keep methods in Activity that could be used in the XML attribute onClick
  37. //表示不混淆Activity中参数是View的方法
  38. //如android:onClick=”click”,用户点击按钮会调用Activity中的click(View view)方法
  39. //如果这个方法被混淆的话就找不到了
  40. -keepclassmembers class * extends android.app.Activity {
  41. public void *(android.view.View);
  42. }
  43.  
  44. # For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
  45. //不混淆枚举中的values()和valueOf()方法
  46. -keepclassmembers enum * {
  47. public static **[] values();
  48. public static ** valueOf(java.lang.String);
  49. }
  50. //不混淆Parcelable实现类中的CREATOR字段
  51. // CREATOR字段是绝对不能改变的(包括大小写),否则整个Parcelable工作机制都会失败
  52. -keepclassmembers class * implements android.os.Parcelable {
  53. public static final android.os.Parcelable$Creator CREATOR;
  54. }
  55. //表示不混淆R文件中的所有静态字段
  56. // R文件是通过字段来记录每个资源的id的,字段名若被混淆了,id就找不到了
  57. -keepclassmembers class **.R$* {
  58. public static <fields>;
  59. }
  60.  
  61. # The support library contains references to newer platform versions.
  62. # Dont warn about those in case this app is linking against an older
  63. # platform version. We know about them, and they are safe.
  64. //版本比较低在打包时就会给予警告
  65. //不过support包中所有的代码都在版本兼容性上做足了判断
  66. //因此不用担心代码会出问题,所以直接忽略警告就可以了
  67. -dontwarn android.support.**

4.  自定义混淆规则

我们可以修改proguard-android.txt中的规则,但是直接在proguard-android.txt中修改会影响对本机上所有项目的混淆规则,app模块目录下都有一个proguard-rules.pro文件,这个文件就是用于让我们编写只适用于当前项目的混淆规则的,下面我们对混淆规则做一次修改。

4.1  对MyFragment类进行完全保留,不混淆其类名、方法名、以及变量名

  1. //keep后声明完整的类名,然后保留类中的所有内容可以使用*通配符实现
  2. -keep class com.example.guolin.androidtest.MyFragment {
  3. *;
  4. }


4.2  对Utils类中的未调用方法进行保留,防止其被移除掉

  1. //使用keepclassmembers关键字,后跟Utils完整类名,然后在内部声明未调用的方法
  2. -keepclassmembers class com.example.guolin.androidtest.Utils {
  3. public void methodUnused();
  4. }


4.3  对第三方库进行保留,不混淆android-support库

  1. //引入第三方库,一种是通过本地jar包引入的,一种是通过remote引入
  2. //这两种方式没什么区别,要保留代码都可以使用**这种通配符来实现,支持extends关键字
  3. -keep class <第三方包名>.<类名> {
  4. *;
  5. }
  6.  
  7. -keep class android.support.** {
  8. *;
  9. }


5.  拓展资料

虽说上面表格已经解释的很详细了,但是很多人对于keep和keepclasseswithmembers这两个关键字的区别还是搞不懂。确实,它们之间用法有点太像了,唯一的区别就在于类中声明的成员存不存在,我们还是通过一个例子来直接地看一下。

  1. //保留所有含有native方法的类的类名和native方法名
  2. //而如果某个类中没有含有native方法,那就还是会被混淆
  3. -keepclasseswithmember class * {
  4. native <methods>;
  5. }

  1. //所有类的类名都不会被混淆了
  2. //因为keep看到class *就认为应将所有类名进行保留,不关心该类是否含有native方法
  3. //当然这样写只会保证类名不会被混淆,类中的成员还是会被混淆的
  4. -keep class * {
  5. native <methods>;
  6. }

本文转载整理自郭大侠博客:http://blog.csdn.net/guolin_blog/article/details/50451259

Androd安全——混淆技术完全解析的更多相关文章

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

    在上一篇文章当中,我们学习了Android程序反编译方面的知识,包括反编译代码.反编译资源.以及重新打包等内容.通过这些内容我们也能看出来,其实我们的程序并没有那么的安全.可能资源被反编译影响还不是很 ...

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

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/49738023 之前一直有犹豫过要不要写这篇文章,毕竟去反编译人家的程序并不是什么值 ...

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

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/50451259 在上一篇文章其中,我们学习了Android程序反编译方面的知识,包括 ...

  4. Androd安全——反编译技术完全解析

    )第二步成功后我们会发现在当前目录下多了一个<APKName>文件夹,这个文件夹中存放的就是反编译的结果了.我们可以打开AndroidManifest.xml.res/layout即可查看 ...

  5. 字符串混淆技术应用 设计一个字符串混淆程序 可混淆.NET程序集中的字符串

    关于字符串的研究,目前已经有两篇. 原理篇:字符串混淆技术在.NET程序保护中的应用及如何解密被混淆的字符串  实践篇:字符串反混淆实战 Dotfuscator 4.9 字符串加密技术应对策略 今天来 ...

  6. 秋色园QBlog技术原理解析:性能优化篇:缓存总有失效时,构造持续的缓存方案(十四)

    转载自:http://www.cyqdata.com/qblog/article-detail-38993 文章回顾: 1: 秋色园QBlog技术原理解析:开篇:整体认识(一) --介绍整体文件夹和文 ...

  7. 字符串混淆技术在.NET程序保护中的应用及如何解密被混淆的字符串

    Visual Studio提供的Dotfuscator保护程序,可以对用户代码中包含的字符串进行加密.比如下面的例子,为了找到这个程序的注册算法,用.NET Reflector加载程序集后,发现代码中 ...

  8. ProGuard代码混淆技术详解

    前言     受<APP研发录>启发,里面讲到一名Android程序员,在工作一段时间后,会感觉到迷茫,想进阶的话接下去是看Android系统源码呢,还是每天继续做应用,毕竟每天都是画UI ...

  9. 最快下载速度100Mbps!4G LTE技术全解析

    1导读,关于4G的几个关键概念 [PConline资讯]100Mbps下载速度是什么概念?比3G网速快50倍又是什么概念?比3G通信方式更灵活.通信频谱更宽绰.通信质量更高效.通信费用更便宜是怎样一个 ...

随机推荐

  1. 《ArcGIS Runtime SDK for Android开发笔记》——问题集:使用TextSymbol做标注显示乱码

    1.前言 在14年的时候写过一篇博客关于ArcGIS for Android 10.1.1API 中文标注导致程序异常崩溃问题,但是当时并没有很好的解决这样一个问题,也并没有深入研究分析这样的一个异常 ...

  2. python数据类型和数据运算

    数字 整型 包括正整数和负整数,和数学的表示方法一样.如:1.100.8008.-12等. 浮点型 浮点数字也称为小数,如果按照科学计数法表示时,小数点的位置是可变的.如:1.23x109==12.3 ...

  3. Zamplus 晶赞天机

    类型: 定制服务 软件包: car/vehicle integrated industry solution collateral tourism 联系服务商 产品详情 解决方案 概要 DMP:通常称 ...

  4. 关于Mysql中文乱码问题该如何解决(乱码问题完美解决方案)(转)

    这篇文章给大家介绍关于Mysql中文乱码问题该如何解决(乱码问题完美解决方案)的相关资料,还给大家收集些关于MySQL会出现中文乱码原因常见的几点,小伙伴快来看看吧   最近两天做项目总是被乱码问题困 ...

  5. 02、体验Spark shell下RDD编程

    02.体验Spark shell下RDD编程 1.Spark RDD介绍 RDD是Resilient Distributed Dataset,中文翻译是弹性分布式数据集.该类是Spark是核心类成员之 ...

  6. s7nodave用于上位机连接西门子PLC,开源项目epics

    s7nodave 可以看作是Prodave的开源替代者,在PLC侧,不需要编程 This device support does not require any special programming ...

  7. 如何使用ABAP代码反序列化JSON字符串成ABAP结构

    假设我有这个JSON字符串如下图所示: 我的任务是解析出上图黑色方框里的几个字段,比如ObjectID, ETag, BuyerID, DateTime, ID, Name等等,把它们的值存储到对应A ...

  8. C++学习之显式类型转换与运行时类型识别RTTI

    static_cast const_cast reinterpret_cast 运行时类型识别(RTTI) dynamic_cast 哪种情况下dynamic_cast和static_cast使用的情 ...

  9. POJ-3126 Prime Path---BFS+素数打表

    题目链接: https://vjudge.net/problem/POJ-3126 题目大意: 给两个四位数a,b 每次改变a中的一位而且改动之后的必须是素数,问最少改动几次可以到b?(永远达不到b就 ...

  10. Entity Framework的扩展库

    https://github.com/jcachat/EntityFramework.DynamicFilters Provides global & scoped filters for E ...