Android 逆向系列:Android APK 代碼混淆

你能攻擊我的 app,那我肯定有防守的策略,接下來我們介紹一下 Android 中的混淆技術(shù)

注意:下面演示均是在 mac 下進(jìn)行

Github Demo 地址:github.com/sweetying52…

一、jadx 介紹

在此之前,我想介紹另外一款反編譯工具:jadx,它相當(dāng)于是 apktool + dex2jar + jd-gui 的結(jié)合體,既能反編譯代碼也能反編譯資源,關(guān)鍵使用起來還特別簡(jiǎn)單,你只需要將文件拖進(jìn)來即可,一定程度上提高了我們的開發(fā)效率

Github 地址:github.com/skylot/jadx

1.1、jadx 特點(diǎn)

1、能將 APK,AAR,JAR,DEX,AAB,ZIP 等文件中的代碼反編譯為 Java 類

2、能反編譯 APK,AAR,AAB,ZIP 中的資源

1.2、jadx 安裝

1、安裝 jadx,推薦使用 brew 去安裝,執(zhí)行如下命令:

brew install jadx

使用 brew 安裝的好處就是 mac 會(huì)給你自動(dòng)配置好環(huán)境變量,你只需要專注軟件的使用即可,等待安裝完成在驗(yàn)證一下

2、jadx 驗(yàn)證

在 Terminal 輸入如下命令:

jadx --version

如果打印出了版本號(hào)就證明安裝成功了:

1.3、jadx 使用

這里我們直接使用 jadx 提供的可視化界面進(jìn)行操作

1、在 Terminal 輸入如下命令:

jadx-gui

此時(shí)就會(huì)打開 jadx 的可視化界面了:

2、將你需要反編譯的文件拖入即可查看反編譯的代碼和資源了,如下圖:

二、混淆 APK 代碼

2.1、準(zhǔn)備工作

首先我們先做一些準(zhǔn)備工作

1、添加一些類:

//1、新建 Utils.java 文件,創(chuàng)建 Utils 類
public class Utils {

    public void methodNormal(){
        String logMessage = "this is normal method";
        logMessage = logMessage.toLowerCase();
        System.out.println(logMessage);
    }

    public void methodUnused(){
        String logMessage = "this is unused method";
        logMessage = logMessage.toLowerCase();
        System.out.println(logMessage);
    }
}

//2、新建 NativeUtils.java 文件,創(chuàng)建 NativeUtils 類
public class NativeUtils {

    public static native void methodNative();

    public static void methodNotNative(){
        String logMessage = "this is not native method";
        logMessage = logMessage.toLowerCase();
        System.out.println(logMessage);
    }
}

//3、新建 MyFragment.java 文件,創(chuàng)建 MyFragment 類
public class MyFragment extends Fragment {

    private String toastTips = "toast in MyFragment";

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_layout,container,false);
        methodWithGlobalVariable();
        methodWithLocalVariable();
        return rootView;
    }

    private void methodWithGlobalVariable() {
        Toast.makeText(getActivity(), toastTips, Toast.LENGTH_SHORT).show();
    }

    private void methodWithLocalVariable() {
        String logMessage = "log in MyFragment";
        logMessage = logMessage.toLowerCase();
        System.out.println(logMessage);
    }
}

2、接著在 MainActivity 中進(jìn)行引用

public class MainActivity extends AppCompatActivity {

    String toastTips = "toast in MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getSupportFragmentManager().beginTransaction().add(R.id.flFragmentContainer,new MyFragment()).commit();
        //1、Utils 下的方法調(diào)用
        Utils utils = new Utils();
        utils.methodNormal();
        //2、NativeUtils 下的方法調(diào)用
        try {
            NativeUtils.methodNative();
            NativeUtils.methodNotNative();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        //3、第三方庫下工具類的方法調(diào)用
        int result = StringUtils.getLength("erdai666");
        System.out.println(result);
        //4、MainActivity 下的 methodWithGlobalVariable 方法調(diào)用
        methodWithGlobalVariable();
        //5、MainActivity 下的 methodWithLocalVariable 方法調(diào)用
        methodWithLocalVariable();
    }

