【APP逆向】了解加固基本原理-FDex2_1.1脱壳工具使用


一、加固后的APK包
1)为什么要加固?
  • 一定程度保护源代码

加壳又称加固技术为了加强Android保护强度,随着安全技术的发展,又出现了新型的“加固技术”。Dex加固是对Dex文件进行加壳防护,防止被静态反编译工具破解而泄露源码,

在这里插入图片描述
整体加固技术的原理如上所示,包括替换application/classes.dex、解密/动态加载原classes.dex、调用原application相关方法、将原application对象/名称设置到系统内部相关变量四大环节。其中最为关键的一步就是解密/动态加载原classes.dex,通过加密编译好的最终Dex源码文件,然后在一个新项目中用新项目的application启动来解密原项目代码并加载到内存中,再把当前进程替换为解密后的代码,能够很好地隐藏源码并防止直接性的反编译。

2)加固方式?
  • .dex加固
  • .so加固

一般在哪家应用市场发布,就要用哪家的加固方案。

APP常用加固厂商特征:

娜迦: libchaosvmp.so, libddog.so,libfdog.so
爱加密:libexec.so,libexecmain.so,ijiami.dat
梆梆: libsecexe.so,libsecmain.so , libDexHelper.so
360:libprotectClass.so,libjiagu.so, libjiagu_art.so,libjiagu_x86.so
通付盾:libegis.so,libNSaferOnly.so
网秦:libnqshield.so
百度:libbaiduprotect.so
腾讯:libshellx-2.10.6.0.so,libBugly.so,libtup.so, libexec.so,libshell.so
阿里聚安全:aliprotect.dat,libsgmain.so,libsgsecuritybody.so
腾讯御安全:libtosprotection.armeabi.so,libtosprotection.armeabi-v7a.so,libtosprotection.x86.so
网易易盾:libnesec.so
APKProtect:libAPKProtect.so
几维安全:libkwscmm.so, libkwscr.so, libkwslinker.so
3)查看app是否被加固

使用反编译工具,比如:apktool或者Android Killer 反编译一下代码就知道了,能否看到源代码或者看到的源代码是不是有意义,没意义就是代码被混淆过了,比如全是a,b,c,d这样的代码或者方法名,你看不懂就是被混淆过了,没有源代码就是被加固了。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
加壳还会加入特定的so文件进入。所以可以根据包名或者SO名来判断是否加壳以及加了什么壳(但不是说apk中有so就是加壳的,很多apk为了提升性能,增强安全性,也会用so来实现一些重要的功能)。用一些工具比如GDA(自己写的)、apk查壳PKiDApk、Detecter查壳工具等等可以直接识别出壳,甚至还能识别版本号。

例如我们将apk文件后缀改为zip后解压缩:

在这里插入图片描述
如果该目录下没有显示加固特征可以进入\lib\armeabi目录下查看.so文件

在这里插入图片描述
借助搜索引擎直接搜索

在这里插入图片描述

二、了解加固后的app启动流程

app启动流程:https://www.jianshu.com/p/910df9643d8c

加固后APK运行

  • 先加载壳APP
  • 壳APP读取Dex文件末尾的源APK大小
  • 在内存中壳APP解密出源APP
  • 运行源APP

壳有自己的Application对象

源APK有自己的Application对象

没有加固的app在程序启动后首先会进入自己的application然后找到入口函数android:name="com.xxx.xxx.xxx

经过加固后的app首先会进入壳的application然后找到壳的入口函数进行相关的解密(脱壳)操作,解密后在进入apk自己的application然后找到入口函数

在这里插入图片描述
解密源码

在这里插入图片描述
在这里插入图片描述

三、FDex2脱壳工具使用

实验环境

  • 夜神模拟器6.2.1.1
  • xposed2.7
  • FDex2(Hook ClassLoader loadClass方法 通用脱壳 )

在这里插入图片描述

1、打开xposed模块下面的FDex2

在这里插入图片描述

2、选择要脱壳的app

