专栏首页编码前线Android APK脱壳--腾讯乐固、360加固一键脱壳

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

概述

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

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

腾讯乐固:

360加固:

从上面可以看出,经过加固后的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:

Step3:

VirtualXposed中激活 FDex2:

Step4:

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

Step5:

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

Step6:

VirtualXposed中运行要脱壳的应用。

Step7:

脱壳后的dex文件:

导出脱壳的dex文件:

root设备:

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

未root设备:

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

Step8:

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

从上图就可以看到脱壳后的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. https://www.52pojie.cn/forum.php?mod=viewthread&tid=758726&fromguid=hot
  2. https://bbs.pediy.com/thread-224105.htm

本文分享自微信公众号 - 编码前线(gh_acef1225aadd),作者:辉天神龙

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-09-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java对象复制的方法

    在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会...

    用户1205080
  • 在Android Studio中自定义代码模板

    实例:EmptyActivity\root\src\app_package\SimpleActivity.java.ftl

    用户1205080
  • IntentService解析

    Step1. 定义IntentService的子类:传入线程名称、重写 onHandleIntent()方法

    用户1205080
  • 《SpringCloud篇:002Ribbon服务之间的负载均衡》

    Ribbon是为了实现服务与服务之间调用的负载均衡,是客户端负载均衡,并且默认采用轮询的策略。(类似于Nginx,但是Ribbon是用于服务端的,Nginx是客...

  • 05Ribbon负载均衡服务调用

    http://localhost/consumer/payment/get/130

    Remember_Ray
  • Spring Boot 之 Spring Data JPA 三(Specifications)一、首先建立一个Spring Boot 工程二、编写一个实体类来记录数据三、通过接口实现Specifica

    孙亖
  • 单片机实验说明<四>矩阵键盘与LCD基本使用

    对4×4矩阵式键盘电路的键值进行编码,编程实现在LCD液晶显示器上显示每个按键的“0-F”序号

    zhangrelay
  • (二十一)敏捷项目管理和传统项目管理有何区别?

    敏捷项目管理VS传统项目管理

    砖家认证
  • 浏览器的overflow事件

    Webkit和Firefox其实是原生支持探测元素overflow状态改变的事件。参看这个DEMO:

    mmzhou
  • 不到200行 JavaScript 代码如何实现富文本编辑器

    作者:吴锴 http://www.wukai.me/2017/12/31/rich-text-editor-in-less-than-200-lines-jav...

    企鹅号小编

扫码关注云+社区

领取腾讯云代金券