首页 > 系统相关 >逆向内存加载Dex(动态加载class)

逆向内存加载Dex(动态加载class)

时间:2024-07-08 15:43:46浏览次数:17  
标签:Dex dex ids range base uint 加载 class size

逆向一个app, 其核心算法是通过反射调用的, 反编译软件中无法找到该类, 并且也无法hook.

Java.perform(function(){
        Java.enumerateClassLoaders({
           onMatch:function(loader){
			try{
				if(loader.loadClass("com.xxxxx")){
					console.log("================="+loader);
					Java.classFactory.loader=loader;
					var app=Java.use("com.xxxxx");
					console.log(app);

					// app.sayHello.implementation=function(){
					// 	return "bye";
					// }
				}else{
					console.log("NOT HAS CLASS");
				}
				}catch(error){

				}
			},
			onComplete:function(){

			}
        });
    });

Java.enumerateClassLoaders使用枚举classloader, 可以找到该类使用的classloader是
InMemoryDexClassLoader动态加载的,如何逆向呢?

dalvik.system.InMemoryDexClassLoader[DexPathList[[dex file "InMemoryDexFile[cookie=[473962364016, 473425978352]]"],nativeLibraryDirectories=[/data/app/~~ZljW8Ol47mfWsrqYu4qjCQ==/xyz.be.customer-oh7iq9v-8TFeIWPLBt2Pow==/lib/arm64, /data/app/~~ZljW8Ol47mfWsrqYu4qjCQ==/xyz.be.customer-oh7iq9v-8TFeIWPLBt2Pow==/base.apk!/lib/arm64-v8a, /system/lib64, /system_ext/lib64, /product/lib64]]]

学习下动态加载dex 加固(正向)

https://blog.csdn.net/zzx410527/article/details/51673908

方法一: 使用objection wallbreak

plugin wallbreaker classsearch com.xxx.internal.xxxx$1101
这里有个bug 就是有$的 他无法dump需要处理下
wallbreak的原理是通过调用Java.enumerateLoadedClassesSync()
来枚举所有已经加载的Java 类, 然后再通过java反射获取class的属性\方法\等整体结构, 但是无法获得方法的内容.
我们来试试

Java.perform(function() {
	var packagename = "com.appsflyer.internal";
	console.log("\n[*] enumerating classes...");
	Java.enumerateLoadedClasses({
		onMatch: function(_className) {
			if (_className.startsWith(packagename)) {
				console.log("[*] found class: " + _className);
			}
		},
		onComplete: function() {
			console.log("[*] class enuemration complete");
		}
	});
});

frida -U -f xxxx -l _fcagent.js
注意hook时机, 使用延时调用setImmediate(myjs.myjs, 10000);
ok, 成功捕获到改包名下的所有class文件
然后, 再通过反射来获取
获取类的基本信息java.lang.Class
获取类的实例java.lang.Class和java.lang.reflect.Constructor
操作实例的属性java.lang.reflect.Field
调用实例的方法java.lang.reflect.Method

但是他并不能获取到真正的方法内容, 所以只适合用来查看类的结构, 还有用他的objectdump打印object的值

方法二:可以通过遍历内存中的dex文件特征来dump 所有dex

了解下dex 头文件

struct header_item {
    uchar[8] magic;          // 魔数,用于标识文件类型
    uint checksum;           // Alder32 校验和,用于验证文件完整性
    uchar[20] signature;     // 文件剩余部分的 SHA-1 签名
    uint file_size;          // 文件总大小,以字节为单位
    uint header_size;        // 头部大小,以字节为单位
    uint endian_tag;         // 字节序标记,标识文件的字节序
    uint link_size;          // 链接部分的大小
    uint link_off;           // 链接部分的文件偏移量
    uint map_off;            // map 列表的文件偏移量
    uint string_ids_size;    // 字符串 ID 列表中的字符串数量
    uint string_ids_off;     // 字符串 ID 列表的文件偏移量
    uint type_ids_size;      // 类型 ID 列表中的类型数量
    uint type_ids_off;       // 类型 ID 列表的文件偏移量
    uint proto_ids_size;     // 方法原型 ID 列表中的项数
    uint proto_ids_off;      // 方法原型 ID 列表的文件偏移量
    uint field_ids_size;     // 字段 ID 列表中的字段数量
    uint field_ids_off;      // 字段 ID 列表的文件偏移量
    uint method_ids_size;    // 方法 ID 列表中的方法数量
    uint method_ids_off;     // 方法 ID 列表的文件偏移量
    uint class_defs_size;    // 类定义列表中的类数量
    uint class_defs_off;     // 类定义列表的文件偏移量
    uint data_size;          // 数据部分的大小,以字节为单位
    uint data_off;           // 数据部分的文件偏移量
};
字段详细解释:
magic