在这里插入图片描述
注意:点击ok后需要打开目标app后脱壳后的dex才会保存在对应的路径下。

3、找到对应路径下脱壳后的dex文件

在这里插入图片描述

4、利用adb工具将脱壳后的dex文件导出到pc
adb pull /data/data/com.iCitySuzhou.suzhou001/com.iCitySuzhou.suzhou001191572.dex D:\工具\安卓反编译

在这里插入图片描述
在这里插入图片描述

5、利用jadx反编译脱壳后的dex查看源码

在这里插入图片描述
在这里插入图片描述

可以看到在导出的5个dex文件中,该文件是这个app的源码,且代码进行了混淆。

四、分析加密参数signature
1、全局搜索参数signature

在这里插入图片描述

2、定位关键代码

我们通过抓包工具获取的数据包

在这里插入图片描述
之所以定位到如下代码,因为代码中的参数正是我们通过抓包工具获取的其中包含了signature

在这里插入图片描述

3、跟进查看代码

在这里插入图片描述
在这里插入图片描述
注意:反混淆并不是将代码完全还原,只是将原有相同的方法名(a/b)进行重命名

import android.os.Build.VERSION;
import com.hualong.framework.C2407a;
import com.hualong.framework.LibApplication;
import com.hualong.framework.p170b.C2408a;
import com.hualong.framework.p172d.C2415a;
import java.io.IOException;
import p001a.C0002u;
import p001a.C0002u.C0003a;
import p001a.C0121aa;
import p001a.C0127ac;

/* renamed from: com.iCitySuzhou.suzhou001.d.d */
public class C2651d implements C0002u {
    /* renamed from: a */
    public C0127ac mo4a(C0003a c0003a) throws IOException {
        C0121aa a = c0003a.mo5a();
        String udid = LibApplication.getInstance().getUDID();
        String valueOf = String.valueOf(System.currentTimeMillis() / 1000);
        C0127ac a2 = c0003a.mo6a(a.mo317e().mo311b("sys", "Android").mo311b("sysVersion", VERSION.RELEASE).mo311b("appVersion", LibApplication.getInstance().getAppVersion()).mo311b("appVersionCode", String.valueOf(LibApplication.getInstance().getAppVersionCode())).mo311b("udid", udid).mo311b("clientType", "android").mo311b("timestamp", valueOf).mo311b("signature", C2651d.m12099a(udid, valueOf)).mo309a());
        if (C2407a.m11253a()) {
            float l = ((float) (a2.mo352l() - a2.mo351k())) / 1000.0f;
            C2415a.m11307c("LoggingInterceptor", " ");
            C2415a.m11307c("LoggingInterceptor", " ");
            C2415a.m11307c("LoggingInterceptor", String.format("[%s] %s, %d in %.2f ms", new Object[]{a.mo314b(), a.mo312a(), Integer.valueOf(a2.mo341b()), Float.valueOf(l)}));
            C2415a.m11307c("LoggingInterceptor", "-----------------------------------");
            C2415a.m11307c("LoggingInterceptor", "          Request Headers          ");
            C2415a.m11307c("LoggingInterceptor", "-----------------------------------");
            C2415a.m11307c("LoggingInterceptor", String.format("%s", new Object[]{a.mo315c()}));
            C2415a.m11307c("LoggingInterceptor", "-----------------------------------");
            C2415a.m11307c("LoggingInterceptor", "          Response Headers          ");
            C2415a.m11307c("LoggingInterceptor", "-----------------------------------");
            C2415a.m11307c("LoggingInterceptor", String.format("%s", new Object[]{a2.mo346f()}));
            C2415a.m11307c("LoggingInterceptor", " ");
            C2415a.m11307c("LoggingInterceptor", " ");
        }
        return a2;
    }

    /* renamed from: a */
    public static String m12099a(String str, String str2) {
        return C2408a.m11256a(String.format("%s&&%s&&%s", new Object[]{str, str2, "f1190aca-d08e-4041-8666-29931cd89dde"}));
    }
}

