android proguard 代码混淆有什么用,Android代码混淆——Proguard

一、概述

ProGuard 会检测和移除封装应用中未使用的类、字段、方法和属性,包括自带代码库中的未使用项(这使其成为以变通方式解决64k 引用限制的有用工具)。ProGuard 还可优化字节码,移除未使用的代码指令,以及用短名称混淆其余的类、字段和方法。混淆过的代码可令您的 APK 难以被逆向工程,这在应用使用许可验证等安全敏感性功能时特别有用。

二、开启混淆

在module的build.gradle文件中添加minifyEnabled true。由于代码混淆会导致构建速度变慢,所有不要在调试的构建中使用。

buildTypes {

release {

minifyEnabled true

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

}

}

proguard-android.txt :是系统默认的混淆规则配置文件,位置在 /tools/proguard文件夹下。如果想进一步的压缩代码,可以使用位于相同目录下的proguard-android-optimize.txt文件,它还包含其他在字节码一级(方法内和方法间)执行分析的优化,以进一步减小APK大小和帮助提供运行速度。

prguard-rules.pro在每个module中都有一个该文件,用于自定义对应module的ProGuard规则。

三、混淆规则

从系统的proguard-android.txt文件来学习混淆规则。

# This file is no longer maintained and is not used by new (2.2+) versions of the

# Android plugin for Gradle. Instead, the Android plugin for Gradle generates the

# default rules at build time and stores them in the build directory.

# SDK中的该文件已经不再维护,从Gradle2.2+版本后,gradle插件会自动生成这个默认配置文件,位置位于/build/intermediates/proguard-files

# 混淆时不使用大小写混合,混淆后的类名为小写

-dontusemixedcaseclassnames

# 不跳过非公共的库的类

-dontskipnonpubliclibraryclasses

# 混淆后生成映射文件,map 类名->转化后类名的映射

-verbose

# Optimization is turned off by default. Dex does not like code run

# through the ProGuard optimize and preverify steps (and performs some

# of these optimizations on its own).

# 优化默认关闭,Dex不喜欢通过ProGuard的优化和预处理操作

-dontoptimize

-dontpreverify

# Note that if you want to enable optimization, you cannot just

# include optimization flags in your own project configuration file;

# instead you will need to point to the

# "proguard-android-optimize.txt" file instead of this one from your

# project.properties file.

# 保护代码中的Annotation不被混淆,这在JSON实体映射非常重要,如GSON

-keepattributes *Annotation*

-keep public class com.google.vending.licensing.ILicensingService

-keep public class com.android.vending.licensing.ILicensingService

# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native

# 保留所有的地方native方法不被混淆

-keepclasseswithmembernames class * {

native ;

}

# keep setters in Views so that animations can still work.

# see http://proguard.sourceforge.net/manual/examples.html#beans

# 不混淆View中的setXxx()和getXxx()方法,以保证熟悉动画能正常工作

-keepclassmembers public class * extends android.view.View {

void set*(***);

*** get*();

}

# We want to keep methods in Activity that could be used in the XML attribute onClick

# 不混淆Activity中参数是View的方法,保证xml绑定的点击事件可以正常工作

-keepclassmembers class * extends android.app.Activity {

public void *(android.view.View);

}

# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations

# 不混淆枚举类中的value()和valueOf()方法

-keepclassmembers enum * {

public static **[] values();

public static ** valueOf(java.lang.String);

}

# 不混淆Parcelable实现类中的CREATOR字段,以保证Parcelable机制正常工作

-keepclassmembers class * implements android.os.Parcelable {

public static final android.os.Parcelable$Creator CREATOR;

}

# 不混淆R文件中的所有静态字段,以保证正确找到每个资源id

-keepclassmembers class **.R$* {

public static ;

}

# The support library contains references to newer platform versions.

# Don't warn about those in case this app is linking against an older

# platform version. We know about them, and they are safe.

# 不对android.support包下的代码警告。(如果打包的版本低于support包下某些类的使用版本,会出现警告)

-dontwarn android.support.**

# Understand the @Keep support annotation.

# 不混淆Keep类

-keep class android.support.annotation.Keep

# 不混淆使用了注解的类和类成员

-keep @android.support.annotation.Keep class * {*;}

# 如果类中有使用了注解的方法,则不混淆类和类成员

-keepclasseswithmembers class * {

@android.support.annotation.Keep ;

}

# 如果类中有使用了注解的字段,则不混淆类和类成员

-keepclasseswithmembers class * {

@android.support.annotation.Keep ;

}

# 如果类中有使用了注解的构造函数,则不混淆类和类成员

-keepclasseswithmembers class * {

@android.support.annotation.Keep (...);

}

keep类关键字

关键字

含义

keep

保留类和类成员,防止被混淆或移除

keepnames

保留类和类成员,防止被混淆,但是没被引用的类成员会被移除

keepclassmembers

只保留类成员,防止被混淆或移除

keepclassmembersnames

只保留类成员,防止被混淆,但没被引用的类成员会被移除

