如何停止害怕Proguard并开始生活


您好,我是一名Android开发人员,我不再担心ProGuard ...


通常,在遇到dalvik dex-limit问题或需要提高应用程序安全性时会记住该实用程序。 不幸的是,要正确配置Proguard并不是第一次。 在中断项目之后,我经常看到有多少人关闭了Proguard并打开了Mulditex支持,而每次我对此感到有点难过,因为Proguard既有助于减小应用程序的大小,又可以提高其性能。


最后,我决定写一篇文章,其中可以介绍我在与Proguard一起工作的几年中学到的所有有用信息,并且可以对初学者和已经知道一些知识的人都有帮助。


有什么事


Proguard是用于优化和混淆Java代码的开源实用程序。 该工具处理已编译的Java代码,因此它可以与任何JVM语言一起使用。 更准确地说,Proguard的语言本身是无关紧要的,仅字节码很重要。 所有proguard字节码操作都可以分为3个主要类别: 代码收缩优化混淆


代码缩小


是的,编写代码然后删除它是一件很奇怪的事情,但这是Android开发的现实。 当然,这与手工编写的代码无关(尽管这种情况确实发生了),而是与各种图书馆带来的无数载货有关。 GuavaApache CommonsGoogle Play Services和其他公司可以将apk文件的大小从500kb扩大到几十兆字节。 有时,由于超出Dalvik方法的限制 ,程序无法编译。 Proguard将帮助删除所有未使用的代码,并将应用程序的大小减小到几兆字节。


最佳化


除了删除不必要的代码外,Proguard还可以优化剩余的代码。 它的工具库包括控制流分析,数据流分析,部分评估,静态单项分配,全局值编号,活动性分析。 Proguard可以执行窥孔优化,减少变量分配的数量,简化尾部递归等等( Wiki )。 除了此类常规操作之外,Proguard还具有专门用于Android平台的优化,例如,用int替换枚举类,删除日志记录指令。


混淆


最后,Proguard可以通过将所有类,方法和字段重命名为随机(实际上不是很随机)字母集,从而将所有代码变成难以理解的混乱。 这是一个非常有用的选项,因为任何人都可以反编译您的apk文件,并且并非每个人都有耐心来理解混淆的代码。


工作原理


Proguard按上述顺序分3个步骤工作: 代码收缩优化混淆 。 每个步骤都是可选的。


对于Android SDK,优化步骤默认为关闭。


为了使Proguard正常工作,您需要提供3个组件:


  • 编译后的代码是一个归档文件,其中包含程序的class文件以及您使用的所有库(jar,aar,apk,war,zip等)。 Proguard仅修改已经编译的代码,与源代码无关。
  • 配置文件-包含要开始处理的所有规则,选项和设置的文件。
  • Library jars(aar,apks,...)-运行程序的平台的类。 如果是Android,则为android.jar 。 这些归档文件仅用于正确分析您的代码,它们不会被修改(这没有意义,因为android.jar位于“电话中”,我们无法访问它)。

Jeb Ware演示文稿中的图片,文章末尾的链接
(图片来自Jeb Ware的演讲,文章末尾的链接)


通过使用库类和配置文件,Proguard定义了程序( 种子 )的所有入口点。 换句话说,它定义了可以从外部调用并且无法触摸的类和方法。 然后,从发现的种子开始 ,Proguard递归遍历您的所有代码,标记“使用”了它可能到达的所有内容。 所有其他代码将被删除。 您必须在配置中至少指定一个入口点。 对于标准的Java程序,这是main功能。 在Android中,程序中没有单个入口;相反,我们具有由系统创建和调用的标准组件(活动,服务等)。 幸运的是,我们不需要自己在此处指定任何内容,Android SDK会为我们创建必要的配置。


伴随文件


检测到所有入口点后,Proguard会将它们写入seeds.txt文件。


Proguard认为不需要的所有代码都写入了usage.txt文件。 对于包含远程代码的文件来说,这是一个相当奇怪的名称,将其命名为unusage.txt会更正确,但是请记住,我们已经拥有了。


