ProGuard工作原理
ProGuar由shrink、optimize、obfuscate和preveirfy四个步骤组成,每个步骤都是可选的,我们可以通过配置脚本来决定执行其中的哪几个步骤。
(1)压缩(Shrink):检测并移除代码中无用的类、字段、方法和特性(Attribute)。
(2)优化(Optimize):对字节码进行优化,移除无用的指令。
(3)混淆(Obfuscate):使用a,b,c,d这样简短而无意义的名称,对类、字段和方法进行重命名。
(4)预检(Preveirfy):在Java平台上对处理后的代码进行预检,确保加载的class文件是可执行的。
ProGuard操作步骤:
1.前往proGuard 官网下载地址下载并解压proguard,执行 bin目录下的proguardgui.bat
https://sourceforge.net/projects/proguard/
2.左下角Load configuration可加载已存在的配置文件进行代码加密混淆,若没有现成的配置文件(xxx.pro),可参照如下步骤
Add input:导入需要操作的文件。
Add Outinput: 需要输入的文件。
Filter: 过滤导入文件内那些不需要操作的文件。
Add an enty:添加支持库library jars. 对添加的项目支持 jar文件或者其他文件不会进行处理。
点击左边“input/output”菜单,然后点击右边的“Add input”按钮,添加需要混淆的jar包,然后点击“add output”,选择输出的路径和包名。
页面右下角“”ADD“”开始添加支持库, 需要添加项目里所以依赖的jar包,我使用的是idea
打包工具,图二,添加完需要打包的项目之后Build或者Rebuild后将在对应文件下生成artifacts文件,里面有依赖的jar包和主程序包;(使用maven工具栏打包项目默认将项目和及其依赖打包成一个jar包,影响后续proguard执行)
4.压缩
—print usage 指定列出输入类文件的无效代码
Keep一些保留选项,可自行配置,一般采用默认配置。
-
混淆
我测试了如下几项
. User mixed-case class names 指定在混淆的时候使用大小写混用的类名
. Keep package names 声明不混淆指定的包名
. Flatten package hierarchy 所有重新命名的包都重新打包
. Keep attributes 指定要保留的所有可选属性
. Keep parameter names 指定被保护的方法的参数类型和参数名不被混淆 -
obfuscate 代码混淆,除-keep指定的类及成员外,都会被替换成简短、随机的名称,以达到混淆的目的。
-
Print Mapping 输出映射文件即混淆后与混淆前的映射
-
Apply mapping 根据指定的mapping映射文件进行混淆。
-
Obfuscation dictionary 自定义混淆的类名,方法名,变量名字典,替换原先的a,b,c.这种格式的
-
Class Obfuscation dictionary 指定一个文本文件,所有有效单词都用作混淆的类名
-
Package Obfuscation dictionary 该文件中的所有有效词均用作混淆的程序包名称
-
Overload aggressively 混淆的时候大量使用重载,减小包体积,增加理解难度,然后,只要Java字节码要求参数和返回类型不同(而不是Java语言要求的参数),多个字段和方法就可以使用相同的名称。此选项可以使处理后的代码更小(更难理解)。
-
User unique clssmember names 指定相同的混淆名对应相同的方法名,将不同的混淆名称分配给具有不同名称的类成员
-
User mixed-case class names 指定在混淆的时候使用大小写混用的类名
-
Keep package names 声明不混淆指定的包名
-
Flatten package hierarchy 所有重新命名的包都重新打包
-
Repackage Classes 所有重新命名过的类都重新打包
-
Keep attributes 指定要保留的所有可选属性,例如
-keepattributes Exceptions,InnerClasses,Signature
-keepattributes SourceFile,LineNumberTable
-keepattributes Annotation -
Keep parameter names 指定被保护的方法的参数类型和参数名不被混淆
-
Rename sourceFile attribute 指定一个字符串常量设置到源文件的类的属性中
-
Adapt class strings 指定字符串常量如果与类名相同,也需要被混淆
-
Adapt resource file names 如果资源文件与某类名同,那么混淆后资源文件被命名为与之对应的类的混淆名
-
Adapt resource file names 指定资源文件的中的类名随混淆后的名字更新
6.代码优化
-dontoptimize
不对class进行优化,默认开启优化。
注意:由于优化会进行类合并、内联等多种优化,-applymapping可能无法完全应用,需使用热修复的应用,建议使用此配置关闭优化。
-optimizations优化配置,可进行字段优化、内联、类合并、代码简化、算法指令精简等操作。例如:
#只进行移除未使用的局部变量、算法指令精简
-optimizations code/removal/variable,code/simplification/arithmetic
#进行除算法指令精简、字段、类合并外的所有优化
-optimizations !code/simplification/arithmetic,!field/,!class/merging/
-optimizationpasses n
指定要执行的优化遍数。默认情况下,执行一次通过。多次通过可能会导致进一步的改进。
一般采用默认配置。
7.预验证选项
-preverify
预先验证已处理的类文件
-micro edition
声明目标平台是java micro版本。预校验会根据这项配置加载合适的StackMap,而不是标准的StackMap。
-android
指定已处理的类文件针对Android平台。然后,ProGuard确保某些功能与Android兼容。
-verbose
指定在处理期间写出更多信息。如果程序因异常终止,则此选项将打印出整个堆栈跟踪,而不仅仅是异常消息。
-dontnote
指定不打印有关配置中潜在错误或遗漏的注释,例如类名中的错字或可能有用的缺失选项。可选过滤器是一个正则表达式;ProGuard不会打印有关名称匹配的类的注释。
-dontwarn
指定根本不警告尚未解决的引用和其他重要问题。可选过滤器是一个正则表达式;ProGuard不会打印有关名称匹配的类的警告。
-ignorewarnings
指定打印有关未解决的引用和其他重要问题的任何警告,但在任何情况下都将继续处理。
- ProGuard一些注意事项
a. 反射使用。比如 setName()方法通过混淆被映射为了a()如果我们希望通过方法名 setName 来调用类中的该方法,在写代码的时候,我们也不会知道这个名字将会被映射为 a,混淆之后,会找不到方法的。混淆使得方法名发生改变,而我们还在使用原来的方法名进行反射。
b. bean 文件使用。对于 bean 文件,很多时候,它们作为和服务器之间的通信实体。如果在这种情况下进行了混淆,当数据发给服务器之后,服务器是看不懂的,因为属性名都变了,而服务端保存的是原来的 bean 文件(序列化问题,json问题)。
c. 回调函数。这是一个值得注意的地方。比如在 Activity 中的 onTouchEvent 回调,如果被你混淆了,而系统实际上不知道的,混淆是你的个人行为。它不会知道到该回调的,同样因为找不到。
d. 枚举。在使用枚举类型的时候,应当注意不要对它们进行混淆。因为枚举会使用反射进行操作。
e. native 方法不要混淆。
f. 内嵌类经常会被混淆,结果在调用的时候为空就崩溃了。(开发尽量避免内嵌类)
保留内嵌类不被混淆 列:-keep class com.example.xxx.MainActivity$* { *; }
为了获得最佳结果,ProGuard的优化算法假定处理后的代码从不故意抛出NullPointerExceptions或ArrayIndexOutOfBoundsExceptions甚至OutOfMemoryErrors或StackOverflowErrors,以实现有用的功能。
例如,myObject.myMethod()如果该方法调用无效,它可能会删除该方法调用。它忽略myObject可能为null 的可能性,从而导致NullPointerException。在某种程度上,这是件好事:优化的代码可能会抛出更少的异常。如果整个假设都是错误的,则必须使用-dontoptimize选项关闭优化。
9点击NEXT->Process,进行混淆处理。
测试结果如下,使用JD-GUI工具反编译已混淆的JAR
如上混淆处理的结果,可以看做一个重命名的过程,即实现名字(类名、方法名、属性名)的映射,但是无法改变程序主结构。
10
另外,如需后期更改配置,可在混淆处理结束后,Process->Save configguration,保存本次混淆处理配置文件(xxx.pro),以便后续更改。