专栏首页my notebookAndroid APK脱壳--腾讯乐固、360加固一键脱壳
原创

Android APK脱壳--腾讯乐固、360加固一键脱壳

参考文章:https://www.jianshu.com/p/138c9de2c987

概述

现在使用Proguard进行混淆的代码,也很容易被破解,所以就出现了加固工具,让反编译的难度更大。但是有了加固技术,就会有反加固技术,正所谓道高一尺魔高一丈。

经过加固后的apk,通过dex2jar反编译:

腾讯乐固:

legu.png

360加固:

360jiagu.png

从上面可以看出,经过加固后的apk,通过常规方法反编译无法获取到源码。

下载工具

脱壳工具FDex2

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

下载地址:

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

VirtualXposed

VirtualXposed:无需root手机即可使用xp框架。

下载地址:

https://vxposed.com/

脱壳

Step1:

VirtualXposedFDex2需要脱壳的应用都安装到手机上。

Step2:

启动VirtualXposed,并在VirtualXposed中安装FDex2:

vp-install-fdex2.gif

Step3:

VirtualXposed中激活FDex2:

active-fdex2.gif

Step4:

VirtualXposed中安装要脱壳的应用,方法和Step2一样。

Step5:

启动VirtualXposed中的FDex2,并配置要脱壳的应用。

fdex2-config.png

Step6:

VirtualXposed中运行要脱壳的应用。

Step7:

脱壳后的dex文件:

shelling-dex.png

导出脱壳的dex文件:

root设备:

adb root
adb pull /data/user/0/iv.va.exposed/virtual/data/user/0/{packageName}   {电脑上的目录}

未root设备:

VirtualXposed中,设置-->高级设置-->文件管理,安装文件管理器,然后通过文件管理器进入到指定的目录,通过分享功能发到电脑上。

Step8:

通过dex2jar对 脱壳的dex进行反编译:

shelling-dex2jar.png

从上图就可以看到脱壳后的dex文件被成功的反编译。

FDex2核心代码MainHook

package com.ppma.xposed;
 
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
 
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
 
public class MainHook implements IXposedHookLoadPackage {
 
    XSharedPreferences xsp;
    Class Dex;
    Method Dex_getBytes;
    Method getDex;
    String packagename;
 
 
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
        xsp = new XSharedPreferences("com.ppma.appinfo", "User");
        xsp.makeWorldReadable();
        xsp.reload();
        initRefect();
        packagename = xsp.getString("packagename", null);
        XposedBridge.log("设定包名:"+packagename);
        if ((!lpparam.packageName.equals(packagename))||packagename==null) {
            XposedBridge.log("当前程序包名与设定不一致或者包名为空");
            return;
        }
        XposedBridge.log("目标包名:"+lpparam.packageName);
        String str = "java.lang.ClassLoader";
        String str2 = "loadClass";
 
        XposedHelpers.findAndHookMethod(str, lpparam.classLoader, str2, String.class, Boolean.TYPE, new XC_MethodHook() {
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                Class cls = (Class) param.getResult();
                if (cls == null) {
                    //XposedBridge.log("cls == null");
                    return;
                }
                String name = cls.getName();
                XposedBridge.log("当前类名:" + name);
                byte[] bArr = (byte[]) Dex_getBytes.invoke(getDex.invoke(cls, new Object[0]), new Object[0]);
                if (bArr == null) {
                    XposedBridge.log("数据为空:返回");
                    return;
                }
                XposedBridge.log("开始写数据");
                String dex_path = "/data/data/" + packagename + "/" + packagename + "_" + bArr.length + ".dex";
                XposedBridge.log(dex_path);
                File file = new File(dex_path);
                if (file.exists()) return;
                writeByte(bArr, file.getAbsolutePath());
            }
            } );
    }
 
    public void initRefect() {
        try {
            Dex = Class.forName("com.android.dex.Dex");
            Dex_getBytes = Dex.getDeclaredMethod("getBytes", new Class[0]);
            getDex = Class.forName("java.lang.Class").getDeclaredMethod("getDex", new Class[0]);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
 
    }
 
    public  void writeByte(byte[] bArr, String str) {
        try {
            OutputStream outputStream = new FileOutputStream(str);
            outputStream.write(bArr);
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
            XposedBridge.log("文件写出失败");
        }
    }
}

参考链接

  1. 【手机端】腾讯乐固,360加固一键脱壳
  2. 安卓xposed脱壳工具FDex2

悲伤的夏洛特@席玉铎666

好文要顶

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • xposed模块开发流程

    xposed是什么: 必须部署在ROOT后的安卓手机上,通过替换/system/bin/app_process程序控制zygote进程,使得app_proces...

    悲伤的夏洛特@席玉铎666
  • Hook Java方法

    Java.use方法用于加载一个Java类,相当于Java中的Class.forName()。比如要加载一个String类:

    悲伤的夏洛特@席玉铎666
  • Android逆向分析大全

    Android程序的特点相比在于使用混淆方式打包,将包名、类名、函数名改成不易看懂的字母,从而使生成的apk小很多(android studio提供了relea...

    悲伤的夏洛特@席玉铎666
  • 关于云技术混合架构的三个认识误区

    我以一位负责以云服务为基础实现多种业务解决方案交付工作的CIO的身份表达自己对混合架构的观点。在过去五个月中,我有幸参与到十几次高层对话当中,交流对象包括多位来...

    静一
  • 中文点选验证码之自动识别

    某次测试中遇到了汉字点选的验证码,看着很简单,尝试了一下发现有两种简单的识别方法,终于有空给重新整理一下,分享出来。

    FB客服
  • 前端基础-正则语法(元字符)

    限定符(量词)用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6种。

    cwl_java
  • 【死磕Sharding-jdbc】---group by结果合并(2)

    在sharding-jdbc源码之group by结果合并(1)中主要分析了sharding-jdbc如何在GroupByStreamResultSetMerg...

    用户1655470
  • 4 个妙招增强 Jupyter Notebook 功能

    Jupyter Notebook 是所有开发者共享工作的神器,它为共享 Notebooks 提供了一种便捷方式:结合文本、代码和图更快捷地将信息传达给受众。目前...

    智能算法
  • 4 个妙招增强 Jupyter Notebook 功能

    Jupyter Notebook 是所有开发者共享工作的神器,它为共享 Notebooks 提供了一种便捷方式:结合文本、代码和图更快捷地将信息传达给受众。目前...

    1480
  • 新年礼物请查收~TAPD 5.0重磅上线!

    相信现在的你,应该已经收到了TAPD团队精心准备的惊喜。 一直以来,TAPD团队都在思考,如何为用户提供更加轻量化、数字化、一体化、自动化的服务,让协作更敏捷...

    TAPD敏捷研发

扫码关注云+社区

领取腾讯云代金券