吃掉Android混淆——ProGuard第一篇

之前对于安卓混淆只是在字面上有一个认识,看过几篇别人的博客,不过很抓狂,知道怎么用却不了解为什么是这样,于是有了写安卓混淆相关的文章想法:一方面帮助我梳理知识,另一方面希望给大家提供一些帮助。

介绍

Android官网介绍

ProGuard 工具通过移除无用的代码以及使用语义隐晦的名称来重命名类、字段和方法,从而达到压缩、优化和混淆代码的目的。最终您将获得一个较小的 .apk 文件,此文件更难于进行反向工程。由于 ProGuard 会使应用更难于进行反向工程,因此当应用使用对安全性要求极高的功能时(例如,当您向应用授予许可时),您必须使用此工具。

摘自Google Android官网 ProGuard Android官方中文版介绍(需翻墙)
强烈建议阅读,可以让你在宏观上有一个掌握

ProGuard官网 介绍

ProGuard一共有四个功能:压缩(shrinker)、优化(optimizer)、混淆(obfuscator)以及预校验(preverifier)。
压缩:检查移除无用的类(classes)、字段(fields)、方法(methods)和属性(attributes)。
优化:分析优化方法的字节码。
混淆:使用无意义的短名字重命名剩余的类(classes)、字段(fields)、方法(methods)
这些步骤使得代码更小,更高效而且很难被逆向工程。
预校验:在classes中加入了预校验信息,Java Micro Edition、java 6以及更高的版本需要。
每一步都是可选的。不如,ProGuard也可以只用来列出应用的死代码(dead code),或者为了在java6上的高效使用只做预校验。
ProGuard_Intruduction
ProGuard实现读取input jars(or aars, wars, ears, zips, apks, or directories),它随后会被压缩、优化、混淆以及预处理。ProGuard会把处理结果写入到一个或多个output jars(or aars, wars, ears, zips, apks, or directories)。读取的输入中也可能包含资源文件,这些也可以可选的更新到映射到混淆后的类。

ProGuard需要指定的输入(input) jar相关的依赖jar**library jars**(or aars, wars, ears, zips, apks, or directories)。本质上就是你需要编译代码的库。ProGuard需要它们重构类的依赖关系。依赖库jar始终保持不变。你应该依然把它们放到你最终应用的类路径中(class path)。

切入点(Entry points)

为了决定哪些代码必须要保留,哪些代码可以被丢弃和混淆,你需要为你的代码指定一个或多个切入点(entry point)。典型的切入点类是有main methods,applets,midlets,activities(安卓)等。

  • 压缩这步:ProGuard从这些种子(seeds)开始并且递归的决定了哪些类和类方法被使用到。所有其他的类和类方法会被丢弃掉。
  • 优化这步:ProGuard进一步优化代码。在其他优化中,不是切入点的类和方法会变成private、static、或者final,不使用的参数( parameter)被移除,还有一些方法会被内联(inlined)。
  • 混淆这步:ProGuard会把不是切入点的类和类成员重命名。在整个过程中会保留切入点以确保能通过它们的原始名字(original name)被访问到。
  • 预校验这步:是唯一一个不需要知道切入点的步骤。

在手册(Manual)的使用部分(Usage section)描述了需要的-keep选项并且在示例部分(Examples section)提供了大量的例子。

反射

对于任何自动处理的代码中反射(reflection)和内省(introspection)表现出了特定的问题。使用ProGuard,代码中被创建的类和类成员或者动态调用(也就是,通过名字调用)也必须指定作为切入点。例如,Class.forName()的构造会在运行时(run-time)引用一些类。再比如,类名可能会从配置文件中读取,你是无法从他们的原始名字来计算哪些类需要保留。因此,你必须使用一些-keep选项在你的ProGuard配置中指定好它们。

然而,ProGuard已经为你检查处理了一些情况

  • Class.forName(“SomeClass”) SomeClass.class
  • SomeClass.class.getField(“someField”)
  • SomeClass.class.getDeclaredField(“someField”)
  • SomeClass.class.getMethod(“someMethod”, new Class[] {})
  • SomeClass.class.getMethod(“someMethod”, new Class[] { A.class })
  • SomeClass.class.getMethod(“someMethod”, new Class[] { A.class,
    B.class })
  • SomeClass.class.getDeclaredMethod(“someMethod”, new
    Class[] {})
  • SomeClass.class.getDeclaredMethod(“someMethod”, new
    Class[] { A.class })
  • SomeClass.class.getDeclaredMethod(“someMethod”,
    new Class[] { A.class, B.class })
  • AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, “someField”)
  • AtomicLongFieldUpdater.newUpdater(SomeClass.class, “someField”)
  • AtomicReferenceFieldUpdater.newUpdater(SomeClass.class,
    SomeType.class, “someField”)
    哪些类名和类成员当然可以不同,但是为了ProGuard能识别,构造必须相同。引用的类和类成员要在压缩阶段保留并且字符串参数(string paramter)在混淆阶段要正确更新。

此外,如果看起来有必要保留一些类和类成员时ProGuard会提供一些建议。比如,ProGuard会注意结构类似“(SomeClass)Class.forName(variable).newInstance()”这样的。也就是表明类或者接口SomeClass以及它的实现可能需要保留。然后你可以相应的调整你的配置。

为了正确的结果,你至少对你要处理的代码有一些了解。混淆一些反射的时候可能还需要多次尝试错误,尤其是处理没有必要信息的内部代码的时候。

以上就是安卓和ProGuard两方面的介绍了,看了这些你应该对这个开源项目有一定认知了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值