Frida07 - dexdump核心源码分析

news2025/2/3 21:03:51

项目地址

https://github.com/hluwa/frida-dexdump

代码解析

项目中的核心函数是 searchDex:

function searchDex(deepSearch) {
    var result = [];
    Process.enumerateRanges('r--').forEach(function (range) {
        try {
            ....
        } catch (e) {
        }
    });
    return result;
}

里面用了一个新的API,Process.enumerateRanges,我们看一下API介绍:

enumerates memory ranges satisfying protection given as a string of the form: rwx, where rw- means “must be at least readable and writable”.

使用这个API可以在进程中搜索所有可读的内存段,我们可以直接传递 ‘r—’ 的形式,也可以传递一个对象:{protection: '---', coalesce: true } ,coalesce 的值表示是否需要合并相同权限的内存段,默认是 false。

这个函数会返回一个数组对象,里面的元素有如下属性:

  1. base:基地址,NativePointer,可以理解为C里面的指针。

  2. size:内存块大小,in bytes

  3. protection:保护属性,string

  4. file:(如果有的话)内存映射文件:

    1. path,文件路径,string

    2. offset,文件内偏移,in bytes

    3. size,文件大小,in bytes

继续看源码:

Memory.scanSync(range.base, range.size, "64 65 78 0a 30 ?? ?? 00").forEach(function (match) {
    if (range.file && range.file.path && (range.file.path.startsWith("/data/dalvik-cache/") || range.file.path.startsWith("/system/"))) {
        return;
    }

    if (verify(match.address, range, false)) {
        var dex_size = get_dex_real_size(match.address, range.base, range.base.add(range.size));
        result.push({
            "addr": match.address,
            "size": dex_size
        });
        var max_size = range.size - match.address.sub(range.base).toInt32();

        if (deepSearch && max_size != dex_size) {
            result.push({
                "addr": match.address,
                "size": max_size
            });
        }
    }
});

又用到了一个新的API,Memory.scanSync,看看文档介绍:

scan memory for occurrences of pattern in the memory range given by address and size.

就是按照 pattern 给定的模式来搜索指定范围的内存是否又匹配的。

64 65 78 0a 30 ?? ?? 00

表示搜索的模式是 以 64 65 78 0a 30 字节开头的,中间两个字节不关心,后面跟着一个 00 的8个字节,如果有满足的则触发回调。

为啥要搜索这几个字节呢?是因为这几个字节是 dex 的文件魔数。可以看下官方文档介绍:

https://source.android.com/docs/core/runtime/dex-format?hl=zh-cn

作者设置的比较宽泛,中间的两个字节表示的是 dex 的版本号,会搜索所有版本号的 dex。

文档介绍 pattern 还有一个 r2-style 的写法,但是搜了一下没看太明白,就不说了。

回调会传递一个对象,里面的属性有:

  1. onMatch: function(address, size): 扫描到一个内存块,起始地址是address,大小size的内存块,返回字符串 stop 表示停止扫描

  2. onError: function(reason): 扫描内存的时候出现内存访问异常的时候回调

  3. onComplete: function(): 内存扫描完毕的时候调用

再回到源码:

if (range.file && range.file.path && (range.file.path.startsWith("/data/dalvik-cache/") || range.file.path.startsWith("/system/"))) {
    return;
}

系统app的dex,我们不需要。

if (verify(match.address, range, false)) {
    var dex_size = get_dex_real_size(match.address, range.base, range.base.add(range.size));
    result.push({
        "addr": match.address,
        "size": dex_size
    });
    var max_size = range.size - match.address.sub(range.base).toInt32();

    if (deepSearch && max_size != dex_size) {
        result.push({
            "addr": match.address,
            "size": max_size
        });
    }
}

verify 函数是对 dex 进行校验,主要是根据自己对 dex 文件的熟悉程度来做校验。

比如 dex 文件应该至少有 0x70 个字节,因为这是 dex 文件头的大小。

比如,0x3c位置的字节必定是 0x70,因为文件头后面跟着的就是字符串。

作者还开了一个深度验证,利用maps,其实原理很简单,我们使用010editor打开一个dex:

文件头里面有一个 map_off 字段,它的值是 map_list 段在dex文件内的偏移。

我们再看 map_list 段:

这里也储存了自身的一个偏移,那么根据这两个东西,就可以认为这个是dex文件。

具体代码如下:

function verify_by_maps(dexptr, mapsptr) {
    var maps_offset = dexptr.add(0x34).readUInt();
    var maps_size = mapsptr.readUInt();

    for (var i = 0; i < maps_size; i++) {
        var item_type = mapsptr.add(4 + i * 0xC).readU16();

        if (item_type === 4096) {
            var map_offset = mapsptr.add(4 + i * 0xC + 8).readUInt();

            if (maps_offset === map_offset) {
                return true;
            }
        }
    }

    return false;
}

然后再计算 map_list 结束的位置:

function get_maps_end(maps, range_base, range_end) {
    var maps_size = maps.readUInt();

    if (maps_size < 2 || maps_size > 50) {
        return null;
    }

    var maps_end = maps.add(maps_size * 0xC + 4);

    if (maps_end < range_base || maps_end > range_end) {
        return null;
    }

    return maps_end;
}

最后通过减掉起始地址,就可以得到真正的文件大小了:

function get_dex_real_size(dexptr, range_base, range_end) {
    var dex_size = dexptr.add(0x20).readUInt();
    var maps_address = get_maps_address(dexptr, range_base, range_end);

    if (!maps_address) {
        return dex_size;
    }

    var maps_end = get_maps_end(maps_address, range_base, range_end);

    if (!maps_end) {
        return dex_size;
    }

    return maps_end.sub(dexptr).toInt32();
}

如果开了深度搜索,匹配方式又有不同:

Memory.scanSync(range.base, range.size, "70 00 00 00").forEach(function (match) {
    var dex_base = match.address.sub(0x3C);

    if (dex_base < range.base) {
        return;
    }

    if (dex_base.readCString(4) != "dex\n" && verify(dex_base, range, true)) {
        var real_dex_size = get_dex_real_size(dex_base, range.base, range.base.add(range.size));

        if (!verify_ids_off(dex_base, real_dex_size)) {
            return;
        }

        result.push({
            "addr": dex_base,
            "size": real_dex_size
        });
        var max_size = range.size - dex_base.sub(range.base).toInt32();

        if (max_size != real_dex_size) {
            result.push({
                "addr": dex_base,
                "size": max_size
            });
        }
    }
});

70 00 00 00 是dex文件头里面字符串的偏移段。这是因为有些加固厂商会修改 dex 的魔数,所以作者选择了这种匹配方式。

可以看到,逆向的重心,除了api用的熟之外,还需要对app本身的相关知识要有足够的了解才行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1328701.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV-Python(19):Canny边缘检测

目录 学习目标 Canny 边缘检测原理 1.噪声抑制(噪声去除) 2.梯度计算 3.非极大值抑制 4.双阈值检测(滞后阈值) 5.边缘连接 Canny 边缘检测步骤 Canny 边缘检测的OpenCV实现 不同阈值的边缘检测效果 学习目标 了解Canny边缘检测的概念学习掌握函数cv2.Canny()的用法 …

Django(二)

1.django框架 1.1 安装 pip install django3.21.2 命令行 创建项目 cd 指定目录 django-admin startproject 项目名mysite ├── manage.py [项目的管理工具] └── mysite├── __init__.py├── settings.py 【配置文件&#xff0c;只有一部分…

电子科大软件测试~第三次作业

第三次作业 第一题 采用JUnit软件测试框架进行测试程序编程&#xff0c;实现对下面java程序进行单元测试&#xff0c;找出其中缺陷。然后修改缺陷&#xff0c;直到通过单元测试&#xff0c;给出测试程序脚本和运行结果界面。 public class getMax {public int get_max(int x…

Redis实现日榜|直播间榜单|排行榜|Redis实现日榜01

前言 直播间贡献榜是一种常见的直播平台功能&#xff0c;用于展示观众在直播过程中的贡献情况。它可以根据观众的互动行为和贡献值进行排名&#xff0c;并实时更新&#xff0c;以鼓励观众积极参与直播活动。 在直播间贡献榜中&#xff0c;每个观众都有一个对应的贡献值&#…

这样使用云渲染又快又省钱

我们都知道使用云渲染是要钱的&#xff0c;而且渲染的时间越久&#xff0c;需要的渲染费越多&#xff0c;哪么如何又快又省钱的拿到效果图呢&#xff1f;用炫云的渲染质量&#xff0c;保准让你使用云渲染渲染效果图又快又省钱。 我们使用炫云的时候&#xff0c;根据自己的需求…

输入框获取焦点