在混淆步骤中,将创建一个包含对<原始类名|方法|字段>-> <混淆类名|方法|字段>对的mapping.txt文件。 当您需要对程序进行模糊处理时(例如,读取stacktrace),此文件很有用。 不需要手动将文件映射回; Android SDK具有retraceproguardui实用程序来提供帮助。 而且,如果您使用Fabric Crashlytics,那么他们的gradle插件可以独立地找到该文件并将其加载到控制台中,因此您无需担心。


对于Android,这些文件通常位于app/build/output/mapping/<product-flavor-name>/


Proguard还会创建一个dump.txt文件,其中包含Proguard放入最终归档文件中的所有内容。 他从来没有为我派上用场,但也许对某人有用。


Android上的情况如何


Android Gradle插件可以自行运行Proguard。 您要做的就是启用此选项并指定配置文件。


 buildTypes { <...> release { minifyEnabled true proguardFiles 'proguard-rules.pro', getDefaultProguardFile('proguard-android.txt') } } 

minifyEnabled true在构建时启用Proguard


proguardFiles配置文件列表。 来自所有配置文件的规则将按照它们出现的顺序添加到常规列表中。


proguard-rules.pro是带有项目特定规则的配置文件


getDefaultProguardFile('proguard-android.txt') -返回针对Android应用程序的标准配置文件的函数。 它位于AndroidSDK/tools/proguard


实际上,Android SDK有两个proguard-android.txtproguard-android.txtproguard-android-optimize.txt 。 第一个具有-dontoptimize选项,该选项关闭所有优化。 如果要启用优化,请使用第二个配置。


除了这些标准配置之外,Android SDK(aapt)会自动生成一组资源规则:aapt检查所有xml文件(包括清单)以查找所有活动,服务,视图等。 并为他们生成必要的规则。 生成的规则可以在app/build/intermediates/proguard-rules/<flavor>/aapt_rules.txt 。 您无需自行指定,Android Gradle插件会自动添加这些规则。


Jeb Ware演示文稿中的图片,文章末尾的链接
(图片来自Jeb Ware的演讲,文章末尾的链接)


设定档


设置Proguard是使用它的最基本的部分,同时也是最困难的部分。 错误的配置很容易在运行时中断应用程序和应用程序本身的编译。 所有可用的配置选项均已详细记录为开。 网站。


在所有选项中,我将选出三个最重要的组:


  • 保留规则-程序的所有可能的入口点。 告诉Proguard哪些类别或类别的一部分保持不变或哪些修改对特定类别有效的规则。
  • 优化调整-指明可接受哪些优化,需要完成多少个优化周期。
  • 处理警告,错误和调试

遵守规则


这是一组旨在保护您的代码免受无情的Proguard攻击的选项。 在最一般的形式下,此规则如下所示:


 -keep [,modifier,...] class_specification 

keep是这些选项中最常见的选项(还有其他选项),它告诉Proguard保存类本身及其所有类成员:字段和方法。


class_specification指向类或其部分(类成员)的模板。 模板的一般视图非常大,可以将其关闭。 文件 。 您可以与他联系,但总的来说,您只需要记住我们就有机会编写以下组件的模板:


  • 选择所有具有特定名称的类,包
  • 选择所有继承/实现某些类/接口的类
  • 选择所有带有特定修饰符和/或特定注释的类
  • 选择所有具有特定名称,修饰符,参数和返回值的方法
  • 选择具有特定名称,修饰符和特定类型的所有字段。
  • 可以使用通配符


    再一次,这不是对模板的严格描述;它只是我们所拥有功能的列表。 以下是一些示例:



-keep public class com.example.MyActivity
保存类com.example.MyActivity


-keep public class * extends android.app.Activity
保存所有继承自android.app.Activity公共类


 -keep public class * extends android.view.View { public <init>(android.content.Context); public <init>(android.content.Context, android.util.AttributeSet); public <init>(android.content.Context, android.util.AttributeSet, int); public void set*(...); } 

找到所有继承android.view.View公共类,并在其中保存3个带有某些参数的构造函数+所有公共方法, void修饰符,任何参数和以set开头的名称。 该类的所有其他部分都可以修改。


-keep class com.habr.** { *; }
将所有类及其所有内容保存在com.habr包中


