Android开发之反编译、apk重新打包、apk混淆、apk对齐与不对齐、jar混淆、proguard

版权声明:转载请注明出处,谢谢配合 https://blog.csdn.net/qq_33750826/article/details/54092684

前言:学习必须脚踏实地,所以大家真心想学,我相信你花点时间在认真按照博客编写过程一步一步去阅读,你将会有不一般的收获,不建议大跳跃性的阅读。

1.反编译

详细说明地址

一、概括:

破解别人的app,拿到别人app的源代码。


二、应用场景:

自己做不出的功能,全网搜索都没结果的情况下,但是看到别人的app中有你需要的功能 ,就会去考虑反编译,但是一般是不会去做这样的事的,首先反编译也不是那么容易,其次可能有些人会想着去其中获利不道德。

所谓事物都有正反两面,既然你可以破解别人的app,当然别人也有办法对自己的app进行保护措施,所以就有了相应的混淆技术,android开发人员都应该会这反编译和混淆两个技能。

三、过程:

3.1、准备APP

因为反编译别人的东西有点不道德,所以我们自己手工去创建一个简单的app。
点击下载简单的app

app代码我就不贴了,对于我们来说实在太简单,简单的看下界面效果图:

这里写图片描述

可以看到,很简单,一个按钮点击弹出hello world。

3.2、解压APK,拿到classes.dex文件

3.2.1 怎么拿到classes.dex文件
首先解释一下我们上面下载文件FBYDemo.zip其实就是一个我们平常.apk文件,只是因为这里需要拿到.apk文件中的classes.dex,所以我简单的将.apk后缀改成了.zip,这样我们拿到.zip文件就可以直接解压然后拿到内部的classes.dex文件。

3.2.2 关于apk文件中的classes.dex文件
我的理解是java的可执行文件是.class文件,而android的dalvik虚拟机则是.dex可执行文件,我们可以简单理解为.class文件的进一步封装。

3.3、将classes.dex文件反编译成java源码形式的.jar文件
这一步我们需要用到两个工具下载dex2jar下载jd-gui

3.3.1 关于dex2jar、jd-gui工具

dex2jar : 将classes.dex文件反编译成java源码形式的.jar文件
jd-gui : 用于观看我们dex2jar反编译后的.jar文件的代码。

那好我们下载完这两个工具,解压,首先复制3.2中的classes.dex文件到解压后的dex2jar文件里:
这里写图片描述

然后打开cmd,定位到自己的dex2jar解压后的目录,输入如下命令:d2j-dex2jar.bat classes.dex
这里写图片描述

可以看到已经成功反编译出来一个.jar文件,接着使用解压后的jd-gui.exe工具打开这个生成的.jar文件,打开MainActivity:
这里写图片描述

到这里基本上能够模糊的看到MainActivity中的源代码了,由于默认会自动混淆布局,所以尽管我打包之前没有进行任何混淆处理还是看不到我们的所用的布局是哪个文件,但是在我们学习混淆过后就可以让更多代码模糊。

当然这里我们也可以更加清晰的知道classes.dex文件是.class文件的进一步封装,从我们上面就可以看到我们反编译后的classes.dex不存在任何不是.class的文件,那么我们的布局和我们资源都去哪里呢?细心的小伙伴就会发现我们最开始下载的.apk的压缩包中有布局和资源文件。

找到这两个文件,打开,但是发现都乱码了
这里写图片描述
这里写图片描述

3.4、反编译资源布局文件
不例外的这里也需要下载一个工具:下载apktool
3.4.1 关于apktool工具
这个工具用于最大幅度地还原APK文件中的9-patch图片、布局、字符串等等一系列的资源。

下载后解压,拿到3.1中下载的FBYDemo.zip将后缀名改成.apk复制到解压后的apktoo文件中 :
这里写图片描述

接着打开cmd,定位到自己的apktool解压目录,输入如下命令:apktool d FBYDemo.apk
这里写图片描述

