Java代码是非常容易反编译的。为了很好的保护Java源代码,我们往往会对编译好的class文件进行混淆处理。
ProGuard是一个混淆代码的开源项目。它的主要作用就是混淆,当然它还能对字节码进行缩减体积、优化等
官网网址:http://proguard.sourceforge.net
1、原理
混淆就是对发布出去的程序进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能,而混淆后的代码很难被反编译,即使反编译成功也很难得出程序的真正语义。被混淆过的程序代码,仍然遵照原来的档案格式和指令集,执行结果也与混淆前一样,只是混淆器将代码中的所有变量、函数、类的名称变为简短的英文字母代号,在缺乏相应的函数名和程序注释的况下,即使被反编译,也将难以阅读。同时混淆是不可逆的,在混淆的过程中一些不影响正常运行的信息将永久丢失,这些信息的丢失使程序变得更加难以理解。
混淆器的作用不仅仅是保护代码,它也有精简编译后程序大小的作用。由于以上介绍的缩短变量和函数名以及丢失部分信息的原因, 编译后 jar 文件体积大约能减少25%
2、语法 详见: http://proguard.sourceforge.net
输入输出选项
-include {filename} 从给定的文件中读取配置参数
-basedirectory {directoryname} 指定基础目录为以后相对的档案名称
-injars {class_path} 指定要处理的应用程序jar,war,ear和目录
-outjars {class_path} 指定处理完后要输出的jar,war,ear和目录的名称
-libraryjars {classpath} 指定要处理的应用程序jar,war,ear和目录所需要的程序库文件
-dontskipnonpubliclibraryclasses 指定不去忽略非公共的库类。
-dontskipnonpubliclibraryclassmembers 指定不去忽略包可见的库类的成员。
保留选项
-keep {Modifier} {class_specification} 保护指定的类文件和类的成员
-keepclassmembers {modifier} {class_specification} 保护指定类的成员,如果此类受到保护他们会保护的更好
-keepclasseswithmembers {class_specification} 保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在。
-keepnames {class_specification} 保护指定的类和类的成员的名称
-keepclassmembernames {class_specification} 保护指定的类的成员的名称
-keepclasseswithmembernames {class_specification} 保护指定的类和类的成员的名称,如果所有指定的类成员出席
-printseeds {filename} 列出类和类的成员-keep选项的清单,标准输出到给定的文件
压缩
-dontshrink 不压缩输入的类文件
-printusage {filename}
-whyareyoukeeping {class_specification}
优化
-dontoptimize 不优化输入的类文件
-assumenosideeffects {class_specification} 优化时假设指定的方法
-allowaccessmodification 优化时允许访问并修改有修饰符的类和类的成员
混淆
-dontobfuscate 不混淆输入的类文件
-printmapping {filename}
-applymapping {filename} 重用映射增加混淆
-obfuscationdictionary {filename} 使用给定文件中的关键字作为要混淆方法的名称
-overloadaggressively 混淆时应用侵入式重载
-useuniqueclassmembernames 确定统一的混淆类的成员名称来增加混淆
-flattenpackagehierarchy {package_name} 重新包装所有重命名的包并放在给定的单一包中
-repackageclass {package_name} 重新包装所有重命名的类文件中放在给定的单一包中
-dontusemixedcaseclassnames 混淆时不会产生形形色色的类名
-keepattributes {attribute_name,…} 保护给定的可选属性,例如Exceptions,InnerClasses,Signature,Deprecated,
SourceFile,LineNumberTable,Annotation,EnclosingMethod
-renamesourcefileattribute {string} 设置源文件中给定的字符串常量
* android 通用实例 *
-injars dist/jpush-sdk-dev.jar
-outjars dist/jpush-sdk-release.jar
-libraryjars /Users/zhangfl/local/android-sdk-macosx/platforms/android-23/android.jar
-ignorewarnings # 忽略警告,避免打包时某些警告出现
-optimizationpasses 5 # 指定代码的压缩级别
-dontusemixedcaseclassnames # 是否使用大小写混合
-dontskipnonpubliclibraryclasses # 是否混淆第三方jar
-dontpreverify # 混淆时是否做预校验
-verbose # 混淆时是否记录日志
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 混淆时所采用的算法
-dontwarn android.support.v4.** #缺省proguard 会检查每一个引用是否正确,但是第三方库里面往往有些不会用到的类,没有正确引用。如果不配置的话,系统就会报错。
-dontwarn android.os.**
-keep class android.support.v4.** { *; } # 保持哪些类不被混淆
-keep class android.os.**{*;}
-keep interface android.support.v4.app.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.support.v4.widget
-keepclasseswithmembernames class * { # 保持 native 方法不被混淆
native <methods>;
}
-keepclasseswithmembers class * { # 保持自定义控件类不被混淆
public <init>(android.content.Context,android.util.AttributeSet);
}
-keepclassmembers class * extends android.app.Activity { //保持类成员
public void *(android.view.View);
}
-keepclassmembers enum * { # 保持枚举 enum 类不被混淆
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {# 保持 Parcelable 不被混淆
public static final android.os.Parcelable$Creator *;
}
-keepclassmembers class **.R$* {
public static <fields>;
}
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}
-keep class MyClass; # 保持自己定义的类不被混淆
3、生成的文件
mapping.txt
列出了原始的类,方法,和字段名与混淆后代码之间的映射。
dump.txt
描述apk内所有class文件的内部结构。
seeds.txt
列出了没有被混淆的类和成员。
usage.txt
列出了源代码中被删除在apk中不存在的代码。
当我们发布的release版本的程序出现bug时,可以通过以上文件(特别是mapping.txt)文件找到错误原始的位置,进行bug修改。同时,可能一开始的proguard配置有错误,也可以通过错误日志,根据这些文件,找到哪些文件不应该混淆,从而修改proguard的配置。
PS:重新release编译后,这些文件会被覆盖,所以每次发布程序,最好都保存一份配置文件。
4、不能混淆的代码
顾名思义,不能混淆代码,否则会出错。
反射的地方
系统接口
Jni接口等
android proguard 官方demo : http://proguard.sourceforge.net
5、常见错误
官网bug解决方法:
http://proguard.sourceforge.net/index.html#manual/troubleshooting.html
使用gson包解析数据时,出现missing type parameter异常
类型转换错误
-keepattributes Signature
空指针异常
混淆过滤掉相关类与方法
java.lang.reflect.UndeclaredThrowableException
内存溢出和无法写入堆栈
Error: Unable to access jarfile ..\lib\proguard.jar
路径问题
java.lang.NoSuchMethodError
没有相关方法,方法被混淆了,混淆过滤掉相关方法便可。
Zelix KlassMaster是一个来自Zelix Pty Ltd的商业混淆器,
它的保护功能非常强大,可以进行符号混淆和控制混淆,支持字符串的复杂加密保护,堆栈混乱,支持异常重构,支持增量混淆.
zkm script
详见: http://www.zelix.com/klassmaster/docs/langZKMScript.html
demo:
//sets the classpath that will be used by Zelix KlassMaster
classpath
"%java.dir%\\jre\\lib\\resources.jar"
"%java.dir%\\jre\\lib\\rt.jar"
"%java.dir%\\jre\\lib\\jsse.jar"
"%java.dir%\\jre\\lib\\jce.jar"
"%java.dir%\\jre\\lib\\charsets.jar"
"%sdk.dir%\\platforms\\%android-target%\\android.jar";
//load all classes in a jar file
open "%project.dir%/dist/%jpush-sdk-name%-%jpush-sdk-version-name%.jar";
//Don't rename anything
exclude *.^* +;
//Just String encrypt
obfuscate changeLogFileIn=""
changeLogFileOut="%proguard.dir%/ChangeLog.txt"
aggressiveMethodRenaming=false
keepInnerClassInfo=true
keepGenericsInfo=true
obfuscateFlow=none
encryptStringLiterals=flowObfuscate
lineNumbers=delete
exceptionObfuscation=none
autoReflectionHandling=none;
//saves all opened classes to the specified directory
saveAll archiveCompression=all "%project.dir%/dist/zkm";
> ps: 可以通过 %var% 来引用变量, eg %project.dir%