契约锁逆向(失败,已老实正在学习)

参考

00x1 查壳

之前那篇文章分析文件结构的时候,其实就有看到了,这里有360加壳的特征。

Android 检查

确定为360加壳

00x2 加壳

原理

加壳全称为可执行程序资源压缩,是一种通过特殊算法对可执行文件资源进行压缩保护的技术。压缩后的程序可直接运行,其解压过程在内存中完成,通过优先控制权转移隐藏原始程序入口点(OEP),阻止静态反编译和动态分析。加壳工具分为压缩壳和加密壳两类,压缩壳以减小软件体积为主,加密壳侧重于代码保护并可附加注册机制等功能限制。在Android平台称为加固技术,包含整体Dex加固、拆分Dex加固及虚拟机加固等方式,通过替换application/classes.dex或拆分Dex文件防止反编译,虚拟机加固会对字节码进行变化处理 1

木马或病毒常利用加壳技术封装恶意代码以绕过杀毒软件检测,部分网络犯罪案件中存在通过加壳实施免杀技术的案例 2-3。判断APK是否加壳可通过检查特定SO文件、包名或使用查壳工具识别外壳Application和解密代码的so文件

360 加壳

在Android应用开发中,为了保护自己的代码和数据,开发者经常会对自己的应用进行加壳处理。360加固就是其中一种常用的加壳方式。然而,对于一些安全研究者或者黑客来说,他们需要通过逆向工程来分析应用的行为或者寻找漏洞,这就需要对加壳的应用进行脱壳操作。

首先,我们需要了解360加固的原理。360加固主要通过修改应用的DEX文件和ODEX文件来实现加壳。它会在DEX文件和ODEX文件的基础上,增加一层加密和混淆,使得应用在运行时无法被反编译成可读的代码。同时,360加固还会对应用的资源文件进行混淆和加密,使得应用的数据也无法被轻易获取。

1 加壳

1.1 什么是加壳

加壳是在二进制的程序中植入一段代码,在运行的时候优先取得程序的控制权,做一些额外的工作。大多数病毒就是基于此原理。是应用加固的一种手法对原始二进制原文进行加密/隐藏/混淆。

1.2 加壳作用

加壳的程序可以有效阻止对程序的反汇编分析,以达到它不可告人的目的。这种技术也常用来保护软件版权,防止被软件破解。

1.3 加壳原理

市面上主要的加壳原理:将原Dex文件加密,加密后的Dex文件和壳Dex文件一起重新打包,产生新的Dex文件。安卓虚拟机加载的时候,会从壳Dex文件的ProxyApplication类开始,执行attachBaseContext方法和onCreate方法。而壳Dex文件会在这两个方法里面实现原Dex文件的还原和加载,让虚拟机顺利启动APP。

在这个过程中,牵扯到三个角色:

1、加壳程序:加密源程序为解壳数据、组装解壳程序和解壳数据

2、解壳程序:解密解壳数据,并运行时通过DexClassLoader动态加载

3、源程序:需要加壳处理的被保护代码

原理

参考

https://developer.baidu.com/article/details/3037868

http://www.newxtc.com/article.php?id=227

https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458457914&idx=1&sn=1d505597b47089cbd8bdc1e6e0a2b068&chksm=b18e27b086f9aea63d4404f118b0dc4c0b87c5986f20e556a5a85c967834df0d7f3e8e6d98d6&scene=27

https://bbs.kanxue.com/thread-271538-1.htm

壳代码

加壳之后一般文件清单也会消失不见。

很多加固方案会修改 AndroidManifest.xml,把原本的 <application android:name=".RealApplication"> 替换成自己的 StubApp(壳入口)。

StubApp 并不实现业务逻辑,而是:

· 在 attachBaseContext() / onCreate() 中解密原始Dex

· 通过反射/类加载器创建真实的 Application 对象

· 将所有生命周期方法(onCreate、onTerminate 等)委托给真实 Application 调用

这本质就是代理模式:StubApp 是代理类,真实 Application 是被代理类。

