Android开发学习之路-脱壳反编译

前言

  • 想研究下某app里面的实现技术,使用常规的反编译发现竟然是加固的,所谓Apk加固,就是对dex文件进行加密,防止App被反编译,保证apk的安全。市面上有很多的加固平台,有360加固,腾讯乐固,爱加密等等。
  • 上一篇文章我们了解了加固,也有了加固后的apk,这里我们利用工具脱壳查看源码。

1. 脱壳工具FDex2

道高一尺魔高一丈嘛,有加固平台,也有脱壳工具,常见的脱壳工具有FDex2,dumpdex,drizzleDumper等。这里我们使用FDex2来实现脱壳。

FDex2通过Hook ClassLoader的loadClass方法,反射调用getDex方法取得Dex(com.android.dex.Dex类对象),在将里面的dex写出。

下载地址:

链接:https://pan.baidu.com/s/1smxtinr 密码:dk4v

我们看下主要的源码:

public class MainHook implements IXposedHookLoadPackage {
  Class Dex;
  
  Method Dex_getBytes;
  
  Method getDex = (Method)null;
  
  XSharedPreferences shared;
  
  @Override
  public void handleLoadPackage(XC_LoadPackage.LoadPackageParam paramLoadPackageParam) {
    this.shared = new XSharedPreferences("formatfa.xposed.Fdex2", "package");
    this.shared.reload();
    initRefect();
    String str = this.shared.getString("packagename", (String)null);
    if (str == null) {
      return;
    } 
    if (!paramLoadPackageParam.packageName.equals(str))
      return; 
    XposedBridge.log(str + " has hook");
    ClassLoader classLoader = paramLoadPackageParam.classLoader;
    try {
      Class clazz = Class.forName("java.lang.String");
      XposedHelpers.findAndHookMethod("java.lang.ClassLoader", classLoader, "loadClass", new Object[] { clazz, boolean.class, new XC_MethodHook(this, str, paramLoadPackageParam) {
              private final MainHook this$0;
              
              private final String val$aim;
              
              private final XC_LoadPackage.LoadPackageParam val$p1;
              
              @Override
              protected void afterHookedMethod(MethodHookParam param1MethodHookParam) {
                XposedBridge.log(" after hook : ");
                boolean bool = true;
                clazz = (Class)param1MethodHookParam.getResult();
                if (clazz == null)
                  return; 
                try {
                  str = clazz.getName();
                  try {
                    Class clazz1;
                    (clazz1 = Class.forName("formatfa.xposed.Fdex2.MainHook")).forName(str, false, clazz1.getClassLoader().getSystemClassLoader());
                  } catch (ClassNotFoundException str) {
                    throw new NoClassDefFoundError(str.getMessage());
                  } 
                } catch (ClassNotFoundException classNotFoundException) {
                  bool = false;
                } 
                if (bool)
                  return; 
                try {
                  Object object = this.this$0.getDex.invoke(clazz, new Object[0]);
                  byte[] arrayOfByte = (byte[])this.this$0.Dex_getBytes.invoke(object, new Object[0]);
                  if (arrayOfByte != null) {
                    "/data/data/" + this.val$aim + "/" + this.val$aim + arrayOfByte.length + ".dex";
                    File file = new File(this.this$0.shared.getString("dir", "/sdcard"), this.val$p1.packageName + arrayOfByte.length + ".dex");
                    if (!file.exists())
                      FIO.writeByte(arrayOfByte, file.getAbsolutePath()); 
                  } 
                  return;
                } catch (Exception clazz) {
                  XposedBridge.log(clazz.toString());
                  return;
                } 
              }
              
              @Override
              protected void beforeHookedMethod(MethodHookParam param1MethodHookParam) {}
            } });
      return;
    } catch (ClassNotFoundException paramLoadPackageParam) {
      throw new NoClassDefFoundError(paramLoadPackageParam.getMessage());
    } 
  }
  