keepclasseswithmembers

保留类和类成员,防止被混淆或移除,如果指定的类成员不存在还是会被混淆

keepclasseswithmembernames

保留类和类成员,防止被混淆,如果指定的类成员不存在还是会被混淆,没有被引用的类成员会被移除

通配符

通配符

含义

*

匹配任意长度字符,但不包含分隔符 “.”。例如一个类com.github.xch168.User,使用com.github.xch168.* 是可以匹配,但是com.github.* 不能匹配

**

匹配任意长度字符,包括分隔符"."。如使用com.github.**,匹配包下的所有内容,可以匹配到com.github.xch168.User。

***

匹配任意参数类型。例如*** getName(***) 可匹配String getName(String)

...

匹配任意长度的任意类型参数。例如void setName(…) 可以匹配void setName(String firstName, String secondName)

匹配类、接口中所有字段

匹配类、接口中所有方法

匹配类中所有构造函数

四、配置自己的混淆

WebView中使用JS调用,需要添加如下配置:

-keepclassmembers class fqcn.of.javascript.interface.for.webview {

public *;

}

不混淆某个特定的类和类中的所有成员

-keep class com.github.xch168.utils.CommonUtil { *; }

不混淆膜拜目录下的文件,例如使用Gson时,数据bean不能被混淆,需要添加如下配置:

-keep class com.github.xch168.model.** { *; }

保留泛型

-keepattributes Signature

保留用于调试堆栈跟中的行号信息

-keepattributes SourceFile,LineNumberTable

如果使用了上一行配置,还需要添加如下配置将源文件重命名为SourceFile,以便通过鼠标点击直达源文件:

-renamesourcefileattribute SourceFile

第三方库混淆案例

# Retrofit

# Retain generic type information for use by reflection by converters and adapters.

-keepattributes Signature

# Retain service method parameters.

-keepclassmembernames,allowobfuscation interface * {

@retrofit2.http.* ;

}

# Ignore annotation used for build tooling.

-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

# Okhttp

-dontwarn okhttp3.**

-dontwarn okio.**

-dontwarn javax.annotation.**

-dontwarn org.conscrypt.**

# A resource is loaded with a relative path so the package of this class must be preserved.

-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase

五、查看混淆结果

混淆打包后就会在/build/outputs/mapping/release目录生成混淆的相关文件。

dump.txt

说明 APK 中所有类文件的内部结构。

mapping.txt

提供原始与混淆过的类、方法和字段名称之间的转换。

seeds.txt

列出未进行混淆的类和成员。

usage.txt

列出从 APK 移除的代码。

混淆后可以用Android Studio的Analyze APK工具对混淆后的apk包进行分析。

c53dec86d1fd

apkanalyze.png

六、追溯Crash堆栈信息

混淆后的代码运行出错的堆栈信息如下,看不到具体的类名

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setOnClickListener(android.view.View$OnClickListener)' on a null object reference

at com.github.xch168.testas32.MainActivity.k(Unknown Source:7)

at com.github.xch168.testas32.MainActivity.onCreate(Unknown Source:9)

at android.app.Activity.performCreate(Activity.java:7130)

at android.app.Activity.performCreate(Activity.java:7121)

at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1262)

at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2905)

at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3060)

at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)

为了解决这个问题,可以使用\tools\proguard\bin下的proguardgui.bat脚本将Crash堆栈信息还原到混淆前的状态。步骤如下:

运行proguardgui.bat脚本,然后点击ReTrace

选择mapping.txt文件,位于/build/outputs/mapping/release

拷贝混淆后出错的堆栈信息

点击右下角的ReTrace!按钮,完成Crash堆栈信息的追溯

c53dec86d1fd

retrace.png

七、压缩资源

资源压缩只与代码压缩协同工作。代码压缩器移除所有未使用的代码后,资源压缩器便可确定应用仍然使用的资源。这在您添加包含资源的代码库时体现得尤为明显 - 您必须移除未使用的库代码,使库资源变为未引用资源,才能通过资源压缩器将它们移除。

开启资源压缩:

在 build.gradle 文件中将 shrinkResources 属性设置为 true(在用于代码压缩的 minifyEnabled 旁边)

android {

...

buildTypes {

release {

shrinkResources true

minifyEnabled true

proguardFiles getDefaultProguardFile('proguard-android.txt'),

'proguard-rules.pro'

}

}

}

八、自定义要保留的资源

如果您有想要保留或舍弃的特定资源,请在您的项目中创建一个包含 标记的 XML 文件,并在 tools:keep 属性中指定每个要保留的资源,在 tools:discard 属性中指定每个要舍弃的资源。这两个属性都接受逗号分隔的资源名称列表。您可以使用星号字符作为通配符。

tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"

tools:discard="@layout/unused2" />

将该文件保存在项目资源中,例如,保存在 res/raw/keep.xml。构建不会将该文件打包到 APK 之中。

九、总结

混淆后可以减小APK的大小,可以提高被反编译的难度,但是混淆后需要进行系统全面的测试。

参考链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值