    private void methodWithGlobalVariable() {
        Toast.makeText(this, toastTips, Toast.LENGTH_SHORT).show();
    }

    private void methodWithLocalVariable() {
        String logMessage = "log in MainActivity";
        logMessage = logMessage.toLowerCase();
        System.out.println(logMessage);
    }
}

好的,到這里準(zhǔn)備工作已經(jīng)基本完成,接下來我們對(duì) APK 中的代碼進(jìn)行混淆

2.2、開啟混淆打 APK 包

1、在 app 的 build.gradle 文件中的 android 閉包下 的 release 閉包中開啟代碼混淆:

android {
    buildTypes {
        release {
            //開啟代碼混淆
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

上述我們僅是把 minifyEnabled 改為 true 即開啟了代碼混淆,非常的簡(jiǎn)單

另外需要注意: 這里是在 release 閉包內(nèi)進(jìn)行配置的,因此只有打出正式版的 APK 才會(huì)進(jìn)行混淆,debug 版的 APK 是不會(huì)混淆的。當(dāng)然這也是非常合理的,因?yàn)?debug 版的 APK 文件我們只會(huì)用來內(nèi)部測(cè)試,不用擔(dān)心被人破解。

2、接下來打一個(gè)正式的 APK 包

1、在 Android Studio 導(dǎo)航欄中點(diǎn)擊 Build -> Generate Signed Bundle or APK,選擇 APK

2、然后選擇簽名文件并輸入密碼,如果沒有簽名文件就創(chuàng)建一個(gè)

3、點(diǎn)擊 next 選擇打 release 包,最終點(diǎn)擊 Finish 完成打包

4、生成的 APK 會(huì)自動(dòng)存放在 app/release/ 目錄下

Tips: 我們可以在 app 的 build.gradle 文件中添加簽名文件配置,后續(xù)就可以直接通過 ./gradlew assembleRelease 命令或者 AndroidStudio 右側(cè)的 Gradle 可視化界面去操作:

android {
    //1、聲明簽名文件
    signingConfigs{
        release{
            storeFile file('../Certificate')
            storePassword 'erdai666'
            keyAlias 'key0'
            keyPassword 'erdai666'
        }
    }

    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            //2、配置簽名文件
            signingConfig signingConfigs.release
        }
    }
}

需要注意

1、使用 AndroidStudio 導(dǎo)航欄 Generate Signed Bundle or APK 方式生成的 APK 在:app/release/ 目錄下

2、使用 ./gradlew assembleRelease 命令或者 AndroidStudio 右側(cè)的 Gradle 可視化界生成的 APK 在:app/build/outputs/apk/ 目錄下

3、接著使用 jadx 打開當(dāng)前 APK,如下圖所示:

很明顯我們代碼混淆的功能已經(jīng)生效了。

2.3、混淆文件介紹

下面我們嘗試來閱讀一下混淆后之前準(zhǔn)備的那些類:

MainActivity

可以看到:

1、MyFragment 被混淆了

2、Utils 下的方法調(diào)用:直接是把方法里面的內(nèi)容拷貝到了方法的調(diào)用處

3、NativeUtils 下的方法調(diào)用:1、Native 方法還是正常的調(diào)用 2、非 Native 方法則是把方法里面的內(nèi)容拷貝到了方法的調(diào)用處

4、第三方庫下工具類的方法調(diào)用:直接是將方法的結(jié)果填充到了調(diào)用處

5、MainActivity 中的成員方法:直接是把成員方法里面的內(nèi)容拷貝到了方法的調(diào)用處

6、MainActivity 類名是沒有混淆的,onCreate 方法也沒有被混淆,但定義的成員變量,局部變量被混淆了

Utils

Utils 類直接沒有了

NativeUtils

可以看到:

1、NativeUtils 類名沒有被混淆,其中聲明成 native 的方法也沒有被混淆