等待一会成功后会自动生成一个FBYDemo的文件夹,打开文件又可以看到我们的布局文件,接着试着打开布局文件,发现代码清晰的展现在我们眼前:
这里写图片描述

到这里class文件和布局还有资源文件我们都已经反编译成功了,如果我们要使用别人的app的功能或者布局就得自己去复制粘贴了,但是我们能不能就直接随意改动app中的代码,然后成为自己的app呢,当然也是可以的。我们找到刚才生成的FBYDemo的文件夹,看到smali这样的一个文件夹,接着在进去就可以看到我们的class文件都成了.smail的文件,打开MainActivity.smail

关于smail文件

这里写图片描述

可以看到里面都是一些关于smail的语言,如果你懂的smail语法就可以很清晰的看懂这个文件,尽管我看不懂,但是我还是可以猜测出上图的hello world是我们的吐丝内容,所以我将改变hello world,然后重新打包试一下,看看内容是否可以改变。


四、重新打包

重新打包也很简单,打开cmd,定位到我们刚才的apktool文件夹,输入如下命令:apktool b FBYDemo -o NewFBYDemo.apk

FBYDemo是我们在反编译过程最后得到的那个文件夹,NewFBYDemo.apk表明我们重新打包之后的apk名称,可以任意取以.apk为后缀。

这里写图片描述

这样在apktool你将又会看到一个NewFBYDemo.apk的文件,如果你冲动的直接去安装它,发现是不能安装成功的,因为android是不会安装任何位签名的apk文件,不信可以试一试。

如何签名:
既然要签名我们就需要一个签名的文件,在eclipse一般是.keystore的文件,当然还可以自己随便定义后缀名,因为eclipse中没有明确的规定,而AS中是规定好了以.jks为后缀的签名文件。现在我就使用eclipse简单给大家示范一下怎么创建一个签名文件。

在演示如何创建签名文件之前,我先把apk的签名过程告诉大家,这样的话大家就可以更容易去了解签名文件创建的过程。

1.将jdk的bin目录配置到环境变量,这样就可以在哪里都可以调用目录下的命令:
2.将你的keystore和后来新生成NewFBYDemo.apk放到同一个目录下面
3.定位到NewFBYDemo.apk和keystore目录下,记住要把apk和keystore放在同一个目录下,输入如下命令:jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore 签名文件名 -storepass 签名密码 待签名的APK文件名 签名的别名

看到这里我们可以知道在创建签名文件的时候必须得记住创件签名文件时 的一些东西了,不然等会给apk签名很可能就是失败,无奈,那好,我们现在就去创建一个签名文件,总之我们在创建签名文件时要记住上面需要给apk签名的东西就好了。

eclipse如何创建签名文件:
1.在eclipse随便点击一个项目右键—->Android tools—->Export Signed Application Package,直到出现:
这里写图片描述

Next,Creat new keystore,点击Browse:
这里写图片描述
可以随意的在任意目录输入一个任意的文件,当然最好以.keystore为后缀,此时我们就必须记住这个文件的文件名,因为在签名apk的时候要用到,然后保存就好了,输入大于等于6位的密码,确认密码:
这里写图片描述

第一个就是签名的别名,第二三是别名的密码,我们需要记住它们。其他的随意填写。最后Next—->Finish。这样就成功的在刚才那个文件创建了一个keystore。

最后就可以正式给apk签名了,回顾apk签名过程,当然1,2步就不重复了,第三步定位到.apk和签名的目录下,输入命令:jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore fbydemo.keystore -storepass 111111 NewFBYDemo.apk wt

这里写图片描述

我们可以看到fbydemo.keystore就是我们创建的签名文件的名字,111111是我的签名密码,NewFBYDemo.apk是需要签名的apk,wt是我的签名别名,之后会要输入一个别名的密钥口令,就是我们在别名中的密码,在cmd中输入密码默认是看不到,你输了也不会有任何的显示,但是你确实输入了密码。最后enter就成功的给我们重新打包的apk签名了

最后我们安装apk试一试,使用adb 命令安装,如图:
这里写图片描述