  public void initRefect() {
    try {
      this.Dex = Class.forName("com.android.dex.Dex");
      this.Dex_getBytes = this.Dex.getDeclaredMethod("getBytes", new Class[0]);
      if (!MainActivity.h.endsWith("0"))
        return; 
      int i = MainActivity.s.length();
      if (i != 91)
        return; 
      try {
        Class clazz = Class.forName("java.lang.Class");
        this.getDex = clazz.getDeclaredMethod("getDex", new Class[0]);
      } catch (ClassNotFoundException classNotFoundException) {
        throw new NoClassDefFoundError(classNotFoundException.getMessage());
      } 
    } catch (Exception exception) {
      XposedBridge.log(exception.toString());
    } 
  }
  
  void writeDex(String paramString, Object paramObject) {
    try {
      paramObject = this.getDex.invoke(paramObject.getClass(), new Object[0]);
      byte[] arrayOfByte = (byte[])this.Dex_getBytes.invoke(paramObject, new Object[0]);
      if (arrayOfByte != null) {
        File file = new File(this.shared.getString("dir", "/sdcard"), paramString + arrayOfByte.length + ".dex");
        if (!file.exists())
          FIO.writeByte(arrayOfByte, file.getAbsolutePath()); 
      } 
      return;
    } catch (InvocationTargetException paramString) {
      return;
    } catch (IllegalAccessException paramString) {
      return;
    } catch (IllegalArgumentException paramString) {
      return;
    } 
  }
}

他会根据选中的package,然后运行目标apk的时候把dex文件缓存到/data/data/package下。然后我们可以拿到这些dex文件,通过dex2jar工具转换为jar包,然后再通过jd_gui来查看源码。

关于xposed的hook功能,之后一篇文章会介绍如何使用。

2.VirtualXposed

现在的手机很多都是没有root权限的,若是测试机倒没事,要是是自己在用的手机root了权限显得没那么安全。这里我们可以使用VirtualXposed(下载点击这里),即使不root掉权限,我们也可以拿到我们需要的dex文件.

3.开始脱壳

现在我们应该准备好了VirtualXposed、FDex2和需要脱壳的应用ShellApk,可以开始脱壳了。

3.1 安装

将准备好的上述应用都安装到手机上

3.2 VirtualXposed添加应用

  • 启动VirtualXposed,在"设置->添加应用"中,安装FDex2和脱壳应用ShellApk。
Android开发学习之路-脱壳反编译_第1张图片

安装完后如下所示:

Android开发学习之路-脱壳反编译_第2张图片
  • 打开Xposed Installer,在"模块"中勾选FDex2,并重启设备,这里的重启是VirtualXposed的设置中重启,是个软重启。
Android开发学习之路-脱壳反编译_第3张图片

3.3 启动FDex2

启动VirtualXposed中的FDex2,选择要hook的目标,也就是ShellApk

Android开发学习之路-脱壳反编译_第4张图片

3.4 启动需要脱壳的应用

启动需要脱壳的应用ShellApk,我们所需要的dex就已经缓存好了

3.5 传输

在VirtualXposed中,“设置–>高级设置–>文件管理”,安装文件管理器.Amaze文件管理器,如果没法安装可以手动安装,当然也可以安装Total Commander自定义标签为"/data/data/io.va.exposed"。

在virtual/data/user/0/com.jared.shellapk目录下选中dex文件,然后通过分享传输到电脑上

Android开发学习之路-脱壳反编译_第5张图片

注:这里要是不能传输的话,可以试试安装qq,可以通过qq传输到电脑上。

4 查看源码

拿到了dex文件,我们就可以通过dex2jar来生成jar文件,然后通过jd-gui来查看具体的代码了(怎么从dex到jar并查看源码,这里就不分析了,可以参考这篇文章,《逆向分析反编译》)

  • 逆向后的代码如下所示:
Android开发学习之路-脱壳反编译_第6张图片

和ApkTest的源码比较,基本上一致,这里有两个方法gotoSecondActivity和showStr,下一篇文章基于此做一个hook插件。

你可能感兴趣的:(Android开发学习之路)