ProGuard进行混淆代码原理初探

ProGuard充当Java类文件的缩小,优化,混淆,先行检验的角色。

ProGuard进行混淆代码原理初探_第1张图片

总体原理描述

<1>缩小步骤找出并移除无用类,字段,方法和属性。优化过程分析和优化方法的字节码。
<2>混淆过程用短而无意义的名字重命名余下的类,字段,和方法。
<3>第一个的步骤使代码变得更加小且效率和更难进行逆行工程。最后一个先行检验的步骤添加先行检验信息到类,这个类是java微型版要求并且能提高Java6的启动速度。

注意:

每一个步骤都是可选的。例如,ProGuard也能够仅仅被用来列出应用程序dead code,或者先行检验类文件在Java6高效果运行。
ProGuard通常读取输入Jars(或者wars,ears,zips,或者目录树),然后收缩,优化,混淆和先行检验它们。具有选择性的是,它可以执行多个优化通道,每一个通常遵循另一个收缩步骤。ProGuard把处理结果写到到一个或多个输出jars(或者wars,ears,zips,或者目录树)。输入可能包含资源文件,其名称和内容可选择的被更新以映射到混淆的类名称。
ProGuard要求 library jars(或者wars,ears,zips,或者目录树)指定输入Jars。这些本质上是你将需要编译代码时的libraries。ProGuard用他们去重建那些需要适当处理的类的依赖关系。Library jars本身是保持不变的,你仍然应该把它们放到你的最终应用程序的类路径。
本《使用手册》describes keep 选项的必要性和实例部分提供了很多例子。

Entry points(切入点)


为了确保哪些代码被保存,哪些被废弃或混淆,你必须为你的代码指定一个或多个切入点。
以下切入点通常与类的主方法,applet和midlets相关
1.在收缩步骤,ProGuard从那些种子和递归确定所使用的类和类成员开始。所有其它类和类成员都会被丢弃。
2.在优化步骤,ProGuarad进一步优化代码。在其他的优化中,没有切入点的类和方法可以是private,static or final,且无用的参数可以被移除和一些方法可能是inline的。
3.在混淆步骤,ProGuard重命名没有切入点的类和类成员。在整个过程中,保持切入点确保它们冉冉可以访问它们原来的名字。
4.在先行检验步骤,是不知道入口点的唯一步骤。

Reflection(反射)

反射和introspection为任何自动执行的代码提出了特殊的问题。在ProGuard中,你代码中的类和类成员被创建或者动态调用(即是通过名字),不得不指定为一个切入点。
例如,Class.forName()构造器可能在运行时涉及到任何类。通常不可能预见哪些类必须保留(用它们的原始名称),因为类名可能会从配置文件中读取。例如,你因此必须指定他们在你的ProGuard配置,用简单的 -keep选项确保其不被混淆。

ProGuard在下列情况下检测并帮你处理。

1.Class.forName("SomeClass")
2.SomeClass.class
3.SomeClass.class.getField("someField")
4.SomeClass.class.getDeclaredField("someField")
5.SomeClass.class.getMethod("someMethod", new Class[] {})
6.SomeClass.class.getMethod("someMethod", new Class[] { A.class })
7.SomeClass.class.getMethod("someMethod", new Class[] { A.class, B.class })
8.SomeClass.class.getDeclaredMethod("someMethod", new Class[] {})
9.SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class })
10.SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class, B.class })
11.AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, "someField")
12.AtomicLongFieldUpdater.newUpdater(SomeClass.class, "someField")
13.AtomicReferenceFieldUpdater.newUpdater(SomeClass.class, SomeType.class, "someField")

当然,类和类成员的名字不同,但结构确实应为ProGuard所确认那样。
引用的类和类成员被保留在收缩阶段,字符串参数在混淆阶段正确的更新。
为了获得正确的结果,您至少应该熟悉您正在处理的代码。混淆代码执行很多反射,可能要要求实验和遇到各种错误,特别是在没有代码内部的必要信息的情况。

ReTrace是ProGuard混淆堆栈的一个陪同工具。

当一个模糊的程序抛出一个异常,产生结果的堆栈一般都不是有用的信息。类名和方法名已经被无意义的短字符串替代。源文件名和行号也同样被忽略。虽然这可能是故意的,但在调试问题时候也坑能带来不便。
ProGuard进行混淆代码原理初探_第2张图片
ReTrace可以阅读混淆的堆栈跟踪和恢复它到一种看起来没有混淆时候的状态。恢复以 mapping file为基础,ProGuard 可以在混淆时写完. mapping file连接初始的类名和类成员名字和它们混淆后的名字。

