apk反编译(6)用ProGuard 混淆、压缩代码,压缩资源。

1.android官方文档

  https://developer.android.com/studio/build/shrink-code  主要内容如下:

1.1 压缩代码

  • 混淆生成的文件:<module-name>/build/outputs/mapping/release/目录下 
  • 自定义要保留的代码,-keep与@keep
  • 解码混淆,使用 <sdk-root>/tools/proguard/ 下的工具。
  • 通过 Instant Run 启用代码压缩

1.2 压缩资源

  • shrinkResources
  • 自定义要保留的资源
  • 启用严格引用检查
  • 移除未使用的备用资源
  • 合并重复资源
  • 排查资源压缩问题 

1.3 官方proguard示例

   Android SDK/tools/proguard/proguard-android.txt 是默认混淆文件,同目录下的proguard-android-optimize.txt 是优化版本。

  在proguard/examples下有很多示例,如android.pro:

  1 #
  2 # This ProGuard configuration file illustrates how to process Android
  3 # applications.
  4 # Usage:
  5 #     java -jar proguard.jar @android.pro
  6 #
  7 # If you're using the Android SDK (version 2.3 or higher), the android tool
  8 # already creates a file like this in your project, called proguard.cfg.
  9 # It should contain the settings of this file, minus the input and output paths
 10 # (-injars, -outjars, -libraryjars, -printmapping, and -printseeds).
 11 # The generated Ant build file automatically sets these paths.
 12 
 13 # Specify the input jars, output jars, and library jars.
 14 # Note that ProGuard works with Java bytecode (.class),
 15 # before the dex compiler converts it into Dalvik code (.dex).
 16 
 17 # -injars  bin/classes
 18 # -injars  libs
 19 # -outjars bin/classes-processed.jar
 20 
 21 # -libraryjars /usr/local/android-sdk/platforms/android-9/android.jar
 22 #-libraryjars /usr/local/android-sdk/add-ons/google_apis-7_r01/libs/maps.jar
 23 # ...
 24 
 25 # Save the obfuscation mapping to a file, so you can de-obfuscate any stack
 26 # traces later on.
 27 
 28 -printmapping bin/classes-processed.map
 29 
 30 # You can print out the seeds that are matching the keep options below.
 31 
 32 #-printseeds bin/classes-processed.seeds
 33 
 34 # Preverification is irrelevant for the dex compiler and the Dalvik VM.
 35 
 36 -dontpreverify
 37 
 38 # Reduce the size of the output some more.
 39 
 40 -repackageclasses ''
 41 -allowaccessmodification
 42 
 43 # Switch off some optimizations that trip older versions of the Dalvik VM.
 44 
 45 -optimizations !code/simplification/arithmetic
 46 
 47 # Keep a fixed source file attribute and all line number tables to get line
 48 # numbers in the stack traces.
 49 # You can comment this out if you're not interested in stack traces.
 50 
 51 -renamesourcefileattribute SourceFile
 52 -keepattributes SourceFile,LineNumberTable
 53 
 54 # RemoteViews might need annotations.
 55 
 56 -keepattributes *Annotation*
 57 
 58 # Preserve all fundamental application classes.
 59 
 60 -keep public class * extends android.app.Activity
 61 -keep public class * extends android.app.Application
 62 -keep public class * extends android.app.Service
 63 -keep public class * extends android.content.BroadcastReceiver
 64 -keep public class * extends android.content.ContentProvider
 65 
 66 # Preserve all View implementations, their special context constructors, and
 67 # their setters.
 68 
 69 -keep public class * extends android.view.View {
 70     public <init>(android.content.Context);
 71     public <init>(android.content.Context, android.util.AttributeSet);
 72     public <init>(android.content.Context, android.util.AttributeSet, int);
 73     public void set*(...);
 74 }
 75 
 76 # Preserve all classes that have special context constructors, and the
 77 # constructors themselves.
 78 
 79 -keepclasseswithmembers class * {
 80     public <init>(android.content.Context, android.util.AttributeSet);
 81 }
 82 
 83 # Preserve all classes that have special context constructors, and the
 84 # constructors themselves.
 85 
 86 -keepclasseswithmembers class * {
 87     public <init>(android.content.Context, android.util.AttributeSet, int);
 88 }
 89 
 90 # Preserve the special fields of all Parcelable implementations.
 91 
 92 -keepclassmembers class * implements android.os.Parcelable {
 93     static android.os.Parcelable$Creator CREATOR;
 94 }
 95 
 96 # Preserve static fields of inner classes of R classes that might be accessed
 97 # through introspection.
 98 
 99 -keepclassmembers class **.R$* {
100   public static <fields>;
101 }
102 
103 # Preserve the required interface from the License Verification Library
104 # (but don't nag the developer if the library is not used at all).
105 
106 -keep public interface com.android.vending.licensing.ILicensingService
107 
108 -dontnote com.android.vending.licensing.ILicensingService
109 
110 # The Android Compatibility library references some classes that may not be
111 # present in all versions of the API, but we know that's ok.
112 
113 -dontwarn android.support.**
114 
115 # Preserve all native method names and the names of their classes.
116 
117 -keepclasseswithmembernames class * {
118     native <methods>;
119 }
120 
121 # Preserve the special static methods that are required in all enumeration
122 # classes.
123 
124 -keepclassmembers class * extends java.lang.Enum {
125     public static **[] values();
126     public static ** valueOf(java.lang.String);
127 }
128 
129 # Explicitly preserve all serialization members. The Serializable interface
130 # is only a marker interface, so it wouldn't save them.
131 # You can comment this out if your application doesn't use serialization.
132 # If your code contains serializable classes that have to be backward 
133 # compatible, please refer to the manual.
134 
135 -keepclassmembers class * implements java.io.Serializable {
136     static final long serialVersionUID;
137     static final java.io.ObjectStreamField[] serialPersistentFields;
138     private void writeObject(java.io.ObjectOutputStream);
139     private void readObject(java.io.ObjectInputStream);
140     java.lang.Object writeReplace();
141     java.lang.Object readResolve();
142 }
143 
144 # Your application may contain more items that need to be preserved; 
145 # typically classes that are dynamically created using Class.forName:
146 
147 # -keep public class mypackage.MyClass
148 # -keep public interface mypackage.MyInterface
149 # -keep public class * implements mypackage.MyInterface

