Android ProGuard 代码混淆详解

QuibblerQuibbler 2020-01-07 约 14 分钟 203 次阅读

Android ProGuard 代码混淆详解

ProGuard 是 Android 开发中常用的代码混淆工具,它通过检测和移除未使用的类、字段、方法,优化字节码,以及用短名称混淆类名和方法名,使 APK 难以被逆向工程。本文从启用配置到混淆规则编写,全面介绍 ProGuard 的使用方法。

1、ProGuard 的作用

ProGuard 提供三大核心功能:
       • 压缩(Shrink):检测并移除未使用的类、字段、方法和属性,包括依赖库中未使用的项,也是解决 64k 引用限制的变通方式
       • 优化(Optimize):优化字节码,移除未使用的代码指令,减小 APK 体积
       • 混淆(Obfuscate):用短名称重命名类、字段和方法,增加逆向工程难度

2、启用 ProGuard

在模块级 build.gradle 的 release 构建类型中启用混淆:

android {
    buildTypes {
        release {
            minifyEnabled true       // 启用代码压缩和混淆
            zipAlignEnabled true     // Zipalign 优化
            shrinkResources true    // 移除无用的资源文件,必须和 minifyEnabled 一起使用

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

注意事项:
       • 代码混淆会拖慢构建速度,应仅在 release 构建中启用
       • Android Studio 使用 Instant Run 时会自动停用 ProGuard
       • shrinkResources 必须和 minifyEnabled 一起使用

3、配置文件说明

ProGuard 涉及两个配置文件:

3.1、proguard-android.txt 与 proguard-android-optimize.txt

两个文件的核心区别在于是否开启优化:
       • proguard-android.txt:配置了 -dontoptimize 和 -dontpreverify,关闭优化和预校验
       • proguard-android-optimize.txt:开启了优化,并配置了经过验证的安全优化算法

# proguard-android.txt
-dontoptimize
-dontpreverify

# proguard-android-optimize.txt
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5
-allowaccessmodification
-dontpreverify

建议使用 proguard-android-optimize.txt,但需注意优化可能带来的风险,务必充分测试。

3.2、proguard-rules.pro

项目自定义的混淆规则文件,用于指定需要保留不混淆的类、方法和字段。

4、常用混淆规则

4.1、保留指令说明

ProGuard 提供多种保留指令:
       • -keep:防止类和成员被移除或被重命名
       • -keepnames:防止类和成员被重命名,但允许被移除
       • -keepclassmembers:仅防止成员被移除或重命名
       • -keepclasseswithmembers:保留类中包含指定成员的类及其成员
       • -keepclassmembernames:仅防止成员被重命名

4.2、保留 Android 基础组件

# 保留 Activity 中参数是 View 的方法(避免 layout 中 onClick 指定方法无法调用)
-keepclassmembers class * extends android.app.Activity {
    public void *(android.view.View);
}

# 保留 Native 方法
-keepclasseswithmembernames class * {
    native <methods>;
}

# 保留自定义 View
-keep public class * extends android.view.View {
    *** get*();
    void set*(***);
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

# 保留枚举
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# 保留 Parcelable
-keepclassmembers class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator CREATOR;
}

# 保留 Serializable
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient <fields>;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

4.3、保留第三方库

# 保留 Retrofit
-keepclasseswithmembers class * {
    @retrofit2.http.* <methods>;
}

# 保留 Gson 序列化的实体类
-keep class com.example.bean.** { *; }

# 保留 OkHttp
-dontwarn okhttp3.**
-dontwarn okio.**
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }

# 保留 Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep class * extends com.bumptech.glide.module.AppGlideModule { <init>(android.content.Context, com.bumptech.glide.GlideBuilder); }
-keep public enum com.bumptech.glide.load.ImageHeaderParser { **[] values(); public ** valueOf(java.lang.String); }

# 保留 ButterKnife
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class * { @butterknife.* <fields>; }

4.4、保留反射相关类

# 保留与反射相关的类
-keep class com.example.reflect.** { *; }

# 保留内部类(如 Activity 中的 JSInterface)
-keepclasseswithmembers class com.example.MainActivity$JSInterface {
    <methods>;
}

5、混淆后定位问题

启用混淆后,Crash 堆栈中的类名和方法名会被替换为无意义的短名称,增加调试难度。

5.1、保留映射文件

在 build.gradle 中配置保留 mapping 文件:

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
                           'proguard-rules.pro'

            // 保留 mapping 文件,方便反混淆
            mappingProguardFile "build/outputs/mapping/release/mapping.txt"
        }
    }
}

5.2、使用 retrace 工具还原堆栈

SDK 中的 retrace 工具可以根据 mapping 文件将混淆后的堆栈还原为原始名称:

# 使用 retrace.sh 还原混淆后的堆栈
retrace.sh mapping.txt stacktrace.txt > original_stacktrace.txt

6、常见问题与注意事项

6.1、常见混淆导致的错误

• Gson 反序列化失败:实体类被混淆后字段名改变,需用 -keep 保留实体类
       • Retrofit 方法传参失败:需添加 -keepclasseswithmembers 保留 Retrofit 接口方法
       • ButterKnife 绑定失败:需保留注解相关的类和字段
       • 反射调用失败:反射使用的类和成员需保留
       • JNI 方法找不到:Native 方法对应的 Java 方法需保留

6.2、调试建议

• 开发阶段使用 debug 构建(不启用混淆)
       • release 构建前充分测试,覆盖所有功能模块
       • 保存每次发布的 mapping.txt 文件,与对应版本 APK 关联
       • 使用 -dontwarn 忽略第三方库的警告,避免构建失败

7、总结

ProGuard 是 Android 应用安全的重要防线,通过代码压缩、优化和混淆三重机制保护应用。正确配置混淆规则是关键,需要根据项目使用的第三方库逐一添加保留规则。建议在项目初期就建立混淆规则模板,随着依赖库的增减持续维护,确保每次 release 构建都能正常工作。

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w