2、非 Native 方法直接沒有了,方法的內(nèi)容拷貝到了方法的調(diào)用處

MyFragment

可以看到:

1、所有的方法名,成員變量,局部變量都被混淆了

2、MyFragment 中的成員方法:直接是把成員方法里面的內(nèi)容拷貝到了方法的調(diào)用處

接下來在分析一下上面的混淆結(jié)果

1、Utils 直接沒有了,因?yàn)樗徽{(diào)用的方法內(nèi)容直接拷貝到了方法的調(diào)用處。另外一個(gè)方法沒有被調(diào)用,會(huì)被認(rèn)為是多余的代碼,在打包的時(shí)候就給移除掉了,不僅僅是方法,沒有調(diào)用的資源同樣會(huì)被移除,這樣的好處是可以減少 APK 的體積

2、NativeUtils 類名沒有被混淆,這是由于它有一個(gè)聲明成 native 的方法。只要一個(gè)類中有存在 native 方法,它的類名就不會(huì)被混淆,native 方法的方法名也不會(huì)被混淆,因?yàn)?C 或 C++ 代碼要通過包名+類名+方法名來進(jìn)行交互。 但是類中別的代碼還是會(huì)被混淆,它的非 Native 方法直接沒有了,因?yàn)榉椒ɡ锩娴膬?nèi)容拷貝到了方法的調(diào)用處

3、MyFragment 是混淆的比較徹底的,基本沒有任何保留,連生命周期方法也被混淆了,F(xiàn)ragment 怎么說也算是一個(gè)系統(tǒng)組件吧,搞的一點(diǎn)面子都沒有??

4、MainActivity 的保留程度就比 MyFragment 好多了,至少像類名,生命周期方法都沒有被混淆,這是因?yàn)椋?strong>凡是需要在 AndroidManifest.xml 中注冊(cè)的所有類的類名以及從父類重寫的方法名都不會(huì)被混淆。 因此,除了 Activity 之外,這份規(guī)則同樣適用于:Service,BroadcastReceiver 和 ContentProvider

5、引入的第三方庫也被混淆了,上述可以看到直接是把方法調(diào)用的結(jié)果給填充了進(jìn)來

2.4、默認(rèn)混淆規(guī)則介紹

那么這些混淆規(guī)則是在哪里定義的呢?其實(shí)就是剛才在 build.gradle 的 release 閉包下配置的 proguard-android-optimize.txt 文件,這個(gè)文件存放于Android SDK/tools/proguard/目錄下:

看一眼它的具體內(nèi)容:

# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html
#
# 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.

# Optimizations: If you don't want to optimize, use the
# proguard-android.txt configuration file instead of this one, which
# turns off the optimization flags.  Adding optimization introduces
# certain risks, since for example not all optimizations performed by
# ProGuard works on all versions of Dalvik.  The following flags turn
# off various optimizations known to have issues, but the list may not
# be complete or up to date. (The "arithmetic" optimization can be
# used if you are only targeting Android 2.0 or later.)  Make sure you
# test thoroughly if you go this route.
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5
-allowaccessmodification
-dontpreverify

# The remainder of this file is identical to the non-optimized version
# of the Proguard configuration file (except that the other file has
# flags to turn off optimization).

-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose

-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
-keepclasseswithmembernames class * {
    native <methods>;
}

# keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
-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
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

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

-keepclassmembers class **.R$* {
    public static <fields>;
}

# 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.
-dontwarn android.support.**

# Understand the @Keep support annotation.
-keep class android.support.annotation.Keep

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

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}

這個(gè)就是默認(rèn)的混淆配置文件了,我們來逐行解釋一下:

# 啟動(dòng)優(yōu)化相關(guān)的一些配置
# 指定更精細(xì)級(jí)別的優(yōu)化
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
# 表示對(duì)代碼優(yōu)化的次數(shù),一般為 5
-optimizationpasses 5
# 允許改變作用域
-allowaccessmodification
# 關(guān)閉預(yù)驗(yàn)證
-dontpreverify