分析如下代码:

C0127ac a2 = c0003a.mo6a(a.mo317e().mo311b("sys", "Android").mo311b("sysVersion", VERSION.RELEASE).mo311b("appVersion", LibApplication.getInstance().getAppVersion()).mo311b("appVersionCode", String.valueOf(LibApplication.getInstance().getAppVersionCode())).mo311b("udid", udid).mo311b("clientType", "android").mo311b("timestamp", valueOf).mo311b("signature", C2651d.m12099a(udid, valueOf)).mo309a());

结合抓包情况进行分析,大概猜出上述代码实际上就是给参数进行赋值操作。

例如:

  • sys=Android
  • sysVersion=VERSION.RELEASE(获取版本)

关键参数signature:

  • ignature=C2651d.m12099a(udid, valueOf)

该参数调用了m12099a方法并传入了两个参数udidvalueOf

由上面代码可查看到:

String udid = LibApplication.getInstance().getUDID();  // 抓包中可以获取(不变的值)
String valueOf = String.valueOf(System.currentTimeMillis() / 1000);  // 时间戳

两个参数已经明确,接下来需要找到m12099a方法:

在这里插入图片描述
在这里插入图片描述

 public static String m12099a(String str, String str2) {
        return C2408a.m11256a(String.format("%s&&%s&&%s", new Object[]{str, str2, "f1190aca-d08e-4041-8666-29931cd89dde"}));
}

分析上述代码可知是将传入的两个参数(udidvalueOf)与"f1190aca-d08e-4041-8666-29931cd89dde"进行格式化拼接后传给m11256a方法进行处理,处理后的结果返回。

IMEI8661740 10682363-IMSI4600 768236 19756 && 当前时间戳 && f1190aca-d08e-4041-8666-29931cd89dde

查找m11256a方法

在这里插入图片描述
在这里插入图片描述


import java.security.MessageDigest;

/* renamed from: com.hualong.framework.b.a */
public class C2408a {
    /* renamed from: a */
    public static String m11256a(String str) {
        if (str == null) {
            return null;
        }
        StringBuffer stringBuffer = new StringBuffer();
        try {
            MessageDigest instance = MessageDigest.getInstance("MD5");
            instance.update(str.getBytes());
            for (byte b : instance.digest()) {
                stringBuffer.append(Integer.toString((b >>> 4) & 15, 16)).append(Integer.toString(b & 15, 16));
            }
        } catch (Exception e) {
        }
        return stringBuffer.toString();
    }
}

分析可以发现上面代码就是最后将格式化字符串加密的地方,也就是signature生成的地方。

如果代码实现原理简单我们完全可以用python实现,如果过于复杂我们可以将上面的代码编译成jar包使用python进行调用。

五、将Java代码编译成jar包

import java.security.MessageDigest;

/* renamed from: com.hualong.framework.b.a */ 
public class MySig {
    /* renamed from: a */
    public static String m11256a(String str) {
        if (str == null) {
            return null;
        }
        StringBuffer stringBuffer = new StringBuffer();
        try {
            MessageDigest instance = MessageDigest.getInstance("MD5");
            instance.update(str.getBytes());
            for (byte b : instance.digest()) {
                stringBuffer.append(Integer.toString((b >>> 4) & 15, 16)).append(Integer.toString(b & 15, 16));
            }
        } catch (Exception e) {
        }
        return stringBuffer.toString();
    }
}

/*javac MySig.java编译时类名和文件名要一致*/

上面代码复制到文本文件重命名为MySig.java

注意:类名要和java文件名相同

1、编译Java成class

  • javac MySig.java

2、把class做成jar

  • jar cvf MySig.jar 打包目录

在这里插入图片描述
在这里插入图片描述

六、python调用jar包

参考文章:
https://blog.csdn.net/xqtesting/article/details/79895829

https://blog.csdn.net/zhusongziye/article/details/92066686

参考文章:

https://www.zhihu.com/question/26438444?sort=created

  • 3
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多学点技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值