modifiers -保留规则的补充:


  • includedescriptorclasses除了指定的类/方法/字段外,您还需要保存在其描述符中出现的所有类。
  • includecode此特定规则所指向的方法的内容也不能被触及。
  • allowshrinking此规则指向的类不是种子,可以删除,但只有在程序本身未使用它们时才可以删除。 但是,如果在代码收缩后仍保留该代码(由于有人使用它),则不可能优化/混淆该代码。
  • allowoptimization此规则引用的类只能进行优化,而不能删除或混淆。
  • allowobfuscation此规则指向的类只能被混淆,而不能删除或优化。

除了keep ,还有其他一些选择:


-keepclassmembers指示如果代码收缩后保留了类本身,则应该保存类成员。


-keepclasseswithmembers表示您要保存其内容属于指定模板的类。 例如, -keepclasseswithmembers class * { public <init>(android.content.Context); } -keepclasseswithmembers class * { public <init>(android.content.Context); } -保存所有具有一个公共构造函数且其参数类型为Context的类。


-keepnames -- -keepnames缩写-keep,allowshrinking


-keepclassmembernames -- -keepclassmembernames简写, -keepclassmembers,allowshrinking


keepclasseswithmembernames -- -keepclasseswithmembers,allowshrinking


优化调整


这里最重要的选项是-dontoptimize标志。 如果存在,将不执行优化,所有其他优化选项都将被忽略。


有许多优化选项,但是以下对我来说最有用:


-optimizations optimization_filter列出您要使用的所有方式。 最好使用在proguard-android-optimize.txt指定的集合或其子集。 可以在此处找到所有优化的列表。


-optimizationpasses n优化周期数。 几个循环可以改善结果。 同时,如果Proguard认为结果与上次相比没有改善,则足以停止周期。


-assumenosideeffects class_specification指示此方法没有副作用,仅返回一些值。 如果Proguard检测到未使用返回的结果,则将删除对该方法的调用。 此选项最常见的用法是删除所有调试日志: -assumenosideeffects class android.util.Log { public static int d(...); } -assumenosideeffects class android.util.Log { public static int d(...); }


-allowaccessmodification显示所有隐藏的内容:)一个不错的选择,它使您摆脱了一堆嵌套类的人工访问器方法。 仅与-repackageclasses一起使用


-repackageclasses将所有类移动到一个指定的软件包。 这更适用于混淆,但同时在优化方面提供了良好的结果。


其他有用的选择


-dontwarn-dontnote


Proguard非常聪明,在代码分析过程中总是报告可疑的地方,有时是注释,有时是警告。 如果在打开Proguard的情况下构建失败,请确保阅读生成的所有日志,它将记录出错误的地方,并且很可能甚至会告诉您如何修复它。 阅读所有消息后,如果确定没有问题,则可以解决问题或忽略这些选项之一的消息。


例如,碰巧某些Java库使用的平台类不在android.jar中,Proguard会对此发出警告。 如果您确定该库在Android环境中可以正常运行,则可以关闭此警告-dontwarn java.lang.management.**


-whyareyoukeeping class_specification是一个有用的选项,它将显示Proguard决定不触摸此类/方法的原因。


-verbose打印更详细的日志和异常


-printconfiguration从使用的所有配置文件中打印选项的完整列表,包括库中的规则以及通过aapt生成的规则。


-keepattributes SourceFile, LineNumberTable保存元信息(文件名,行号),以便能够在IDE中调试代码并获得有意义的堆栈跟踪。 确保添加此选项。


练习


通常会发生这种情况:打开Proguard,它会使您整个项目陷入瘫痪,并带来大量错误。 许多人在此步骤中关闭了Proguard,并尝试不返回它。 我将尝试提供一些技巧,以使过渡过程更容易。


确定起点


如果您是Android开发人员,那么一切都非常基础-只需从Android SDK中选择以下两个标准配置之一: proguard-android.txtproguard-android-optimize.txt ,它们将处理所有应保持不变的内容。


检查所有库


近来,越来越多的带有现成的proguard-config的库被分发。 Proguard可以查看存档文件,找到库配置并将其添加到其他选项。 检查用于此配置的每个库。


(其中一个库的aar文件的内容)
(其中一个库的aar文件的内容)