2.proguard官方教程

  https://www.guardsquare.com/en/products/proguard/manual   包含:

  • sdk/tools/proguard/bin/proguardgui.sh 界面工具教程
  • 混淆选项列表
  • 混淆示例
  • 解混淆
  • Gradle plugin 配置教程
  • 等等  

  sdk/tools/proguard/docs/index.html 是官方教程本地版本。

2.1 混淆选项列表

  官方地址:https://www.guardsquare.com/en/products/proguard/manual/refcard

@filename Short for '-include filename'.
-include filename Read configuration options from the given file.
-basedirectory directoryname Specifies the base directory for subsequent relative file names.
-injars class_path

Specifies the program jars (or apks, aabs, aars, wars,

ears, jmods, zips, or directories).

-outjars class_path

Specifies the names of the output jars (or apks, aabs,

aars, wars, ears, jmods, zips, or directories).

-libraryjars class_path

Specifies the library jars (or apks, aabs, aars, wars, ears,

jmods, zips, or directories)

-skipnonpubliclibraryclasses Ignore non-public library classes.
-dontskipnonpubliclibraryclasses Don't ignore non-public library classes (the default).
-dontskipnonpubliclibraryclassmembers Don't ignore package visible library class members.
-keepdirectories [directory_filter]

Keep the specified directories in the output jars (or wars,

ears, zips, or directories).

-target version Set the given version number in the processed classes.
-forceprocessing Process the input, even if the output seems up to date.
-keep [,modifier,...] class_specification Preserve the specified classes and class members.
-keepclassmembers [,modifier,...] class_specification

Preserve the specified class members, if their classes

are preserved as well.

-keepclasseswithmembers [,modifier,...] class_specification

Preserve the specified classes and class members, if all

of the specified class members are present.

-keepnames class_specification

Preserve the names of the specified classes and class