类型: uchar[8]
描述: 魔数,通常为 "dex\n035\0" 或 "dex\n036\0",用于标识文件类型。
checksum

类型: uint
格式: 十六进制
描述: Alder32 校验和,用于验证文件除校验和字段外的其余部分的完整性。
signature

类型: uchar[20]
描述: 文件剩余部分的 SHA-1 签名,用于验证文件内容的完整性。
file_size

类型: uint
描述: 文件总大小,以字节为单位。
header_size

类型: uint
描述: 头部大小,以字节为单位。通常是 0x70 (112)。
endian_tag

类型: uint
格式: 十六进制
描述: 字节序标记,用于标识文件的字节序,通常为 0x12345678 (小端) 或 0x78563412 (大端)。
link_size

类型: uint
描述: 链接部分的大小。
link_off

类型: uint
描述: 链接部分的文件偏移量。
map_off

类型: uint
描述: map 列表的文件偏移量。
string_ids_size

类型: uint
描述: 字符串 ID 列表中的字符串数量。
string_ids_off

类型: uint
描述: 字符串 ID 列表的文件偏移量。
type_ids_size

类型: uint
描述: 类型 ID 列表中的类型数量。
type_ids_off

类型: uint
描述: 类型 ID 列表的文件偏移量。
proto_ids_size

类型: uint
描述: 方法原型 ID 列表中的项数。
proto_ids_off

类型: uint
描述: 方法原型 ID 列表的文件偏移量。
field_ids_size

类型: uint
描述: 字段 ID 列表中的字段数量。
field_ids_off

类型: uint
描述: 字段 ID 列表的文件偏移量。
method_ids_size

类型: uint
描述: 方法 ID 列表中的方法数量。
method_ids_off

类型: uint
描述: 方法 ID 列表的文件偏移量。
class_defs_size

类型: uint
描述: 类定义列表中的类数量。
class_defs_off

类型: uint
描述: 类定义列表的文件偏移量。
data_size

类型: uint
描述: 数据部分的大小,以字节为单位。
data_off

类型: uint
描述: 数据部分的文件偏移量。

核心思路(参考frida dexdump):

 Process.enumerateRanges('r--').forEach(function (range: RangeDetails) {
        try {
            Memory.scanSync(range.base, range.size, "64 65 78 0a 30 ?? ?? 00").forEach(function (match) {

                //判断文件路径包含"/data/dalvik-cache/ 和system 则跳过
                if (range.file && range.file.path
                    && (range.file.path.startsWith("/data/dalvik-cache/") ||
                        range.file.path.startsWith("/system/"))) {
                    return;
                }
                //验证dex文件
                if (verify(match.address, range, false)) {
                    const dex_size = get_dex_real_size(match.address, range.base, range.base.add(range.size));
                    result.push({
                        "addr": match.address,
                        "size": dex_size
                    });

                    const max_size = range.size - match.address.sub(range.base).toInt32();
                    if (deepSearch && max_size != dex_size) {
                        result.push({
                            "addr": match.address,
                            "size": max_size
                        });
                    }
                }
            });

            if (deepSearch) {
                Memory.scanSync(range.base, range.size, "70 00 00 00").forEach(function (match) {
                    const dex_base = match.address.sub(0x3C);
                    if (dex_base < range.base) {
                        return;
                    }
                    if (dex_base.readCString(4) != "dex\n" && verify(dex_base, range, true)) {
                        const 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
                        });
                        const max_size = range.size - dex_base.sub(range.base).toInt32();
                        if (max_size != real_dex_size) {
                            result.push({
                                "addr": dex_base,
                                "size": max_size
                            });
                        }
                    }
                })
            } else {
                if (range.base.readCString(4) != "dex\n" && verify(range.base, range, true)) {
                    const real_dex_size = get_dex_real_size(range.base, range.base, range.base.add(range.size));
                    result.push({
                        "addr": range.base,
                        "size": real_dex_size
                    });
                }
            }

        } catch (e) {
        }
    });
Process.enumerateRanges('r--').forEach(function (range: RangeDetails) {

遍历所有具有只读权限的内存范围

Memory.scanSync(range.base, range.size, "64 65 78 0a 30 ?? ?? 00").forEach(function (match) {

扫描 DEX 文件头的标识 "dex\n035\0" 或 "dex\n036\0" 为基础进行扫描。匹配的模式为 "64 65 78 0a 30 ?? ?? 00",其中 64 65 78 0a 是 "dex\n",30 ?? ?? 00 是版本号和一个字节的结尾。

具体可参考:https://mp.weixin.qq.com/s/n2XHGhshTmvt2FhxyFfoMA

标签:Dex,dex,ids,range,base,uint,加载,class,size
From: https://www.cnblogs.com/linuxxx/p/18289702

相关文章

  • Java工程中读取resources目录下properties文件的方式,从上图可知,当工程部署在服务器下
    Java工程中读取resources目录下properties文件的方式,从上图可知,当工程部署在服务器下时,配置文件以及代码都是在对应的classes文件夹下二、具体读取方法1、当需要读取当前路径下的properties文件时,即在本地没有部署到具体服务器上的情况:Filefile=newFile(“src/main/re......
  • WPF ComboBox数据绑定:初始化动态加载ItemsSource后首次赋值Text不显示问题解决
    原来:<ComboBoxText="{BindingItem}"ItemsSource="{BindingItemLists}"></ComboBox>privatevoidParas_Init(){ItemLists=newObservableCollection<string>();ItemLists.Add("111......
  • @ConditionalOnClass注解解析
    文章目录概要Bean注册过程@ConditionalOnClass注解总结概要springboot中各种@ConditionalXxx注解控制着Bean是否注册,只有满足了一定条件才会被注册到容器中。这些注解包含@ConditionalOnClass、@OnBeanCondition、@ConditionalOnProperty等等,这篇文章就和大家探究下......
  • el-tree懒加载获取所有已经选的节点
    用于el-tree懒加载一个图层目录,勾选父级目录,需要得到该父级目录下所有叶子节点数据<el-tree:data="directoryDataLayer"show-checkboxnode-key="id":load="directoryDataLoad"......
  • nuxt3启动报错The requested module 'file://C:/Users/acer/node_modules/ufo/dist/in
    背景进行某些配置后,启动nuxt3的测试服务器报错。解决下面这个目录并不是npm的全局目录C:/Users/acer/node_modules我的解决办法是直接使用npm或者cnpm全局安装ufo这个包,然后把这个包移到该位置。#npm全局安装路径C:\Users\%USERNAME%\AppData\Roaming\npm\node_modules......
  • World of Warcraft [CLASSIC] Talent Tree
    WorldofWarcraft[CLASSIC] TalentTree 天赋树模拟器01)初始化整个页面,选择游戏职业,初始化3个天赋树02)初始化天赋树结构,层次为N层03)每层有4个技能,设置可显示,设置隐藏04)每个技能可配置图表,技能名称,备注说明,每一级说明不同05)每层的技能可支持最大技能点......
  • GaussDB AI新特性:gs_index_advise推荐索引
    GaussDB的AI新特性,可以把SQL文本嵌套在函数中,数据库会返回一个创建合适索引的列gs_index_advise(text)描述:针对单条查询语句推荐索引。参数:SQL语句字符串返回值类型:record  一、通常的SQL优化会通过参数调优的方式进行调整,例如如下参数setenable_fast_query_shippi......
  • Exploring Large Language Models and Hierarchical Frameworks for Classification o
    本文是LLM系列文章,针对《ExploringLargeLanguageModelsandHierarchicalFrameworksforClassificationofLargeUnstructuredLegalDocuments》的翻译。探索大型非结构化法律文件分类的大型语言模型和层次框架摘要1引言2相关工作3方法:分类框架(MESc)4结......
  • VUE0002:pnpm无法加载,解除系统禁止运行脚本
    1,pnpm:无法加载文件C:\Users\xxxxx\AppData\Roaming\npm\pnpm.ps1,因为在此系统上禁止运行脚本。2,解决方案地址:https://blog.csdn.net/jwbabc/article/details/139505236vscode中执行pnpminstall的时候,直接报了上面的错误。解决: 然后输入:set-ExecutionPolicyRemoteSign......
  • 单例模式之延迟加载和初始加载
    一、什么是初始加载?实现单例模式有两种方式,一种是懒加载,也就是延迟加载,当首次调用时创建单例对象,另一种是初始加载,在应用程序启动时就初始化单例对象,并将其保存在内存中以备将来使用,而不是需要时才创建。初始加载不需要考虑多线程环境导致的线程不安全问题,因为CLR将负责对象初始......