# 表示混淆時(shí)不使用大小寫混合類名
-dontusemixedcaseclassnames

# 表示不跳過 library 中的非 public 類
-dontskipnonpubliclibraryclasses

# 表示打印混淆的詳細(xì)信息
-verbose

#表示對(duì)注解中的參數(shù)進(jìn)行保留
-keepattributes *Annotation*

# 表示不混淆如下聲明的兩個(gè)類,這兩個(gè)類基本上也用不上,是接入 Google 原生的一些服務(wù)時(shí)使用的
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService

# 表示不混淆任何包含 native 方法的類名以及 native 方法名,這個(gè)和剛才驗(yàn)證的結(jié)果是一致的
-keepclasseswithmembernames class * {
    native <methods>;
}

# 表示不混淆 View 中的 setXXX() 和 getXXX() 方法,因?yàn)閷傩詣?dòng)畫需要有相應(yīng)的 setter 和 getter 方法實(shí)現(xiàn)
-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();
}

# 表示不混淆 Activity 中參數(shù)是 View 的方法,因?yàn)橛羞@么一種用法,在 XML 中配置 android:onClick="btnClick" 屬性,混淆就找
# 不到了
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

# 表示不混淆枚舉的 values() 和 valueOf() 方法
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# 表示不混淆 Parcelable 實(shí)現(xiàn)類中的 CREATOR 字段,毫無疑問,CREATOR 字段是絕對(duì)不能改變的,包括大小寫都不能變,不然整個(gè)
# Parcelable 工作機(jī)制都會(huì)失效
-keepclassmembers class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator CREATOR;
}

# 表示不混淆 R 文件中的所有靜態(tài)字段,我們都知道 R 文件是通過字段來記錄每個(gè)資源 id ,字段名如果被混淆,id 就找不到了
-keepclassmembers class **.R$* {
    public static <fields>;
}

# 表示對(duì) android.support 包下的代碼不警告,因?yàn)?support 包中的所有代碼都在兼容性上做了足夠的判斷,因此不用擔(dān)心代碼會(huì)出問題
# 所以直接忽略警告就可以了
-dontwarn android.support.**

# 表示不混淆 android.support.annotation.Keep 這個(gè)注解類的所有東西
-keep class android.support.annotation.Keep

# 表示不混淆使用了 class android.support.Keep 注解的類的所有東西
-keep @android.support.annotation.Keep class * {*;}

# 表示不混淆類名和類中使用了 class android.support.Keep 注解的方法
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}

# 表示不混淆類名和類中使用了 class android.support.Keep 注解的屬性
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}

# 表示不混淆類名和類中使用了 class android.support.Keep 注解的構(gòu)造方法
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}

2.4.1、proguard-android-optimize.txt 和 proguard-android.txt 區(qū)別

之前一些 AGP 老版本,我們新建工程默認(rèn)使用的是:proguard-android.txt,那么它和 proguard-android-optimize.txt 有啥區(qū)別呢?

從字面的維度看,就多了一個(gè) optimize(優(yōu)化)這個(gè)單詞,實(shí)際就是多了優(yōu)化這一部分,proguard-android-optimize.txt 相對(duì)于 proguard-android.txt 開啟了優(yōu)化相關(guān)的配置:

# proguard-android-optimize.txt 新增了以下優(yōu)化規(guī)則
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5
-allowaccessmodification
-dontpreverify

# proguard-android-optimize.txt 刪除了關(guān)閉優(yōu)化指令的配置
# -dontoptimize

好了,上述就是 proguard-android-optimize.txt 文件中所有的默認(rèn)配置,而我們混淆代碼也是按照這些配置的規(guī)則來進(jìn)行混淆的。經(jīng)過上面的講解,相信大家對(duì)這些配置的內(nèi)容基本都能理解了。不過 Proguard 語法中還真有幾處非常難理解的地方,下面和大家分享一下這些難懂的語法部分

2.5、Proguard 疑難語法介紹