如果您使用Google Play服务,则com.google.gms.google-services插件将自行选择所需的配置。


如果该库的作者未将配置打包到档案中,则可能是他们注意了并将规则写在其网站,存储库页面或README文件中。 尝试自己找到所用库版本的配置。


如果在任何地方都找不到任何现成的规则,则必须阅读日志并单独解决问题。 最有可能的是,您将需要为已损坏的库代码添加保留规则。 如果不干扰程序,则忽略它们。


检查您的代码


您会更好地知道可以在刀下发送什么代码,但是您应该仔细查看所有使用反射的地方:


  • Class.forName(...)(文档承诺Proguard能够定义此类代码,但是,在某些情况下,值得检查)
  • 序列化,映射中使用的模型/实体类。 所有其字段名称(有时是类本身)都很重要的类(Gson,RealmIO等)
  • 通过JNI进行本地库调用

测验


如果仅在测试中使用类/方法,而在其他地方均未使用,则Proguard会删除此代码。 如果您有TDD,这是一种常见情况:)对于这种情况,我有一个单独的配置,其中添加了尚未集成到项目中,不在任何地方使用但需要进行测试的类。


在Android Gradle插件中,除了proguardFiles指令之外,仍然还有testProguardFiles 。 需要此说明来指定将应用于生成的测试应用程序的配置,以便在运行测试时测试您的应用程序。 通常,这用于在两个apk文件中实现相同的优化/混淆,因此它们之间不会出现同步。 友情链接


APK分析器


Android Studio具有如此出色的工具。 您可以通过“查找操作”->“分析APK”或通过在Android Studio中打开apk文件本身来打开它。 分析器显示了很多有用的信息,但是现在我们对代码感兴趣。 要查看最终打包到APK文件中的文件,您需要选择classes.dex文件



默认情况下,将精确显示经过缩小和优化步骤的结果代码。 但是,您可以单击“ Load Proguard映射...”按钮,添加seeds.txtusage.txt以查看已删除的代码。



如果Proguard出于某种原因修改了所需的代码,请在Analyzer中找到它,然后选择通过RMB 生成Proguard保留规则” 。 分析器将为规则生成几个选项供您选择,从最通用到最具体,选择其中一个。




对于图书馆作者


如果要创建Android库,则可以为客户端添加proguard配置,如下所示:


 buildTypes { release { consumerProguardFiles 'proguard-rules.pro' } } 

我认为,最好不要热心优化和混淆您的图书馆,而要为您的客户提供这个机会。 一个不错的基调是将配置添加到客户(如果客户包括Proguard)仍要添加的内容。 但是,如果您仍想增加安全性,那么很明显,您需要保护库的整个公共API不受Proguard的保护,包括描述符和签名。


R8,DexGuard和Redex


R8是Google用来替代当前Proguard的新工具。 请稍等,不要忘记忘记您​​在本文中阅读的所有内容,就像对待新的Proguard一样。 Google承诺保留整个公共api,以便所有配置都能像以前一样工作。 该项目仍处于测试阶段,但您可以自己尝试。


DexGuard是Proguard开发人员的付费实用程序。 它可以一起使用,也可以代替Proguard使用。 有人认为DexGuard可以做Proguard可以做的所有事情,但效果更好。 不幸的是,我没有机会尝试,如果有人有经验,请分享。


Redex是Facebook的另一个dex优化器。 据报道,通过将工具应用于Proguard已经处理的代码,您可以将生产率提高25%,并减小应用程序的大小。


而不是结论


不要害怕使用Proguard,不要懒惰并花一些时间进行设置。 与增加用户忠诚度相比,这将减小其尺寸,提高工作速度。 同时,尝试创建有效的Proguard配置,不要编写“地毯”规则,否则生气的Jake Wharton会来找您并责骂您。



资源资源


Proguard网站 。 也有关于DexGuard的信息。
各种示例规则
R8
录制Proguard如何与DroidCon一起使用的演示文稿
有效的ProGuard保留了较小的应用程序(Google I / O '18)演示记录的规则
启用和配置Android Proguard的说明
维基页面
Redex

Source: https://habr.com/ru/post/zh-CN415499/


All Articles