members (if they aren't removed in the shrinking step).

-keepclassmembernamesclass_specification

Preserve the names of the specified class members

(if they aren't removed in the shrinking step).

-keepclasseswithmembernamesclass_specification

Preserve the names of the specified classes and class

members, if all of the specified class members are present

(after the shrinking step).

-if class_specification

Specify classes and class members that must be present

to activate the subsequent keep option.

-printseeds [filename]

List classes and class members matched by the various -keep options,

to the standard output or to the given file.

-dontshrink Don't shrink the input class files.
-printusage [filename]

List dead code of the input class files, to the standard output or

to the given file.

-whyareyoukeeping class_specification

Print details on why the given classes and class members are

being kept in the shrinking step.

-dontoptimize Don't optimize the input class files.
-optimizations optimization_filter The optimizations to be enabled and disabled.
-optimizationpasses n The number of optimization passes to be performed.
-assumenosideeffects class_specification

Assume that the specified methods don't have any side effects,

while optimizing.

-assumenoexternalsideeffectsclass_specification

Assume that the specified methods don't have any external side

effects, while optimizing.

-assumenoescapingparametersclass_specification

Assume that the specified methods don't let any reference parameters

escape to the heap, while optimizing.

-assumenoexternalreturnvaluesclass_specification

Assume that the specified methods don't return any external reference

values, while optimizing.

-assumevalues class_specification

Assume fixed values or ranges of values for primitive fields and

methods, while optimizing.

-allowaccessmodification

Allow the access modifiers of classes and class members to be

modified,while optimizing.

-mergeinterfacesaggressively Allow any interfaces to be merged, while optimizing.
-dontobfuscate Don't obfuscate the input class files.
-printmapping [filename]

Print the mapping from old names to new names for classes and

class members that have been renamed, to the standard output

or to the given file.

-applymapping filename Reuse the given mapping, for incremental obfuscation.
-obfuscationdictionary  filename   可自定义混淆字符

Use the words in the given text file as obfuscated field names and

method names.

-classobfuscationdictionary filename Use the words in the given text file as obfuscated class names.
-packageobfuscationdictionaryfilename Use the words in the given text file as obfuscated package names.
-overloadaggressively Apply aggressive overloading while obfuscating.
-useuniqueclassmembernames

Ensure uniform obfuscated class member names for subsequent

incremental obfuscation.

-dontusemixedcaseclassnames Don't generate mixed-case class names while obfuscating.
-keeppackagenames [package_filter] Keep the specified package names from being obfuscated.
-flattenpackagehierarchy[package_name]

Repackage all packages that are renamed into the single given

parent package.

-repackageclasses [package_name] Repackage all class files that are renamed into the single given package.
-keepattributes [attribute_filter]

Preserve the given optional attributes; typically 可选的属性有:[

Exceptions,InnerClassesSignatureDeprecated,

SourceFileSourceDirLineNumberTableLocalVariableTable

LocalVariableTypeTableSyntheticEnclosingMethod,  *Annotation*].

-keepparameternames Keep the parameter names and types of methods that are kept.
-renamesourcefileattribute [string] Put the given constant string in the SourceFileattributes.
-adaptclassstrings [class_filter]

Adapt string constants in the specified classes, based on the

obfuscated names of any corresponding classes.

-adaptresourcefilenames [file_filter]

Rename the specified resource files, based on the obfuscated

names of the corresponding class files.

-adaptresourcefilecontents [file_filter]

Update the contents of the specified resource files, based on the

obfuscated names of the processed classes.

-dontpreverify Don't preverify the processed class files.
-microedition Target the processed class files at Java Micro Edition.
-android Target the processed class files at Android.
-verbose Write out some more information during processing.
-dontnote [class_filter] Don't print notes about potential mistakes or omissions in the configuration.
-dontwarn [class_filter] Don't warn about unresolved references at all.
-ignorewarnings

Print warnings about unresolved references, but continue processing

anyhow.

-printconfiguration [filename]

Write out the entire configuration, in traditional ProGuard style,

to the standard output or to the given file.

-dump [filename]

Write out the internal structure of the processed class files,

to the standard output or to the given file.

-addconfigurationdebugging

Instrument the processed code with debugging statements that

print out suggestions for missing ProGuard configuration.

2.2 混淆选项通配符

  Keep 选项后面的匹配条件中,经常需要用到通配符,例如上面默认 proguard-android.txt 规则中的 void set*(***);。

  常见的通配符如下:

通配符描述
<init> 匹配所有构造函数
<fields> 匹配所有字段
<methods> 匹配所有方法,不包括构造函数
? 匹配任意单个字符
% 匹配任意原始数据类型,例如 boolean、int,但是不包括 void
* 匹配任意长度字符,但是不包括包名分隔符( . ),例如 android.support.* 不匹配 android.support.annotation.Keep
** 匹配任意长度字符,包括包名分隔符( . ),例如 android.support.** 匹配 support 包下的所有类
*** 匹配任意类型,包括原始数据类型、数组
匹配任意数量的任意参数类型

2.3 解混淆

  • debug版本

    如果有异常,直接点击行号就可以了。下图第25行。

    

  • release版本:

    找到对应版本的mapping.txt以及对应的异常日志,如果没有日志可以从android studio logcat中复制,

    1.打开ProGuard图形工具,选择左侧ReTrace 栏

    2.在右侧Mapping file中找到对应的mapping.txt

    3.把异常的堆栈信息复制到Obfuscated stack track中,或者点Load stack trace...按钮找到对应的文件。

    4.点右下角ReTrace!按钮。

    

  

3.哪些不应该混淆

  对于某些情况,ProGuard 会移除所有(并且只会移除)未使用的代码。不过,ProGuard 难以对许多情况进行正确分析,可能会移除应用真正需要的代码。

 

不该混淆

原因
当应用引用的类只来自 AndroidManifest.xml 文件时

混淆处理之后,类名就会被篡改,实际使用的类与 manifest 中注册的类并不匹配,出错。

如android常用4组件和Application类

当应用调用的方法来自 Java 原生接口 (JNI) 时

 
混淆后,会找不对对应的方法的实现

当应用在运行时(例如使用反射或自检)操作代码时

混淆后,找不到相应的名字,经常发生 NoSuchMethodException 和

 NoSuchFiledException 异常

序列化的类

经过混淆的"洗礼"之后,序列化之后的 value 对应的 key 已然变为没有意义的字段。

同时,反序列化的过程创建对象从根本上来说还是借助于反射,混淆之后 key会被改变,

所以也会违背我们预期的效果。

枚举

枚举类内部存在 values 方法,混淆后该方法会被重新命名,并抛出 NoSuchMethodException

Android 系统默认的混淆规则文件中已经添加了对于枚举类的处理,无需再去做额外工作。

自定义控件不需要被混淆

view的一些回调方法会被修改,导致出错或者显示不正常。 

JavaScript 调用 Java 的方法不应混淆

容易找不到方法

第三方库也不建议混淆

通常第3库以jar或者so方式存在,工具很难分析其中的调用关系。容易出错。

注解不能混淆

很多场景下注解被用作在运行时反射确定一些元素的特征,Android工程默认的混淆配置

已经包含了保留注解的配置。

 

4.自定义混淆字典

4.1 以自定义混淆字典选项

  • -obfuscationdictionary filename            混淆属性名、方法名参考的字典
  • -classobfuscationdictionary filename      混淆类参考的字典
  • -packageobfuscationdictionary filename  混淆包名参考的字典

4.2 混淆字典规则

  • 一行一个单词
  • 空行、空格忽略,
  • 标点符号无效
  • 重复的字符被忽略 
  • # 后面的字符忽略
  • 混淆属性和方法时,最好使用类对应的class文件中经存在的字符

4.3 android提供的字典示例

  在sdk/tools/proguard/examples/dictionaries/ 目录下有字典示例:

  

4.4 使用混淆字典

  可把一份字典示例文件(如 windows.txt)或者自定义一个字典文件复制到项目中,与proguard-rules.pro同级。

  在proguard-rules.pro中添加混淆选项,然后开始生成apk。

1 -obfuscationdictionary windows.txt
2 -classobfuscationdictionary windows.txt
3 -packageobfuscationdictionary windows.txt

  如:

 

5.实例

5.1 自定义要保留的类或包

  打完包后,用android studio分析下它。打开其中的dex文件,选择要保留的类,或者包 -> 右键  -> Generate Proguard keep rule 

  复制其中的内容到 proguard-rules.pro 下。

5.2 配置默认android混淆选项

  把  sdk/tools/proguard/examples/android.pro 内容复制到 proguard-rules.pro中

  把不必要的选项关掉。

5.3 打开混淆开关

  在release配置里打开混淆开关

 1     ...
 2     buildTypes {
 3         release {
 4             minifyEnabled true
 5             shrinkResources true
 6             zipAlignEnabled = true
 7             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
 8         }
 9         debug{
10             minifyEnabled false
11             shrinkResources false
12 //            useProguard false
13             zipAlignEnabled = true
14             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
15         }
16     }
17     ...

5.4 混淆走掉 Log.e 等

1 #-dontoptimize
2 -assumenosideeffects class android.util.Log {
3    public static boolean isLoggable(java.lang.String, int);
4    public static int v(...);
5    public static int i(...);
6    public static int w(...);
7    public static int d(...);
8    public static int e(...);
9 }

 

posted @ 2019-07-19 20:16  f9q  阅读(756)  评论(0编辑  收藏  举报