打开app,发现成功将hello world给修改了,虽然图中乱码,因为我写的是中文,可能在格式转换的时候出现了问题,但是也是可以证明我们成功的将apk给反编译并签名打包成自己的app了。
这里写图片描述

到这里反编译和apk重新打包就结束了,下面我们将进入重点混淆


2、混淆

混淆详细概念

一、概括

将java代码打乱,以一些没有任何概念的东西代替我们的代码

二、应用场景

在android中混淆一般在两种情况下

1.发布app的时候,就是我们开发应用的时候
2.开发SDK的时候,有时候公司并不是只是开发应用,可能只是单单写一个封装好的SDK打包成jar给别人调用,然后又不想别人知道你SDK内部的具体实现,所以也需要使用混淆技术

两种混淆的区别:对于app我们的应用来说,你完全可以将所有的代码都给混淆掉,因为可能我们不想提供一点有用的信息给那些反编译的朋友,而对于SDK也就是我们封装的jar来说,因为我们的Jar包你最后无论如何至少得提供一个主类给别人调用,要不然的你的jar就没有任何意义,因为你没有对外暴露的方法,所以在混淆jar时我们不能将需要暴露给别人使用的类给混淆掉,不然的话别人是不知道怎么调用的,因为你已经混淆掉了。

三、过程

这里我们还通过是例子去讲解,毕竟例子能够更易懂。

很好的是无论是eclipse还是AS时创建每个项目都有声明是否混淆代码的配置文件,当然android SDK中还有一个默认的混淆配置文件。

3.1 app混淆过程:
首先我简单的说一下eclipse中的混淆,每次我们在eclipse中创建项目都会有以下内容,当然我下面是混淆之后的效果图,在eclipse中只要你将project.properties中我标记的那句话去掉注释如下图,并打包apk就会默认生成一个proguard文件夹如下图:
这里写图片描述

关于自动生成的proguard文件夹下面的文件:
这里写图片描述
此图摘自这位大神

在没混淆时,打开project.properties,可以看到图中指向的proguard.config使用#注释了的。

project.properties注释的大概意思:
这个文件是由Android工具自动生成的。不要修改这个文件,您的更改将被删除!这个文件必须选择版本控制系统。自定义属性用户通过ant构建系统编辑“ant.properties”,这个覆盖值适应你的项目结构脚本。
关键我们看到
让混淆器缩小和混淆代码,取消这个(可用属性:sdk.dir,user.home)

通过翻译这是我们可以知道

proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt

就是声明我们混淆文件的属性。我们可以使用skd目录下的,也可以使用用户自己定义的混淆文件。

我们可以看到它注释默认使用的是sdk目录下/tools/proguard/proguard-android.txt和我们项目目录下的proguard-project.txt,当然AS中我们默认的也是sdk目录下的proguard-android.txt文件,只是自定义混淆配置文件不同。

当然现在我们就去找一下这两个文件咯,首先我们看到sdk中的默认的混淆文件:proguard-androd.txt:

# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html

-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-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).
-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.

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

注释的我们就不去翻译了,直接去看看究竟sdk默认的混淆代码和规则是一个怎样的情况:


-dontusemixedcaseclassnames:不要使用大小写混合类名

-dontskipnonpubliclibraryclasses:不跳过非公共的library的类

-verbose:表示打印混淆的详细信息。 

-dontoptimize:不使用优化

-dontpreverify:不进行预校验

-keepattributes *Annotation*:保持注释内容

-keep public class com.google.vending.licensing.ILicensingService:保持这个类

-keep public class com.android.vending.licensing.ILicensingService:保持这个类

-keepclasseswithmembernames class * {
    native ;
}:保持native类成员和方法的名字

-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();
}:表示不混淆任何一个继承自View的类的setXXX和getXXX方法

-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}:表示不混淆任何一个activity 中参数是View的方法

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

-keepclassmembers class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator CREATOR;
}:表示不混淆Parcelable实现类中的CREATOR字段

-keepclassmembers class **.R$* {
    public static ;
}:表示不混淆R文件总的字段

