本文正在参与「金石方案 . 瓜分6万现金大奖」

前言

很快乐遇见你~

在本系列的上一篇文章中,咱们讲了:

1、运用 apktool 反编译 app 中的资源

2、运用 dex2jar + jd-gui 反编译 app 中的代码

3、根据一个 app 打造一个新的 app,主要通过 apktool 反编译后,修正资源,然后进行从头打包,签名,对齐,最终装置展现了预期的作用。当然这儿你有才能看懂 .smali格式的代码,那么你能够做的事情就更加多

还没有看过上一篇的朋友,建议先去阅览Android 逆向系列(一):反编译 APK 技能彻底解析

你能攻击我的 app,那我肯定有防卫的策略,接下来咱们介绍一下 Android 中的混杂技能

留意:下面演示均是在 mac 下进行

Github Demo 地址:github.com/sweetying52…

一、jadx 介绍

在此之前,我想介绍其他一款反编译东西:jadx,它适当所以 apktool + dex2jar + jd-gui 的结合体,既能反编译代码也能反编译资源,要害运用起来还特别简略,你只需求将文件拖进来即可,必定程度上提高了咱们的开发功率

Github 地址:github.com/skylot/jadx

1.1、jadx 特点

1、能将 APK,AAR,JAR,DEX,AAB,ZIP 等文件中的代码反编译为 Java 类

2、能反编译 APK,AAR,AAB,ZIP 中的资源

1.2、jadx 装置

1、装置 jadx,引荐运用 brew 去装置,执行如下指令:

brew install jadx

运用 brew 装置的优点便是 mac 会给你自动装备好环境变量,你只需求专注软件的运用即可,等候装置完成在验证一下

2、jadx 验证

在 Terminal 输入如下指令:

jadx --version

假如打印出了版别号就证明装置成功了:

Android 逆向系列(二):Android APK 代码混淆

1.3、jadx 运用

这儿咱们直接运用 jadx 提供的可视化界面进行操作

1、在 Terminal 输入如下指令:

jadx-gui

此时就会翻开 jadx 的可视化界面了:

Android 逆向系列(二):Android APK 代码混淆

2、将你需求反编译的文件拖入即可查看反编译的代码和资源了,如下图:

Android 逆向系列(二):Android APK 代码混淆

二、混杂 APK 代码

2.1、预备作业

首先咱们先做一些预备作业

1、增加一些类:

//1、新建 Utils.java 文件,创立 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 文件,创立 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 文件,创立 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 中进行引证

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 下的办法调用
        Utils utils = new Utils();
        utils.methodNormal();
        //2、NativeUtils 下的办法调用
        try {
            NativeUtils.methodNative();
            NativeUtils.methodNotNative();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        //3、第三方库下东西类的办法调用
        int result = StringUtils.getLength("erdai666");
        System.out.println(result);
        //4、MainActivity 下的 methodWithGlobalVariable 办法调用
        methodWithGlobalVariable();
        //5、MainActivity 下的 methodWithLocalVariable 办法调用
        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);
    }
}

好的,到这儿预备作业现已根本完成,接下来咱们对 APK 中的代码进行混杂

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 即敞开了代码混杂,十分的简略

其他需求留意: 这儿是在 release 闭包内进行装备的,因而只要打出正式版的 APK 才会进行混杂,debug 版的 APK 是不会混杂的。当然这也是十分合理的,由于 debug 版的 APK 文件咱们只会用来内部测试,不必担心被人破解。

2、接下来打一个正式的 APK 包

1、在 Android Studio 导航栏中点击 Build -> Generate Signed Bundle or APK,挑选 APK

2、然后挑选签名文件并输入密码,假如没有签名文件就创立一个

3、点击 next 挑选打 release 包,终究点击 Finish 完成打包

4、生成的 APK 会自动存放在 app/release/ 目录下

Tips: 咱们能够在 app 的 build.gradle 文件中增加签名文件装备,后续就能够直接通过 ./gradlew assembleRelease 指令或许 AndroidStudio 右侧的 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 导航栏 Generate Signed Bundle or APK 办法生成的 APK 在:app/release/ 目录下

2、运用 ./gradlew assembleRelease 指令或许 AndroidStudio 右侧的 Gradle 可视化界生成的 APK 在:app/build/outputs/apk/ 目录下

3、接着运用 jadx 翻开当时 APK,如下图所示:

Android 逆向系列(二):Android APK 代码混淆