java 复制代码
 protected final void attachBaseContext(Context paramContext) {
    System.currentTimeMillis();
    super.attachBaseContext(paramContext);
    a.b();
    d = paramContext;
    if (a == null)
      a = this; 
    if (b == null) {
      Boolean bool3 = Boolean.valueOf(a.a());
      Boolean bool1 = Boolean.valueOf(false);
      Boolean bool2 = Boolean.valueOf(false);
      if (Build.CPU_ABI.contains("64") || Build.CPU_ABI2.contains("64"))
        bool1 = Boolean.valueOf(true); 
      if (Build.CPU_ABI.contains("mips") || Build.CPU_ABI2.contains("mips"))
        bool2 = Boolean.valueOf(true); 
      if (bool3.booleanValue() && needX86Bridge)
        System.loadLibrary("X86Bridge"); 
      if (loadFromLib) {
        if (bool3.booleanValue() && !needX86Bridge) {
          System.loadLibrary("jiagu_x86");
        } else {
          System.loadLibrary("jiagu");
        } 
      } else {
        String str = paramContext.getFilesDir().getParentFile().getAbsolutePath();
        try {
          String str1 = paramContext.getFilesDir().getParentFile().getCanonicalPath();
          str = str1;
        } catch (Exception exception) {}
        str = str + "/.jiagu";
        i = a(str, bool1.booleanValue(), bool2.booleanValue());
        e = a(str, false, false);
        f = str + File.separator + e;
        g = str + File.separator + i;
        h = str;
        if (bool2.booleanValue()) {
          a.a(paramContext, c + "_mips.so", str, e);
        } else if (bool3.booleanValue() && !needX86Bridge) {
          a.a(paramContext, c + "_x86.so", str, e);
        } else {
          a.a(paramContext, c + ".so", str, e);
        } 
        if (bool1.booleanValue() && !bool2.booleanValue()) {
          boolean bool;
          if (bool3.booleanValue() && !needX86Bridge) {
            bool = a.a(paramContext, c + "_x64.so", str, i);
          } else {
            bool = a.a(paramContext, c + "_a64.so", str, i);
          } 
          if (bool) {
            System.load(str + "/" + i);
          } else {
            System.load(str + "/" + e);
          } 
        } else {
          System.load(str + "/" + e);
        } 
      } 
    } 
    DtcLoader.init();
    interface5(a);
    if (b == null) {
      b = a(paramContext);
      if (b != null)
        try {
          Method method = Application.class.getDeclaredMethod("attach", new Class[] { Context.class });
          if (method != null) {
            method.setAccessible(true);
            method.invoke(b, new Object[] { paramContext });
          } 
          interface8(b, paramContext);
          return;
        } catch (Exception exception) {
          throw new RuntimeException("Failed to call attachBaseContext.", exception);
        }  
    } else {
      return;
    } 
    System.exit(1);
  }
  
java 复制代码
public final void onCreate() {
    System.currentTimeMillis();
    super.onCreate();
    ChangeTopApplication();
    if (b != null)
      b.onCreate(); 
    interface21(b);
    Application application = this;
    if (b != null)
      application = b; 
    Context context = d;
    if (application != null && context != null && a.a(context))
      try {
        Method method = Class.forName(a.a("s}>zw>rx>Bu`bdcDy}u")).getDeclaredMethod(a.a("BuwycdubQsdyfydiSq||Rqs{c"), new Class[] { Application.class });
        method.setAccessible(true);
        method.invoke(null, new Object[] { application });
      } catch (Exception exception) {} 
  }
}

代理类,可以理解成他只是一个把真正逻辑转移到另外一个入口的桥梁,加壳可不可以理解成他是把原来的入口内进行篡改,xml进行隐藏,还有可能把重要的逻辑拼接到apk解包下其他的文件中。

比如.so加壳

加固壳加载SO文件主要会用到以下几大类方法:

· Java层标准API (System.load / System.loadLibrary):Android最常规的加载方式,壳通常用 System.load(path) 来加载从APK中释放到特定目录的SO文件。 System.loadLibrary 则用于加载APK安装时就被放置在标准位置的SO文件。

· 自定义/第三方加载器 (DexClassLoader / 自定义Linker):用于高级场景和对抗分析。可利用 DexClassLoader 的 librarySearchPath 参数加载外部SO;而自定义Linker如 SoLoader、ReLinker,可解决加载失败等兼容性问题,或在主 dlopen 前注入解密/防dump逻辑。对抗更深的一层壳甚至会在Native层绕过标准API,模拟系统加载过程,让分析者难以直接跟踪。

· Native层核心API (dlopen):System.load 最终都会调用到Native层的 dlopen 函数。无论Java层如何调用,内核级的加载都绕不开 dlopen。

· 非常规技巧 (不落地加载 / 伪装后缀 / 动态释放):为了"隐藏"自己,壳会用非常规方式加载SO。比如将SO伪装成 .png、.mp3 等文件放入 assets/ 目录;或运行时拷贝APK中的SO文件到 /data/data/包名/ 等私有目录后再 System.load。在Android 8.0以上,还可以使用 InMemoryDexClassLoader,让Dex文件只在内存中存在,磁盘上不留痕迹。

这些加载方式的核心,始终围绕着 System.load() 及其底层的 dlopen() 函数。无论壳的招式多花哨,最终都要回归到这个核心。因此,对于想做脱壳分析的你来说,重点关注 dlopen() 函数的调用,是找到SO加载逻辑、进而实施Hook的关键思路。

参考

https://modun.blog.csdn.net/article/details/106994496

脱壳

参考

https://cloud.tencent.com/developer/article/1180950

所以查询了一下基本上得要在虚拟手机上才能脱壳,先整动态的呗。

00x3 root

相关推荐
江华森1 小时前
Jenkins 从入门到精通 — 完整学习笔记
笔记·学习·jenkins
江华森1 小时前
Kafka 从入门到精通 — 完整学习笔记
笔记·学习·kafka
小陈phd1 小时前
多模态大模型学习笔记(四十二)——从像素到语义的精准问询——视觉问答(VQA)
笔记·学习
wuyuanshun1 小时前
人工智能学习总结(一)
人工智能·学习
吃好睡好便好1 小时前
詹姆斯·艾伦语录
学习·生活
踏着七彩祥云的小丑2 小时前
AI学习——Gradio快速界面
人工智能·学习·ai
「維他檸檬茶」2 小时前
大模型算法学习2026.6.1
学习·算法·大模型·nlp
江华森2 小时前
Git + Maven Java 项目部署实战全指南
运维·笔记·git·学习·maven
小陈phd2 小时前
多模态大模型学习笔记(四十三)—— 视觉定位(Visual Grounding):语言描述在图像中的精准锚定
笔记·学习·目标跟踪