-dontwarn android.support.**:表示不警告android.support包下任何内容

-keep class android.support.annotation.Keep:保持android.support.annotation.Keep类

-keep @android.support.annotation.Keep class * {*;}:表示保持@android.support.annotation.Keep修饰的任何东西

-keepclasseswithmembers class * {
    @android.support.annotation.Keep ;
}:表示保持@android.support.annotation.Keep修饰的任何方法

-keepclasseswithmembers class * {
    @android.support.annotation.Keep ;
}:表示保持@android.support.annotation.Keep修饰的任何字段

-keepclasseswithmembers class * {
    @android.support.annotation.Keep (...);
}:表示保持@android.support.annotation.Keep修饰的任何初始化的方法

这里写图片描述
这里写图片描述
两图摘自郭霖大神。


通过以上我们就可以知道:

1.android sdk给我们提供了一个默认的混淆文件proguard-android.txt

2.为了我们更好扩展也提供了我们自定义混淆配置的文件,在eclipse中是proguard-project.txt文件,而AS中自定义混淆配置文件在哪姓甚名谁,等下我们会通过一个例子去讲解

3.最后我们还知道一些基本的混淆代码规则。那么我们现在通过AS去演示一个混淆的app的例子。
———-

AS混淆APP例子:

经过以上步骤后,我们打开AS去创建一个Module,为了更直观的演示一个大致的app应用,我们将创建一个native类,一主Activity类,一个Fragment类,一个普通的类,使用第三方jar,这样就大致把一个项目的类都可以演示一遍,因为我们这里需要ndk和jni技术,否则项目不能打包和运行,如果不会ndk和jni的朋友可以去我的主页学习,然后如果朋友不想粘贴代码我也上传了csdn:AS混淆实例下载因为我们这里需要ndk和jni技术,否则项目不能打包和运行,如果不会ndk和jni的朋友可以去我的主页学习,然后如果朋友不想粘贴代码我也上传了csdn:
AS混淆实例下载,这样的话你就可以直接跳过代码阶段了

代码:
普通类:

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);
    }


}
native类:
public class NativeUtils {
    static{
        System.loadLibrary("demo");
    }

    public static native String methodNative();

    public static void methodNotNative() {
        String logMessage = "this is not native method";
        logMessage = logMessage.toLowerCase();
        System.out.println(logMessage);
    }
}
Fragment类:
public class MyFragment extends android.support.v4.app.Fragment {
    private String toastTip = "toast in MyFragment";

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

    public void methodWithGlobalVariable() {
        Toast.makeText(getActivity(), toastTip, Toast.LENGTH_SHORT).show();
    }

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

}
主Activity:
public class MainActivity extends  AppCompatActivity{

    private String toastTip = "toast in MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getSupportFragmentManager().beginTransaction().add(R.id.fragment, new MyFragment()).commit();

    }

    public void btn_ndk(View view){
        methodWithGlobalVariable();
        methodWithLocalVariable();
        Utils utils = new Utils();
        utils.methodNormal();
        NativeUtils.methodNative();
        NativeUtils.methodNotNative();
        Connector.getDatabase();
    }


    public void methodWithGlobalVariable() {
        Toast.makeText(MainActivity.this, toastTip, Toast.LENGTH_SHORT).show();
    }

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

MainActivity主要做一个串联的作用,将Fragment类,普通的类,native的类,第三方jar都给应用进来了,
jar包下载,
特别注意并且我们只使用普通类中的一个方法,待会注意混淆打包后的文件普通类中的未使用与使用方法的混淆区别。
与eclipse一样AS同样首先需要声明是否需要混淆文件:
这里写图片描述
可以看到我在我们的AS中划分了三块区域

1.红色区域,build.gradle一般就是用于AS配置一些与项目相关的信息,需要的那些jar啊,jni啥的