ProGuard工具缩小,优化,和混淆你的代码通过删除无用的代码和重命名类、字段和语义模糊的的命名。这样就会产生一个更小的apk文件,更难进行逆向工程。在你应用程序涉及安全敏感的特性时,添加证书的时候,使用ProGurad进行混淆是很重要的。
ProGuard是集成到安卓构建系统的,所以你不必手动调用它。ProGuard仅仅当你在release模式下构建时运行,所以你不必处理混淆代码当你在debug模式构建你的应用程序。ProGuard在运行时是可选的,但是强烈推荐运行它。

启用ProGuard (Gradle Builds)

minifyEnabled

这个属性是relase模式下表示ProGuard是否启动的标记,下面这段代码要加载moudles的build.gradle这个文件中

getDefaultProguardFile(‘proguard-android.txt’)

获取安卓sdk tools/proguard 文件夹下面的默认ProGuard配置
proguard-android-optimize.txt这个文件仍然可以使用,它和普通的有同样的规则,但是能够优化配置,在字节码的级别上去优化你的app。
proguard-rules.pro 在moudle的根目录
这个例子直接给proguardFiles添加proguard-rules-new.pro 和给flavor2添加.other-rules.pro

 android {
   ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
            'proguard-rules.pro'
        }
    }
  }
android {
   ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
            'proguard-rules.pro', 'proguard-rules-new.pro'
        }
    }
   productFlavors {
        flavor1 {
        }
        flavor2 {
            proguardFile 'other-rules.pro'
        }
    }
 }

Configuring ProGuard
在某些情况下,ProGuard配置文件里面的默认配置就足够了。然而,很多情况下,ProGuard很难正确的分析并可能移除它所认为的没用的代码,但你应用程序实际是需要的。
1.一个只在AndroidManifest.xml文件引用的类
2.通过JNI调用的方法
3.动态引用的字段或方法
一般情况下默认的ProGuard配置文件尽量去覆盖,但是你可能遭遇到一个ClassNotFoundException异常,发生在当ProGuard除掉你应用程序调用的一整个类。
通过以下方法确保某个类不被ProGuard移除,举例
-keep public class
这理由很多选项和使用-keep 选项时需要考虑的,所以热烈建议你读ProGuard Manual 了解更多关于自定义配置文件的信息。Keep选项和示例部分的概述特别有用。ProGuard Manual的Troubleshooting(故障排除)概述了其他常见问题,你可能碰到的代码被移除的问题。
Decoding Obfuscated Stack Traces
(解码混淆的堆栈跟踪)
当你的混淆的代码输出一个堆栈跟踪,方法名被混淆,这导致调试变难,这不是不可能的。幸运的是,不管ProGuard什么时候运行,它都输出一个mapping.txt文件,这个文件给你展示了与混淆后的名字匹配的那些初始类,方法和字段的名。
Window操作系统的脚本retrace.bat 或者Linus操作系统的脚本 retrace.sh 或者Mac OS X 能够将混淆的堆栈跟踪转换成可阅读的堆栈跟踪。这个脚本文件位于目录/tools/proguard/ 。执行retrace 工具的语法如下:

retrace.bat|retrace.sh [-verbose] mapping.txt []

For example:

retrace.bat -verbose mapping.txt obfuscated_trace.txt

如果没有指定的值, retrace 工具将从标准输入流读取信息。
Debugging considerations for published applications(已发布应用程序的注意事项)
保存每一次发布产生的文件 mapping.txt 。为每一个发布构建保留 mapping.txt 的副本, 确保你可以调试一个问题如果用户遇到Bug并提交一个混淆的堆栈跟踪。项目的mapping.txt 文件在你每次进行发布构建的时候重写 ,所以你必须注意保存你所需要保存的版本。在Eclipse中, 文件存储在在 /bin/proguard/. 在Android Studio中, 文件存储在app build/outs/ 文件夹.
例如, 假如你发布一个应用程序并继续为新版本开发应用程序的新功能。你不久后用ProGuard再做一个发布构建. 此构建会覆盖以前的mapping.txt 文件. 一个用户提交一个来自当前发布的应用程序且包含堆栈跟踪的bug报告. 你不再有办法调试用户的堆栈跟踪, 因为与用户设备上的版本相关联的 mapping.txt 文件已不复存在.在一些情况下,你的mapping.txt 文件可以被覆盖 , 所以确保你已经保存了每一个预期需要调试的文件副本
如何保存mapping.txt文件是你的决定。例如,您可以重命名文件以包含version或bulid number,也可以版本控制它们和源代码一起。

具体内容参考官方文档

你可能感兴趣的:(安卓,gradle)