你会APK脱壳吗?

概述

众所周知,Android应用开发完成后,除了使用Google官方的混淆外,还需要使用一些第三方的安全软件的加壳处理,比较出名的有腾讯乐固、360加固和爱加密等。我之前所在的公司,就是使用爱加密进行加壳处理的。

虽然加密后,让软件的安全性更高了,但并不是无懈可击,一些反加固技术和脱壳技术应运而生。今天要说的就是腾讯乐固、360加固一键脱壳。

工程,经过加固后的apk,通过dex2jar反编译效果是下面这样的:

腾讯乐固加固:
360加固
可以发现,经过加固处理由,反编译是无法直接获取到源码的,代码的结构如下图所示:工具

要对Android的apk文件进行脱壳,需要使用的软件有:

  • FDex2

  • VirtualXposed

不过,需要说明的是,此技术在Android9.0及以上版本是行不通的,并且VirtualXposed有软件版本限制。

FDex2

下载地址:
链接: https://pan.baidu.com/share/init?surl=0ZfD2MSfukuLdxvUZIAyjA 提取码: asu1

VirtualXposed

VirtualXposed:无需root手机即可使用Xposed框架
下载链接:
https://vxposed.com/

脱壳

首先,将VirtualXposed、FDex2和需要脱壳的应用都安装到手机上。然后,启动VirtualXposed,并在VirtualXposed中安装FDex2。
然后,在VirtualXposed中选择模块管理激活FDex2。
在VirtualXposed中安装要脱壳的应用,具体和上面的步骤一样。然后,启动VirtualXposed中的FDex2,并配置要脱壳的应用。
在VirtualXposed中运行要脱壳的应用,脱壳后的dex文件如下图:
然后,使用adb pull命令将脱壳后的dex文件导出到电脑。

adb pull /data/user/0/iv.va.exposed/virtual/data/user/0/{packageName}

最后,再通过dex2jar对 脱壳的dex进行反编译。

FDex2核心代码

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("文件写出失败");        }    }}

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


首先,查壳,使用PEID或者是FI,为UPX v1.08 接下来用OD载入,提示为“压缩代码是否继续分析”,我们选择否 我在这里介绍几种方法,请看我的操作。 方法1:单步跟踪(最常见的方法) 0040E8C0 N> 60 pushad //停在这里了,我们F8单步 0040E8C1 BE 15B04000 mov esi,NOTEPAD.0040B015 0040E8C6 8DBE EB5FFFFF lea edi,dword ptr ds:[esi+FFFF> 0040E8CC 57 push edi 0040E8CD 83CD FF or ebp,FFFFFFFF 0040E8D0 EB 10 jmp short NOTEPAD.0040E8E2 //跳 。。。。。。。。。。 0040E8E2 8B1E mov ebx,dword ptr ds:[esi] //跳到这里,继续单步 0040E8E4 83EE FC sub esi,-4 0040E8E7 11DB adc ebx,ebx 0040E8E9 ^ 72 ED jb short NOTEPAD.0040E8D8 //这里要往回跳了 0040E8EB B8 01000000 mov eax,1 //F4,然后继续F8 0040E8F0 01DB add ebx,ebx 0040E8F2 75 07 jnz short NOTEPAD.0040E8FB //跳 。。。。。。。。。。。 0040E8FB 11C0 adc eax,eax //来到这里,F8继续 0040E8FD 01DB add ebx,ebx 0040E8FD 01DB add ebx,ebx 0040E8FF ^ 73 EF jnb short NOTEPAD.0040E8F0 0040E901 75 09 jnz short NOTEPAD.0040E90C //跳 。。。。。。。。。。。 0040E90C 31C9 xor ecx,ecx //跳到这里,继续F8 0040E90E 83E8 03 sub eax,3 0040E90E 83E8 03 sub eax,3 0040E911 72 0D jb short NOTEPAD.0040E920 //跳 。。。。。。。。。。。 0040E920 01DB add ebx,ebx //跳到这里,继续F8 0040E922 75 07 jnz short NOTEPAD.0040E92B //跳 。。。。。。。。。。。 0040E92B 11C9 adc ecx,ecx //跳到了这里,继续F8 0040E92D 01DB add ebx,ebx 0040E92F 75 07 jnz short NOTEPAD.0040E938 //跳 。。。。。。。。。。。 0040E938 11C9 adc ecx,ecx //跳到这里,继续F8 0040E93A 75 20 jnz short NOTEPAD.0040E95C //跳 。。。。。。。。。。。 0040E95C 81FD 00F3FFFF cmp ebp,-0D00 //来到这,继续F8 0040E962 83D1 01 adc ecx,1 0040E965 8D142F lea edx,dword ptr ds:[edi+ebp] 0040E968 83FD FC
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值