2.蓝色区域,打开我们AS中随便一个build.gradle,就可以看到蓝色区域的配置,作用是声明是否使用混淆文件,默认minifyEnabled false,我们只要简单的把它改为true表示使用混淆配置文件,打包之后就可以看到混淆后的代码,接下来这行代码就是配置混淆文件,我们可以看到AS和Eclipse一样支持默认的SDK中的proguard-android.txt和自带的自定义混淆配置文件的proguard-rules.pro。

3.灰色区域:上面eclipse中有一个自动生成的proguard-project.txt是用于我们自定义混淆的配置,当然AS也有一个,也是自动生成的proguard-rules.pro文件用于我们自定义配置混淆文件。

将minifyEnabled改为true之后,接下来我们做的就是将我们的实例项目打包成apk和并对它试着进行反编译看看能不能得到清晰的源码呢。
在AS中和Eclipse同样首先得生成一个签名文件,然后在使用该签名文件给apk打包签名:
这里写图片描述
这里写图片描述
选择签名的项目,Next
这里写图片描述
可以看到和Eclipse中几乎是一样的。最后Next—>Finish。
那么最终的apk在哪里呢?Eclipse可是会让我们指定一个保存的路径和填写apk的名字的,让我们看到Project目录下的
这里写图片描述

AS就是这样的强大啊,我们首先看到apk的名字:confusiondemo-release-unaligned.apk,首先
confusondemo表示我们的module名字,release则表示此apk是发布版的apk,unaligned表示我们的apk是没有对齐的,而在eclipse中我们的apk也都是未对齐的,但是Eclipse却不会提醒我们这个问题,那么在我们重新打包apk时也最好去对齐操作我们的apk,因为Eclipse没有提醒我,所以我就忘记了,所以AS牛逼,但是我们还是得结合Eclipse去和AS对比,才能更加懂得其中的原理。


unaligned and aligned

那么既然AS中有这个问题,那么我们就去看看unaligned和aligned有什么不同,很显然的是中国的网站很少会有这种问题并且答案也不怎么详细,所以一般我会选择stackoverflow去查找,很好的是stackoverflow回答确实不错,那我们就引用一下外国朋友的回答,看到官网文档:
这里写图片描述
翻译

一旦你用你的私钥签署了APK文件,文件上运行zipalign。这个工具确保所有未压缩的数据从一个特定的字节对齐,相对于文件的开始。确保在4字节边界对齐,当apk安装在一个设备时提供了一个性能优化。当对齐时,Android系统是能够阅读文件mmap(),即使他们包含二进制数据对齐的限制,而不是复制所有数据从包中。好处是减少数量的内存消耗的运行应用程序中。

根据解释,我们可以清楚的知道给apk签名对齐的好处,可以减少内存,既然可以减少内存,作为一个adnroid程序员,在可以节省的地方我们必须去执行,那么我们AS又为什么不自动帮我们对齐一个APK呢,这样我们就可以更加简便了,为什么要生成一个中间的unaligned的.apk文件呢?当然这也是有原因的,我们去查找官方文档。

官网文档:
这里写图片描述
翻译

zipalign必须在用你的私钥给你的.apk文件签名之后执行,如果你在签名之前执行,签名过程将取消对齐。

根据解释,我们就可以发现原因了,因为我们必须在签名之后进行对齐,所以AS只能在我们签名后给我们一个unaligned的.apk文件,因此我们要aligned一个apk需要手动去操作。

说了这么多次ipalign工具,那它到底是什么有什么呢?来到官网文档:
这里写图片描述
翻译

zipalign是存档对齐工具,提供了重要的优化Android应用程序(. apk)文件。….好处是减少数量的RAM运行应用程序时使用。

根据解释,很显然,我们要对齐一个apk是要使用zipalign工具的。

在给apk对齐之前我们再看看两张图片:
这里写图片描述

这里写图片描述
第一张图片是一个标准的概括性的apk打包过程,可以看到是需要我们使用zipalign进行对齐操作apk的。
第二张图片是一个标准的详细的apk打包,可以看到是需要我们使用zipalign进行对齐操作apk的。