很明显咱们代码混杂的功能现已收效了。

2.3、混杂文件介绍

下面咱们测验来阅览一下混杂后之前预备的那些类:

MainActivity

Android 逆向系列(二):Android APK 代码混淆

能够看到:

1、MyFragment 被混杂了

2、Utils 下的办法调用:直接是把办法里面的内容复制到了办法的调用途

3、NativeUtils 下的办法调用:1、Native 办法仍是正常的调用 2、非 Native 办法则是把办法里面的内容复制到了办法的调用途

4、第三方库下东西类的办法调用:直接是将办法的成果填充到了调用途

5、MainActivity 中的成员办法:直接是把成员办法里面的内容复制到了办法的调用途

6、MainActivity 类名是没有混杂的,onCreate 办法也没有被混杂,但界说的成员变量,局部变量被混杂了

Utils

Utils 类直接没有了

NativeUtils

Android 逆向系列(二):Android APK 代码混淆

能够看到:

1、NativeUtils 类名没有被混杂,其间声明成 native 的办法也没有被混杂

2、非 Native 办法直接没有了,办法的内容复制到了办法的调用途

MyFragment

Android 逆向系列(二):Android APK 代码混淆

能够看到:

1、一切的办法名,成员变量,局部变量都被混杂了

2、MyFragment 中的成员办法:直接是把成员办法里面的内容复制到了办法的调用途

接下来在剖析一下上面的混杂成果

1、Utils 直接没有了,由于它被调用的办法内容直接复制到了办法的调用途。其他一个办法没有被调用,会被认为是多余的代码,在打包的时分就给移除掉了,不仅仅是办法,没有调用的资源相同会被移除,这样的优点是能够减少 APK 的体积

2、NativeUtils 类名没有被混杂,这是由于它有一个声明成 native 的办法。只要一个类中有存在 native 办法,它的类名就不会被混杂,native 办法的办法名也不会被混杂,由于 C 或 C++ 代码要通过包名+类名+办法名来进行交互。 可是类中其他代码仍是会被混杂,它的非 Native 办法直接没有了,由于办法里面的内容复制到了办法的调用途

3、MyFragment 是混杂的比较彻底的,根本没有任何保存,连生命周期办法也被混杂了,Fragment 怎么说也算是一个系统组件吧,搞的一点体面都没有

4、MainActivity 的保存程度就比 MyFragment 好多了,至少像类名,生命周期办法都没有被混杂,这是由于:凡是需求在 AndroidManifest.xml 中注册的一切类的类名以及从父类重写的办法名都不会被混杂。 因而,除了 Activity 之外,这份规矩相同适用于:Service,BroadcastReceiver 和 ContentProvider

5、引入的第三方库也被混杂了,上述能够看到直接是把办法调用的成果给填充了进来

2.4、默许混杂规矩介绍

那么这些混杂规矩是在哪里界说的呢?其实便是方才在 build.gradle 的 release 闭包下装备的 proguard-android-optimize.txt 文件,这个文件存放于Android SDK/tools/proguard/目录下:

Android 逆向系列(二):Android APK 代码混淆

看一眼它的详细内容:

# 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>(...);
}

这个便是默许的混杂装备文件了,咱们来逐行解释一下:

# 发动优化相关的一些装备
# 指定更精细级其他优化
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
# 表明对代码优化的次数,一般为 5
-optimizationpasses 5
# 允许改动作用域
-allowaccessmodification
# 封闭预验证
-dontpreverify
# 表明混杂时不运用大小写混合类名
-dontusemixedcaseclassnames
# 表明不跳过 library 中的非 public 类
-dontskipnonpubliclibraryclasses
# 表明打印混杂的详细信息
-verbose
#表明对注解中的参数进行保存
-keepattributes *Annotation*
# 表明不混杂如下声明的两个类,这两个类根本上也用不上,是接入 Google 原生的一些服务时运用的
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
# 表明不混杂任何包括 native 办法的类名以及 native 办法名,这个和方才验证的成果是一致的
-keepclasseswithmembernames class * {
    native <methods>;
}
# 表明不混杂 View 中的 setXXX() 和 getXXX() 办法,由于属性动画需求有相应的 setter 和 getter 办法完成
-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();
}
# 表明不混杂 Activity 中参数是 View 的办法,由于有这么一种用法,在 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 完成类中的 CREATOR 字段,毫无疑问,CREATOR 字段是肯定不能改动的,包括大小写都不能变,不然整个
# Parcelable 作业机制都会失效
-keepclassmembers class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator CREATOR;
}
# 表明不混杂 R 文件中的一切静态字段,咱们都知道 R 文件是通过字段来记载每个资源 id ,字段名假如被混杂,id 就找不到了
-keepclassmembers class **.R$* {
    public static <fields>;
}
# 表明对 android.support 包下的代码不正告,由于 support 包中的一切代码都在兼容性上做了足够的判断,因而不必担心代码会出问题
# 所以直接忽略正告就能够了
-dontwarn android.support.**
# 表明不混杂 android.support.annotation.Keep 这个注解类的一切东西
-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 注解的结构办法
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}

