Legu3.0脱壳心路历程
字数 2363 2025-08-22 18:37:22

Legu3.0脱壳心路历程 - 完整教学文档

1. 概述

本教学文档详细记录了Legu3.0加固应用的完整脱壳过程,从Java层到Native层的分析方法和技巧。文档将按照实际分析流程,分步骤讲解脱壳的关键技术点。

2. 环境准备

  • 测试设备:Dalvik虚拟机4.4版本
  • 分析工具:
    • IDA Pro 6.8/7.2
    • JEB
    • Jadx
    • 010 Editor
    • readelf
  • 目标应用:TX加固的应用(特征:libshellxxx.so)

3. Java层分析

3.1 识别加固特征

  1. Manifest分析

    • 入口类LoadingActivity在APK中找不到
    • Application类被替换为com.tencent.StubShell.TxAppEntry
  2. 关键方法分析

    • 重点关注attachBaseContext方法(在onCreate之前执行)
    • 主要调用链:
      e(context) → 调试检查
      b(this) → 库地址初始化
      d(context) → 加载nfix/ufix库并调用native修复方法
      a(context) → 核心加载逻辑
      

3.2 attachBaseContext深入分析

  1. 关键方法d(context)

    • 尝试加载不存在的库nfixufix
    • 调用native方法fixNativeResourcefixUnityResource
  2. 关键方法a(context)

    • 调用e()load(f)
    • e()方法首次加载核心so库shell
    • load(f)方法加载shella-3.0.0.0.so

3.3 onCreate分析

  • 主要行为:
    • 反调试检查(isDebugger)
    • 调用native方法runCreate
    • 崩溃信息收集

3.4 广播接收器分析

  • TxReceiver类:
    • 通过native方法reciver实现广播接收
    • 可能是动态注册的广播接收器

4. Native层分析

4.1 初始挑战:Anti-IDA

  1. 现象

    • IDA无法识别so文件的节格式
    • 关键节头表(.dynstr, .dynsym)数据被抹空
  2. 解决方案

    • 使用010 Editor修改节头:
      • 将.dynsym的s_size字段置0
      • 或直接置空节数据
    • IDA会转而使用程序头表进行分析

4.2 JNI_OnLoad加密

  1. 发现

    • JNI_OnLoad函数被加密(虚拟地址0x274C)
    • 解密逻辑应在.init或.init_array节
  2. 修复节头

    • 使用开源工具修复so文件节头
    • 定位到.init_array节(地址0x3e84)
    • 找到解密函数(地址0x944)
  3. 解密逻辑

    • 对0x1000开始的0x2AB4字节数据进行解密
    • 包含JNI_OnLoad函数

4.3 解密方法选择

  1. 静态解密

    • 分析解密算法编写解密脚本
  2. 动态dump

    • 从内存中dump解密后的so文件
    • IDA脚本示例:
      static main() {
          auto i, fp;
          fp = fopen("d:\\dump", "wb");
          auto start = 0x75FFD000;
          auto size = 32768;
          for (i = start; i < start + size; i++) {
              fputc(Byte(i), fp);
          }
      }
      

4.4 JNI_OnLoad动态分析

  1. 调用路径

    • 实际由libdvm.sodvmLoadNativeCode调用
  2. PLT/GOT重定位

    • 第三方库函数调用通过PLT表跳转
    • 首次调用会解析函数地址并写入GOT表
  3. 二次解密

    • 第一层JNI_OnLoad解密后执行第二层解密
    • 从so文件尾部(偏移0x6D88)读取附加数据并解密

4.5 本地方法注册分析

  1. 注册过程

    • 使用dlsym查找JNI_OnLoad符号
    • 通过RegisterNatives注册本地方法
  2. 注册方法列表

    • load (0x75700B1D)
    • runCreate (0x756fc469)
    • changeEnv (0x756FB37D)
    • receiver (0x756f7621)
    • txEntries (0x756FB0F9)

5. 动态调试技巧

5.1 绕过反调试

  1. 方法一

    • 修改isDebuggerConnected返回值
    • 动态调试时修改寄存器/内存值
  2. 方法二

    • 使用mprop工具设置ro.debuggable属性
    • 命令:./mprop ro.debuggable 1

5.2 处理Signal 11错误

  1. 原因

    • 重打包后程序崩溃
    • 可能由签名或manifest修改引起
  2. 解决方案

    • 避免修改manifest调试属性
    • 使用注入方式附加调试

6. Dex解密与dump

6.1 load方法分析

  1. 关键操作

    • 获取odex基址(如0x750DD000)
    • 解密dex头部数据(0xE0字节)
    • 结合解密后的头部和原始数据重建dex
  2. dump脚本

    • dump头部:
      static main(void) {
          auto fp, begin, end, ptr;
          fp = fopen("d:\\header.dex", "wb");
          begin = 0x74fd7000;
          len = 0xe0;
          for (ptr = begin; ptr < begin + len; ptr++)
              fputc(Byte(ptr), fp);
      }
      
    • dump完整odex:
      static main(void) {
          auto fp, begin, end, ptr;
          fp = fopen("d:\\dump.odex", "wb");
          begin = 0x74fd7000;
          end = 0x75b2f000;
          for (ptr = begin; ptr < end; ptr++)
              fputc(Byte(ptr), fp);
      }
      

6.2 重建Dex文件

  1. 步骤
    • 搜索magic值:64 65 78 0a 30 33 35
    • 提取前0xE0字节作为header
    • 读取偏移0x20处的文件大小
    • 计算dex文件偏移:
      a1+0x6C = data_off
      a1+0x68 = data_size
      dex_addr = odex_base + 0x28 + dex_offset
      
    • 合并正确的header和body数据

7. 关键知识点总结

  1. ELF文件分析

    • IDA在节头识别失败时会使用程序头表
    • ELF节头表不会被装载进内存
  2. 动态调试技巧

    • 使用TakeMemorySnapshot(1)保存内存快照
    • 修改段属性时勾选loader选项
  3. Odex结构

    • 0x28为odex中dex_header的偏移
    • (odexAddr + 0x28)为dex header绝对地址
  4. 宏定义

    • #define HIDWORD(l) ((DWORD)(((DWORDLONG)(l) >> 32) & 0xFFFFFFFF))

8. 参考资源

  1. 加固特征识别经验
  2. 手动绕过百度加固Debug.isDebuggerConnected反调试的方法
  3. ELF文件格式规范
  4. IDA Pro高级使用技巧

通过本教学文档,读者可以系统掌握从Java层到Native层的完整脱壳流程,包括对抗Anti-IDA、解密加密函数、动态调试技巧以及Dex重建等关键技术。

Legu3.0脱壳心路历程 - 完整教学文档 1. 概述 本教学文档详细记录了Legu3.0加固应用的完整脱壳过程,从Java层到Native层的分析方法和技巧。文档将按照实际分析流程,分步骤讲解脱壳的关键技术点。 2. 环境准备 测试设备:Dalvik虚拟机4.4版本 分析工具: IDA Pro 6.8/7.2 JEB Jadx 010 Editor readelf 目标应用:TX加固的应用(特征:libshellxxx.so) 3. Java层分析 3.1 识别加固特征 Manifest分析 : 入口类 LoadingActivity 在APK中找不到 Application类被替换为 com.tencent.StubShell.TxAppEntry 关键方法分析 : 重点关注 attachBaseContext 方法(在 onCreate 之前执行) 主要调用链: 3.2 attachBaseContext深入分析 关键方法 d(context) : 尝试加载不存在的库 nfix 和 ufix 调用native方法 fixNativeResource 和 fixUnityResource 关键方法 a(context) : 调用 e() 和 load(f) e() 方法首次加载核心so库 shell load(f) 方法加载 shella-3.0.0.0.so 3.3 onCreate分析 主要行为: 反调试检查( isDebugger ) 调用native方法 runCreate 崩溃信息收集 3.4 广播接收器分析 TxReceiver 类: 通过native方法 reciver 实现广播接收 可能是动态注册的广播接收器 4. Native层分析 4.1 初始挑战:Anti-IDA 现象 : IDA无法识别so文件的节格式 关键节头表(.dynstr, .dynsym)数据被抹空 解决方案 : 使用010 Editor修改节头: 将.dynsym的s_ size字段置0 或直接置空节数据 IDA会转而使用程序头表进行分析 4.2 JNI_ OnLoad加密 发现 : JNI_ OnLoad函数被加密(虚拟地址0x274C) 解密逻辑应在.init或.init_ array节 修复节头 : 使用开源工具修复so文件节头 定位到.init_ array节(地址0x3e84) 找到解密函数(地址0x944) 解密逻辑 : 对0x1000开始的0x2AB4字节数据进行解密 包含JNI_ OnLoad函数 4.3 解密方法选择 静态解密 : 分析解密算法编写解密脚本 动态dump : 从内存中dump解密后的so文件 IDA脚本示例: 4.4 JNI_ OnLoad动态分析 调用路径 : 实际由 libdvm.so 的 dvmLoadNativeCode 调用 PLT/GOT重定位 : 第三方库函数调用通过PLT表跳转 首次调用会解析函数地址并写入GOT表 二次解密 : 第一层JNI_ OnLoad解密后执行第二层解密 从so文件尾部(偏移0x6D88)读取附加数据并解密 4.5 本地方法注册分析 注册过程 : 使用 dlsym 查找JNI_ OnLoad符号 通过 RegisterNatives 注册本地方法 注册方法列表 : load (0x75700B1D) runCreate (0x756fc469) changeEnv (0x756FB37D) receiver (0x756f7621) txEntries (0x756FB0F9) 5. 动态调试技巧 5.1 绕过反调试 方法一 : 修改 isDebuggerConnected 返回值 动态调试时修改寄存器/内存值 方法二 : 使用 mprop 工具设置 ro.debuggable 属性 命令: ./mprop ro.debuggable 1 5.2 处理Signal 11错误 原因 : 重打包后程序崩溃 可能由签名或manifest修改引起 解决方案 : 避免修改manifest调试属性 使用注入方式附加调试 6. Dex解密与dump 6.1 load方法分析 关键操作 : 获取odex基址(如0x750DD000) 解密dex头部数据(0xE0字节) 结合解密后的头部和原始数据重建dex dump脚本 : dump头部: dump完整odex: 6.2 重建Dex文件 步骤 : 搜索magic值: 64 65 78 0a 30 33 35 提取前0xE0字节作为header 读取偏移0x20处的文件大小 计算dex文件偏移: 合并正确的header和body数据 7. 关键知识点总结 ELF文件分析 : IDA在节头识别失败时会使用程序头表 ELF节头表不会被装载进内存 动态调试技巧 : 使用 TakeMemorySnapshot(1) 保存内存快照 修改段属性时勾选loader选项 Odex结构 : 0x28为odex中dex_ header的偏移 (odexAddr + 0x28) 为dex header绝对地址 宏定义 : #define HIDWORD(l) ((DWORD)(((DWORDLONG)(l) >> 32) & 0xFFFFFFFF)) 8. 参考资源 加固特征识别经验 手动绕过百度加固Debug.isDebuggerConnected反调试的方法 ELF文件格式规范 IDA Pro高级使用技巧 通过本教学文档,读者可以系统掌握从Java层到Native层的完整脱壳流程,包括对抗Anti-IDA、解密加密函数、动态调试技巧以及Dex重建等关键技术。