那好以上说了那么多,相必我们现在要做的就是给我们混淆后的apk签名
首先我们要找到zipalign工具,SDK目录下的—>build-tools—->version:
这里写图片描述
然后将此目录配置到环境变量,方便我们在任何地方使用它。

我们复制AS中刚才混淆后的.apk文件到任意目录,打开cdm,定位到你复制后的.apk目录,输入如下命令:zipalign 4 confusiondemo-release-unaligned.apk confusiondemo-release-aligned.apk
这里写图片描述

分析一下这个命令:

zipalign :zipalign 工具
4 :固定不变的,在我们讲解官方文档对以时就说了以4个字节为边界对齐,不记得的话可以再去看看。
confusiondemo-release-unaligned.apk:AS中我们生成的.apk的包名
confusiondemo-release-aligned.apk:对齐后的.apk的名字。

通过以上学习我们就明白了apk对齐与不对齐,并且现在我们在未对齐的apk目录下已经成功得到了一个对齐后的经过混淆后的apk文件,那么我们接下就试着对它用我们最开始学的进行反编译,最后在jg-gui中看到反编译后的.jar包:
这里写图片描述
通过使用SDK默认的混淆文件,我们可以看到第三方jar内部都以abacd这样的字母给替代了,这样我们就不能确定哪个类是哪个类,我们再去看到我们的项目代码类,可以看到MainActivity和Native类名没有被混淆,但是我们的普通类和Fragment却被混淆了,我们打开a:
这里写图片描述
虽然所有的方法名,变量名,都被字母代替了,但是我们还是可以大致猜出它是我们的Fragment类,那我们就可以知道b使我们的普通类的,再打开普通类:
这里写图片描述
之前我们编写代码时知道我们的普通类明明有两个啊 ,就算这里混淆的话,也应该有两个方法存在吧,我在之前说过要我们注意我们的MainActivity只用了我们的普通类b中的一个方法,而未使用的那个方法系统认为是无用的,就直接给你pass掉了,现在我们在去看看native类:
这里写图片描述
可以看到native完好无损,因为native都是和底层打交道的,所以我们系统默认给我们全部保存了。那好最后看到MainActivity:
这里写图片描述
其他的类名啊,方法名啊,变量名啊都被混淆成字母了,只有我们的native类完好无损。

那这时候有朋友就会问了,AS创建项目会给我们一个自定义配置混淆文件吗?该怎么去使用呢?如果我们要自己自定义混淆配置文件,就得自己写proguard语法,想要全面学习proguard语法的可以去proguarde官网学习或者百度一下,但是之前我也有讲解SDK中那个默认的混淆配置文件额语法,那么我们就用所学的部分的proguard语法去进一步自定义我们的混淆配置文件混淆apk。好我们找到项目下的proguard-rules.pro文件,看到它里面的都是处于注释状态的。
我们首先去定义几个混淆需求:
这里写图片描述
此图摘自郭霖大神
那好,现在我们根据需求去编写配置文件,在proguard-rules.pro文件添加如下内容:
1.保留Fragment类:

-keep class com.example.jhl.confusiondemo.MyFragment{
 *;
}

2.保留普通类中未使用的方法:

-keepclassmembers class com.example.jhl.confusiondemo.Utils{
public void methodUnused();
}

3.保留第三方jar:

-keep class org.litepal.** {
    *;
}

-keep class android.support.** {
    *;
}

编写好自定义配置文件之后我们又得重新进行打包,和反编译,再次看看我们定义的混淆规则是否生效了,相信认真学习了上面的朋友都不是难事了,我就直接来到jd-gui观察我们自定义混淆文件后的反编译的.jar文件:
这里写图片描述
我们可以发现此时我们的第三方jar已经完全展示在我们眼前了,然后再去看看Fragment类是否完全保留呢:
这里写图片描述
可以清楚的看到Fragment类完全保留,再去看看普通类中未使用的方法是否保留:
这里写图片描述
可以看到已经成功保留了。

将到这里我们的混淆,apk对齐与不对齐就结束了,接下来我们在最后看看jar的混淆和proguarde工具的使用。


