Android Proguard / ZKM 点滴记录

邵捷
2023-12-01

简介

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、不能混淆的代码

顾名思义,不能混淆代码,否则会出错。

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

    没有相关方法,方法被混淆了,混淆过滤掉相关方法便可。

  • …..

ZKM

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%
 类似资料: