【Android逆向】脱壳项目frida_dump 原理分析

android,逆向,脱壳,项目,frida,dump,原理,分析 · 浏览次数 : 261

小编点评

**核心函数:dump_dex** **功能:** 根据定义的类链接文件(libart.so),生成描述类的dex文件。 **步骤:** 1. 通过 `Process.findModuleByName()` 获取 libart 模块。 2. 遍历库中所有符号,寻找定义类函数的符号名。 3. 通过函数名检查符号的名称是否包含 "ClassLinker"、"DefineClass" 和 "Thread"。 4. 如果符号名包含这些词语,则记录其地址。 5. 创建一个 `DexMap` 对象,存储定义类的信息。 6. 遍历定义类中所有 `DexFile` 对象。 7. 对于每个 `DexFile` 对象,读取其大小、类型和其他信息。 8. 将所有 `DexFile` 对象的结构信息存储在 `DexMap` 中。 9. 创建一个新的文件并写入 `DexMap` 的内容。 10. 使用 `File` 类打开文件并写入数据。 **关键代码:** ```javascript var libart = Process.findModuleByName("libart.so"); var addr_DefineClass = null; for (var index = 0; index < symbols.length; index++) { var symbol = symbols[index]; if (symbol_name.indexOf("ClassLinker") > 0 && symbol_name.indexOf("DefineClass") > 0 && symbol_name.indexOf("Thread") > 0 && symbol_name.indexOf("DexFile") > 0) { console.log(symbol_name, symbol.address); addr_DefineClass = symbol.address; } } ```

正文

脱dex核心文件dump_dex.js

核心函数

function dump_dex() {
    var libart = Process.findModuleByName("libart.so");
    var addr_DefineClass = null;
    var symbols = libart.enumerateSymbols();
    for (var index = 0; index < symbols.length; index++) {
        var symbol = symbols[index];
        var symbol_name = symbol.name;
        //这个DefineClass的函数签名是Android9的
        //_ZN3art11ClassLinker11DefineClassEPNS_6ThreadEPKcmNS_6HandleINS_6mirror11ClassLoaderEEERKNS_7DexFileERKNS9_8ClassDefE
        if (symbol_name.indexOf("ClassLinker") >= 0 &&
            symbol_name.indexOf("DefineClass") >= 0 &&
            symbol_name.indexOf("Thread") >= 0 &&
            symbol_name.indexOf("DexFile") >= 0) {
            console.log(symbol_name, symbol.address);
            addr_DefineClass = symbol.address;
        }
    }
    var dex_maps = {};
    var dex_count = 1;

    console.log("[DefineClass:]", addr_DefineClass);
    if (addr_DefineClass) {
        Interceptor.attach(addr_DefineClass, {
            onEnter: function(args) {
                var dex_file = args[5];
                //ptr(dex_file).add(Process.pointerSize) is "const uint8_t* const begin_;"
                //ptr(dex_file).add(Process.pointerSize + Process.pointerSize) is "const size_t size_;"
                var base = ptr(dex_file).add(Process.pointerSize).readPointer();
                var size = ptr(dex_file).add(Process.pointerSize + Process.pointerSize).readUInt();

                if (dex_maps[base] == undefined) {
                    dex_maps[base] = size;
                    var magic = ptr(base).readCString();
                    if (magic.indexOf("dex") == 0) {

                        var process_name = get_self_process_name();
                        if (process_name != "-1") {
                            var dex_dir_path = "/data/data/" + process_name + "/files/dump_dex_" + process_name;
                            mkdir(dex_dir_path);
                            var dex_path = dex_dir_path + "/class" + (dex_count == 1 ? "" : dex_count) + ".dex";
                            console.log("[find dex]:", dex_path);
                            var fd = new File(dex_path, "wb");
                            if (fd && fd != null) {
                                dex_count++;
                                var dex_buffer = ptr(base).readByteArray(size);
                                fd.write(dex_buffer);
                                fd.flush();
                                fd.close();
                                console.log("[dump dex]:", dex_path);

                            }
                        }
                    }
                }
            },
            onLeave: function(retval) {}
        });
    }
}

原理是:通过hook libart ,然后开始枚举符号表,根据符号名称找到DefineClass函数,通过DefineClass的地址来得到它的入参中的DexFile对象
# /art/runtime/class_linker.cc

 mirror::Class* ClassLinker::DefineClass(Thread* self,
                                          const char* descriptor,
                                          size_t hash,
                                          Handle<mirror::ClassLoader> class_loader,
                                          const DexFile& dex_file,
                                         const DexFile::ClassDef& dex_class_def) {

......
}
DexFile 数据结构为
 struct DexFile {
      /* directly-mapped "opt" header */
      const DexOptHeader* pOptHeader;
  
      /* pointers to directly-mapped structs and arrays in base DEX */
      const DexHeader*    pHeader;
      const DexStringId*  pStringIds;
      const DexTypeId*    pTypeIds;
      const DexFieldId*   pFieldIds;
      const DexMethodId*  pMethodIds;
      const DexProtoId*   pProtoIds;
      const DexClassDef*  pClassDefs;
      const DexLink*      pLinkData;
  
      /*
       * These are mapped out of the "auxillary" section, and may not be
       * included in the file.
       */
      const DexClassLookup* pClassLookup;
      const void*         pRegisterMapPool;       // RegisterMapClassPool
  
      /* points to start of DEX file data */
      const u1*           baseAddr;
  
      /* track memory overhead for auxillary structures */
      int                 overhead;
  
      /* additional app-specific data structures associated with the DEX */
      //void*               auxData;
};

struct DexOptHeader {
      u1  magic[8];           /* includes version number */
  
      u4  dexOffset;          /* file offset of DEX header */
      u4  dexLength;
      u4  depsOffset;         /* offset of optimized DEX dependency table */
      u4  depsLength;
      u4  optOffset;          /* file offset of optimized data tables */
      u4  optLength;
  
      u4  flags;              /* some info flags */
      u4  checksum;           /* adler32 checksum covering deps/opt */
  
      /* pad for 64-bit alignment if necessary */
  };
根据DexFile的数据结构可以得出 dexOffset 就是dex的偏移 ,dexLength 就是dex的大小, 这样就可以将dex整体dump出了
var base = ptr(dex_file).add(Process.pointerSize).readPointer();
var size = ptr(dex_file).add(Process.pointerSize + Process.pointerSize).readUInt();

与【Android逆向】脱壳项目frida_dump 原理分析相似的内容:

【Android逆向】脱壳项目frida_dump 原理分析

脱dex核心文件dump_dex.js 核心函数 function dump_dex() { var libart = Process.findModuleByName("libart.so"); var addr_DefineClass = null; var symbols = libart.e

【Android逆向】脱壳项目 frida-dexdump 原理分析

1. 项目代码地址 https://github.com/hluwa/frida-dexdump 2. 核心逻辑为 def dump(self): logger.info("[+] Searching...") st = time.time() ranges = self.agent.search_

【Android逆向】制作Fart脱壳机,完成对NCSearch的脱壳操作

1. 我的手机是Pixel 1 ,下载fart对应的镜像 镜像位置具体参考大佬博客 https://www.anquanke.com/post/id/201896 2 执行 adb reboot bootloader ——重启手机到fastboot模式, 直接重启手机到fastboot模式,不用关机

【Android逆向】制作Youpk脱壳机,完成对NCSearch的脱壳操作

1. 拉去youpk 代码或镜像,自行编译构建 youpk 代码地址 https://github.com/youlor/unpacker 2. 执行 adb reboot bootloader 3. 执行 sh flash-all.sh 4. 安装NCSearch,并启动app 5. 执行adb

android 逆向笔记

壳检测工具 GDA 2. 逆向分析APP 一般流程 1. 使用自动化检测工具检测APP是否加壳,或者借助一些反编译工具依靠经验判断是否加壳 2. 如果apk加壳,则需要先对apk进行脱壳 3. 使用`jeb`, `jadx`, `apktool`等反编译工具对apk进行反编译 4. 先依据静态分析得

【Android逆向】frida 破解 jwxdxnx02.apk

apk 路径: https://pan.baidu.com/s/1cUInoi 密码:07p9 这题比较简单,主要是用于练习frida 1. 安装apk到手机 需要输入账号密码 2. 使用jdax 查看apk package hfdcxy.com.myapplication; import andr

[Android逆向]Exposed 破解 jwxdxnx02.apk

使用exposed 遇到了一些坑,这里记录一下 源码: package com.example.exposedlesson01; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.X

[Android 逆向]frida 破解 切水果大战原版.apk

1. 手机安装该apk,运行,点击右上角礼物 提示 支付失败,请稍后重试 2. apk拖入到jadx中,待加载完毕后,搜素失败,找到疑似目标类MymmPay的关键方法payResultFalse 4. adb logcat 或者androidstudio 查看该进程的日志,发现以下日志 com.mf

[Android 逆向]Xposed 破解 切水果大战原版.apk

代码 public class Main implements IXposedHookLoadPackage { boolean flag = false; @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam

【Android逆向】frida 破解 滚动的天空

1. apk 安装到手机中 2. 玩十次之后,会提示 充值 3. adb shell dumpsys window | grep mCurrentFocus 查看一些当前activity是哪一个 是 AppActivity 4. 阅读代码,感觉是unity3d做的游戏 5. apk拖入到jadx中,