Jar混淆和proguarde使用

proguarde官网
很好的是SDK也会自带一个proguarde,在我们的SDK目录下/tools/proguard/bin/proguardgui.bat,如图:
这里写图片描述
但是不幸的是它不支持jdk8因为它自带的不是最新版,所以我们可以去官网下载最新版,但是官网下载实属太慢,我这里提供一个支持jdk8的版本:proguard下载

下载后找到bin目录下的proguardgui.bat打开:
这里写图片描述

讲到这里我们就可以使用一个jar用Proguard工具去混淆,当然我们也可以不使用它,根据我们之前的直接编写配置混淆文件就好了,但是为了更好的学习我们还是学习一下,现在我们使用Proguard去混淆我们的一个jar,我们之前讲过jar和app的混淆区别在于jar有些方法是需要对外暴露的,那些对外暴露的方法类不能被混淆。那好我们 编写一个jar,在AS中的例子
这里我就不说明AS怎么将项目打jar包了,当然如果你不会的话,可以评论。我 乐意教你。当然为了方便我还是提供下载地址: [jar下载](http://download.csdn.net/detail/qq_33750826/9730689) 我们的jar为了方便有只两个类: 一个内部实现的类Add,一个对外暴露的类AddUtil:
public class Add {
    public int add(int a,int b){
        return a+b;
    }
}
public class AddUtil {

    Add add;
    public AddUtil(){
        add=new Add();
    }

    public int add(int a,int b){
        return add.add(a,b);
    }
}

到这里我们jar就准备好了,现在我们要做的就是使用proguarde将对外暴露的类也就是我们这里的AddUtil保留,而内部的Add类混淆掉。那么我们现在回到proguard工具
这里写图片描述

点击Load configuration表示加载我们的配置文件,无论Eclipse和AS中混淆处理都会默认加载SDK中的proguard-android.txt文件,所以我们也使用该文件作为我们的默认配置文件,但是我们不能直接将目录定位到SDK的proguard-android.txt文件,因为这样将改变proguard-android.txt中的内容,那么我们以后每次去混淆其他的app或者Jar时就会使用改变后的proguard-android.txt,而不是我们SDK默认的proguard-android.txt文件。所以我们将SDK目录下的proguard-android.txt复制到任意你知道的目录,然后使用定位它的位置,
Load configuartion:

这里写图片描述

这里写图片描述
rt.jar在自己的jdk目录下,android.jar在SDK目录下,v7和v4.jar在我们的项目目录下,随便使用一个项目目录下的即可。
Next

这里写图片描述

Next

这里写图片描述

这里添加正式的混淆逻辑,如果你想在ReTrace中撤回混淆,就必须勾选Print mapping并指定一个文件。

这里写图片描述

对我们外部暴露的类AddUtil类名和方法,还有字段不进行混淆
Next
来到优化的阶段,默认我们不优化,所以继续Next

这里写图片描述

来到预防措施的阶段,直接Next,但是如果你和我一样勾选了Target 1.8请把它去掉,因为Android目前不支持jdk8。千万要去掉,嗯。
Next

这里写图片描述

你想保存配置混淆件就保存,不想保存就直接点击process

这里写图片描述

看到这样就表示混淆成功了。
最后我们通过jd-gui去看看我们混淆后的jar文件:

这里写图片描述

可以看到AddUtil类没有被混淆,其他内部实现都给混淆了,这样对我们的Jar就形成了很好的保护,外人也就无法观察到我们jar包的具体实现了,当然我们可以直接编写一个配置文件去混淆我们的jar,和我们混淆app的方式是一样的,这里就不再重复了。

到这里我们这一篇博客的所有知识点总算是讲解完毕,这篇博客写的挺辛苦的,因为实在太长了,不过收获还是挺多的,希望大家学习之后也能帮到大家,大家最好都自己弄一遍,才能更清晰的反应自己的不足。

猜你喜欢

转载自blog.csdn.net/qq_33750826/article/details/54092684
今日推荐