Proguard 中一共有三組六個(gè) keep 關(guān)鍵字,很多人搞不清楚他們的區(qū)別,我們通過一個(gè)表格直觀的來看一下:

關(guān)鍵字 描述
keep 保留類和類中的成員不被混淆或移除
keepnames 在 keep 的基礎(chǔ)上,如果成員沒有被引用,則會(huì)被移除
keepclassmembers 保留類成員不被混淆或移除
keepclassmembernames 在 keepclassmembers 基礎(chǔ)上,如果成員沒有被引用,則會(huì)被移除
keepclasseswithmembers 保留類和類中的成員不被混淆或移除,前提是類中的成員必須存在,否則還是會(huì)被混淆
keepclasseswithmembernames 在 keepclasseswithmembers 基礎(chǔ)上,如果成員沒有被引用,則會(huì)被移除

除此之外,Proguard 的通配符也比較讓人難懂,proguard-android-optimize.txt 中就使用到了很多通配符,我們來看一下它們之間的區(qū)別:

通配符 描述
<field> 匹配類中所有的字段
<method> 匹配類中所有的方法
init 匹配類中所有的構(gòu)造方法
* 匹配任意長(zhǎng)度字符,但不包含分隔符.,例如我們完成類名是:com.dream.androidreversedemo.MainActivity,使用 com.* 或者 com.dream.* 是無法匹配的,因?yàn)?* 無法匹配報(bào)名中的分隔符,正確的匹配方式是com.dream.*.*或者com.dream.androidreversedemo.*
** 匹配任意長(zhǎng)度字符,包含分隔符.,上面匹配規(guī)則我們可以使用com.**或者com.dream.**來進(jìn)行匹配
*** 匹配任意參數(shù)類型。例如void set*(***)就能匹配傳入任意的參數(shù)類型,***get(*)就能匹配任意返回值的類型
... 匹配任意長(zhǎng)度的任意類型參數(shù),例如void test(...)就能匹配void test(String str)或者void test(int a,double b)這些方法

ok,學(xué)習(xí)了疑難語法,下面我們來一道練習(xí)題??:保留實(shí)現(xiàn)了 com.dream.test.BaseJsonData 接口的類的所有信息不被混淆?

一個(gè)清晰的思路很重要,仔細(xì)分析一下:

1、首先我們要保證 com.dream.test.BaseJsonData 接口不被混淆

2、然后保證實(shí)現(xiàn) com.dream.test.BaseJsonData 接口的類不被混淆

3、最后就是匹配類中所有的成員不被混淆,可以使用通配符 *

我們可以這么寫:

# 保證  com.dream.test.BaseJsonData 接口不被混淆
-keep class com.dream.test.BaseJsonData
# 保證實(shí)現(xiàn) com.dream.test.BaseJsonData 接口的類不被混淆
# 匹配類中所有的成員不被混淆,可以使用通配符 *
-keep class * implements com.dream.test.BaseJsonData{
    *;
}

2.6、自定義混淆規(guī)則

回到項(xiàng)目中,剛才打出的 APK 雖然已經(jīng)成功混淆了,但是混淆的規(guī)則是按照 proguard-android-optimize.txt 中默認(rèn)的規(guī)則來的,當(dāng)然我們可以修改 proguard-android-optimize.txt 中的規(guī)則,但是這樣做會(huì)對(duì)本機(jī)上所有項(xiàng)目的混淆規(guī)則都生效,那么有沒有什么好的辦法只針對(duì)當(dāng)前項(xiàng)目做混淆規(guī)則修改呢?

答:對(duì) proguard-rules.pro 文件進(jìn)行自定義混淆規(guī)則編寫

可以看到 android 閉包下 release 閉包的配置,實(shí)際上配置了兩個(gè)混淆文件,一個(gè)就是我們前面介紹的默認(rèn)混淆規(guī)則,另外一個(gè)就是自定義混淆規(guī)則:

android {

    buildTypes {
        release {
            minifyEnabled true
            //proguard-android-optimize.txt:默認(rèn)混淆規(guī)則 proguard-rules.pro:自定義混淆規(guī)則
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

proguard-rules.pro 文件位于 app 目錄下,接下來我們就使用剛才學(xué)習(xí)的 Proguard 相關(guān)知識(shí)對(duì)混淆規(guī)則做修改吧

這里先列出我們要實(shí)現(xiàn)的目標(biāo):

1、對(duì) MyFragment 類進(jìn)行完全保留,不混淆其任何信息

2、對(duì) MainActivity 類進(jìn)行完全保留,不混淆其任何信息

3、對(duì) Utils 中的方法進(jìn)行保留,防止其被混淆或移除

4、對(duì) NativeUtils 中的非 Native 方法進(jìn)行保留,防止其被混淆或移除

5、對(duì)第三方庫進(jìn)行保留,防止其被混淆或移除

實(shí)現(xiàn)如下:

# 對(duì) MyFragment 類進(jìn)行完全保留,不混淆其任何信息
-keep class com.dream.androidreversedemo.MyFragment{
    *;
}

# 對(duì) MainActivity 類進(jìn)行完全保留,不混淆其任何信息
-keep class com.dream.androidreversedemo.MainActivity{
    *;
}

# 對(duì) Utils 中的方法進(jìn)行保留,防止其被混淆或移除
-keep class com.dream.androidreversedemo.Utils{
    *;
}

# 對(duì) NativeUtils 中的非 Native 方法進(jìn)行保留,防止其被移除
-keepclassmembers class com.dream.androidreversedemo.NativeUtils{
    public static void methodNotNative();
}

# 對(duì)第三方庫進(jìn)行保留,防止其被混淆或移除
-keep class com.dream.androidutils.*{
    *;
}

編寫好了自定義規(guī)則,現(xiàn)在我們重新打一個(gè)正式版的 APK 文件,然后在反編譯看效果:

可以看到我們自己編寫的類和引入的第三方庫中所有的的代碼都被保留了下來,不管是包名,類名都沒有被混淆

接著看一下具體的類:

MainActivity

Utils

NativeUtils

MyFragment

可以看到,上面的這些類基本上按照我們的要求保留了下來

ok,經(jīng)過上面的例子,相信大家已經(jīng)對(duì) Proguard 的用法有相當(dāng)不錯(cuò)的理解了,那么根據(jù)自己的業(yè)務(wù)需求去編寫混淆配置相信也不是什么難事了吧?

關(guān)于混淆 APK 代碼就講這么多,如果你還想了解更多關(guān)于 Proguard 的用法,可以參考這篇文章:juejin.cn/post/684490…

三、總結(jié)

本篇文章我們主要介紹了:

1、反編譯工具 jadx 的安裝與使用

jadx 相當(dāng)于是 apktool + dex2jar + jd-gui 的結(jié)合體,既能反編譯代碼也能反編譯資源,一定程度上提高了我們的開發(fā)效率

2、混淆 APK 代碼

1、準(zhǔn)備了一些類(自定義編寫的類,第三方庫的類)用于混淆后的效果驗(yàn)證

2、在 app -> build.gradle -> android 閉包 -> release 閉包將 minifyEnabled 設(shè)為 true 開啟代碼混淆

3、使用 AndroidStudio 導(dǎo)航欄上 Generate Signed Bundle or APK 的方式打 release 包

4、在 app 的 build.gradle 文件中配置簽名文件,方便后續(xù)使用 gradle 命令或 gradle 可視化界面打包

5、逐行介紹了默認(rèn)混淆規(guī)則文件 proguard-android-optimize.txt 中的配置

6、Proguard 疑難語法介紹

7、自定義混淆規(guī)則保留類(自定義編寫的類,第三方庫的類)不被混淆

好了,本篇文章到這里就結(jié)束了,希望能給你帶來幫助 ??

作者:sweetying
鏈接:https://juejin.cn/post/7168086915445424136
來源:稀土掘金

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容