2.4.1、proguard-android-optimize.txt 和 proguard-android.txt 差异

之前一些 AGP 老版别,咱们新建工程默许运用的是:proguard-android.txt,那么它和 proguard-android-optimize.txt 有啥差异呢?

从字面的维度看,就多了一个 optimize(优化)这个单词,实践便是多了优化这一部分,proguard-android-optimize.txt 相对于 proguard-android.txt 敞开了优化相关的装备:

# proguard-android-optimize.txt 新增了以下优化规矩
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5
-allowaccessmodification
-dontpreverify
# proguard-android-optimize.txt 删除了封闭优化指令的装备
# -dontoptimize

好了,上述便是 proguard-android-optimize.txt 文件中一切的默许装备,而咱们混杂代码也是依照这些装备的规矩来进行混杂的。通过上面的解说,信任咱们对这些装备的内容根本都能了解了。不过 Proguard 语法中还真有几处十分难了解的地方,下面和咱们分享一下这些难懂的语法部分

2.5、Proguard 疑难语法介绍

Proguard 中一共有三组六个 keep 要害字,很多人搞不清楚他们的差异,咱们通过一个表格直观的来看一下:

要害字 描绘
keep 保存类和类中的成员不被混杂或移除
keepnames 在 keep 的基础上,假如成员没有被引证,则会被移除
keepclassmembers 保存类成员不被混杂或移除
keepclassmembernames 在 keepclassmembers 基础上,假如成员没有被引证,则会被移除
keepclasseswithmembers 保存类和类中的成员不被混杂或移除,条件是类中的成员必须存在,不然仍是会被混杂
keepclasseswithmembernames 在 keepclasseswithmembers 基础上,假如成员没有被引证,则会被移除

除此之外,Proguard 的通配符也比较让人难懂,proguard-android-optimize.txt 中就运用到了很多通配符,咱们来看一下它们之间的差异:

通配符 描绘
<field> 匹配类中一切的字段
<method> 匹配类中一切的办法
<init> 匹配类中一切的结构办法
* 匹配恣意长度字符,但不包括分隔符.,例如咱们完成类名是:com.dream.androidreversedemo.MainActivity,运用 com.* 或许 com.dream.* 是无法匹配的,由于 * 无法匹配报名中的分隔符,正确的匹配办法是com.dream.*.*或许com.dream.androidreversedemo.*
** 匹配恣意长度字符,包括分隔符.,上面匹配规矩咱们能够运用com.**或许com.dream.**来进行匹配
*** 匹配恣意参数类型。例如void set*(***)就能匹配传入恣意的参数类型,***get(*)就能匹配恣意返回值的类型
... 匹配恣意长度的恣意类型参数,例如void test(...)就能匹配void test(String str)或许void test(int a,double b)这些办法

ok,学习了疑难语法,下面咱们来一道练习题:保存完成了 com.dream.test.BaseJsonData 接口的类的一切信息不被混杂?

一个清晰的思路很重要,仔细剖析一下:

1、首先咱们要确保 com.dream.test.BaseJsonData 接口不被混杂

2、然后确保完成 com.dream.test.BaseJsonData 接口的类不被混杂

3、最终便是匹配类中一切的成员不被混杂,能够运用通配符 *

咱们能够这么写:

# 确保  com.dream.test.BaseJsonData 接口不被混杂
-keep class com.dream.test.BaseJsonData
# 确保完成 com.dream.test.BaseJsonData 接口的类不被混杂
# 匹配类中一切的成员不被混杂,能够运用通配符 *
-keep class * implements com.dream.test.BaseJsonData{
    *;
}

2.6、自界说混杂规矩