Entry Component struct Test {build() {Row() {Column({ space: 5 }) {//Text("自定义样式").customStyles(20,Color.Yellow).backgroundColor("#36D").padding(10).borderRadius(30)TextInput({placeholder: "获取焦点"}).borderColor(Color.Y…

LLM微调(四)| 微调Llama 2实现Text-to-SQL,并使用LlamaIndex在数据库上进行推理

Llama 2是开源LLM发展的一个巨大里程碑。最大模型及其经过微调的变体位居Hugging Face Open LLM排行榜&#xff08;https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard&#xff09;前列。多个基准测试表明&#xff0c;就性能而言&#xff0c;它正在接近GPT-3.5…

图灵日记之java奇妙历险记--数据类型与变量运算符

目录 数据类型与变量字面常量数据类型变量语法格式整型变量浮点型变量字符型变量希尔型变量类型转换自动类型转换(隐式)强制类型转换(显式) 类型提升不同数据类型的运算小于4字节数据类型的运算 字符串类型 运算符算术运算符关系运算符逻辑运算符逻辑与&&逻辑或||逻辑非…

案例125:基于微信小程序的个人健康数据管理系统的设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

【论文阅读】FreeU: Free Lunch in Diffusion U-Net

FreeU: 无需训练直接提升扩散模型生成效果。 paper&#xff1a;https://arxiv.org/abs/2309.11497 code&#xff1a;GitHub - ChenyangSi/FreeU: FreeU: Free Lunch in Diffusion U-Net 1. 介绍 贡献&#xff1a; •研究并揭示了U-Net架构在扩散模型中去噪的潜力&#xff0…

目标检测入门体验,技术选型,加载数据集、构建机器学习模型、训练并评估

Hi, I’m Shendi 1、目标检测入门体验&#xff0c;技术选型&#xff0c;加载数据集、构建机器学习模型、训练并评估 在最近有了个物体识别的需求&#xff0c;于是开始学习 在一番比较与询问后&#xff0c;最终选择 TensorFlow。 对于编程语言&#xff0c;我比较偏向Java或nod…

vue关闭当前路由页面并跳转到其父页面

1.dom中添加关闭或取消按钮 <el-button type"primary" class"blueLinearbg cancelBtn" click"cancel" >取 消</el-button>2.cancel方法中 /*取消或关闭*/cancel(){this.$store.dispatch("tagsView/delView", this.$route)…

state的保留与重置

让组件状态保留的情况&#xff1a; 让组件状态重置的3种情况&#xff1a;

[NISACTF 2022]easyssrf

[NISACTF 2022]easyssrf wp ssrf 的题目&#xff0c;提示了会使用 curl 连接输入的网站并返回响应包。 测试连接百度 直接在输入框中写 www.baidu.com 是无法连接的&#xff0c;需要在前面加入 http 或者 https &#xff0c;因为 curl 的使用方式就是&#xff1a; curl htt…

从0到1部署gitlab自动打包部署项目

本文重点在于配置ci/cd打包 使用的是docker desktop 第一步安装docker desktop Docker简介 Docker 就像一个盒子&#xff0c;里面可以装很多物件&#xff0c;如果需要某些物件&#xff0c;可以直接将该盒子拿走&#xff0c;而不需要从该盒子中一件一件的取。Docker中文社区、…

Ubuntu 常用命令之 man 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 man命令在Ubuntu系统中是一个非常重要的命令&#xff0c;它用于查看系统的手册页。手册页是Linux和Unix系统中的一种在线文档&#xff0c;用于描述系统中的命令、函数、配置文件等的详细信息。 man命令的基本格式是 man [选项] …

光伏企业如何能够提高光伏电站的建设效率?

随着全球对可再生能源需求的日益增长&#xff0c;光伏行业的发展势头强劲。然而&#xff0c;光伏电站建设过程中往往存在效率低下的问题&#xff0c;这不仅影响了电站的运营成本&#xff0c;也制约了整个行业的发展速度。因此&#xff0c;如何提高光伏电站的建设效率&#xff0…

DshanMCU-R128s2 Hello World!

本文将介绍使用 R128 开发板从串口输出 Hello World 的方式介绍 SDK 软件开发流程。 载入方案 我们使用的开发板是 R128-Devkit&#xff0c;需要开发 C906 核心的应用程序&#xff0c;所以载入方案选择r128s2_module_c906 $ source envsetup.sh $ lunch_rtos 1编辑程序 打…

Windows 系统下本地单机搭建 Redis(一主二从三哨兵)

目录 一、Redis环境准备&#xff1a; 1、下载redis 2、Windows下的.msi安装和.zip格式区别&#xff1a; 二、哨兵介绍&#xff1a; 1、一主二从三哨兵理论图&#xff1a; 2.哨兵的主要功能&#xff1a; 3.哨兵用于实现 redis 集群的高可用&#xff0c;本身也是分布式的&…

锐捷配置PVLAN

一、实验拓扑 二、实验目的 PVLAN可以通过主VLAN和辅助VLAN的概念&#xff0c;部署隔离技术&#xff0c;实现用户间的互访控制。 三、实验配置 SW2 Ruijie >enable Ruijie #configure terminal Ruijie (config)#vlan 20 Ruijie (config-vlan)#private-vlan community …