回到项目中,方才打出的 APK 虽然现已成功混杂了,可是混杂的规矩是依照 proguard-android-optimize.txt 中默许的规矩来的,当然咱们能够修正 proguard-android-optimize.txt 中的规矩,可是这样做会对本机上一切项目的混杂规矩都收效,那么有没有什么好的办法只针对当时项目做混杂规矩修正呢?

答:对 proguard-rules.pro 文件进行自界说混杂规矩编写

能够看到 android 闭包下 release 闭包的装备,实践上装备了两个混杂文件,一个便是咱们前面介绍的默许混杂规矩,其他一个便是自界说混杂规矩:

android {
    buildTypes {
        release {
            minifyEnabled true
            //proguard-android-optimize.txt:默许混杂规矩 proguard-rules.pro:自界说混杂规矩
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

proguard-rules.pro 文件坐落 app 目录下,接下来咱们就运用方才学习的 Proguard 相关常识对混杂规矩做修正吧

这儿先列出咱们要完成的方针:

1、对 MyFragment 类进行彻底保存,不混杂其任何信息

2、对 MainActivity 类进行彻底保存,不混杂其任何信息

3、对 Utils 中的办法进行保存,避免其被混杂或移除

4、对 NativeUtils 中的非 Native 办法进行保存,避免其被混杂或移除

5、对第三方库进行保存,避免其被混杂或移除

完成如下:

# 对 MyFragment 类进行彻底保存,不混杂其任何信息
-keep class com.dream.androidreversedemo.MyFragment{
    *;
}
# 对 MainActivity 类进行彻底保存,不混杂其任何信息
-keep class com.dream.androidreversedemo.MainActivity{
    *;
}
# 对 Utils 中的办法进行保存,避免其被混杂或移除
-keep class com.dream.androidreversedemo.Utils{
    *;
}
# 对 NativeUtils 中的非 Native 办法进行保存,避免其被移除
-keepclassmembers class com.dream.androidreversedemo.NativeUtils{
    public static void methodNotNative();
}
# 对第三方库进行保存,避免其被混杂或移除
-keep class com.dream.androidutils.*{
    *;
}

编写好了自界说规矩,现在咱们从头打一个正式版的 APK 文件,然后在反编译看作用:

Android 逆向系列(二):Android APK 代码混淆

能够看到咱们自己编写的类和引入的第三方库中一切的的代码都被保存了下来,不管是包名,类名都没有被混杂

接着看一下详细的类:

MainActivity

Android 逆向系列(二):Android APK 代码混淆

Utils

Android 逆向系列(二):Android APK 代码混淆

NativeUtils

Android 逆向系列(二):Android APK 代码混淆

MyFragment

Android 逆向系列(二):Android APK 代码混淆

能够看到,上面的这些类根本上依照咱们的要求保存了下来

ok,通过上面的比如,信任咱们现已对 Proguard 的用法有适当不错的了解了,那么根据自己的事务需求去编写混杂装备信任也不是什么难事了吧?

关于混杂 APK 代码就讲这么多,假如你还想了解更多关于 Proguard 的用法,能够参考这篇文章:/post/684490…

三、总结

本篇文章咱们主要介绍了:

1、反编译东西 jadx 的装置与运用

jadx 适当所以 apktool + dex2jar + jd-gui 的结合体,既能反编译代码也能反编译资源,必定程度上提高了咱们的开发功率

2、混杂 APK 代码

1、预备了一些类(自界说编写的类,第三方库的类)用于混杂后的作用验证

2、在 app -> build.gradle -> android 闭包 -> release 闭包将 minifyEnabled 设为 true 敞开代码混杂

3、运用 AndroidStudio 导航栏上 Generate Signed Bundle or APK 的办法打 release 包

4、在 app 的 build.gradle 文件中装备签名文件,便利后续运用 gradle 指令或 gradle 可视化界面打包

5、逐行介绍了默许混杂规矩文件 proguard-android-optimize.txt 中的装备

6、Proguard 疑难语法介绍

7、自界说混杂规矩保存类(自界说编写的类,第三方库的类)不被混杂

好了,本篇文章到这儿就结束了,希望能给你带来帮助

感谢你阅览这篇文章

参考和引荐

Android安全攻防战,反编译与混杂技能彻底解析(下)

深化学习ProGuard之:ProGuard简介与android的使用

jadx github

你的点赞,谈论,是对我巨大的鼓励!

欢迎重视我的大众号: sweetying ,文章更新可第一时间收到

假如有问题,大众号内有加我微信的入口,在技能学习、个人成长的道路上,咱们一同行进!