IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android脱壳之整体脱壳原理与实践,完整版开放下载 -> 正文阅读

[移动开发]Android脱壳之整体脱壳原理与实践,完整版开放下载


五. 通用整体脱壳方法:

从上面的内容可以得知,加载Dex有很多种实现方式,每种壳的实现都不一定相同,如果想实现一个比较通用的整体脱壳方法,就必须寻找一个壳绕不开的方式来实现。这个关键的数据结构就是art虚拟机中定义的类art::DexFile,它的定义位于art/runtime/dex_file.h文件中。
这个类有两个成员变量,分别代表着dex文件加载到内存当中的起始地址以及大小,得到这两个信息以后通过内存dump的方式就可以轻轻松松的将dex文件dump下来。
这就表示一个事实: 在某个时机点内存当中一定会有解密后的完整dex存在。

2021-05-14_17-26.png

art::DexFile是加载dex绕不开的类,不论是使用BaseClassLoader,还是自定义类加载器,最终都需要art::DexFile,这一结论可以通过查阅class_linker.cc文件中的函数得到,像ClassLinker::DefineClassClassLinker::LoadMethodClassLinker::LoadClassMembers这些重量级函数都需要以DexFile对象做为参数:

2021-05-14_17-34.png

2021-05-14_17-34_1.png

2021-05-14_17-34.png

通过BaseDexClassLoader来加载类会执行art/runtime/native/dalvik_system_DexFile.ccDexFile_defineClassNative函数,这个函数的实现就是遍历Java层的DexFilemCookie对象所表示的native层的art::DexFile,因为这个art::DexFile对象代表了dex文件在内存中的结构,所以可以通过类名在art::DexFile列表中查找到DexFile::ClassDef结构,才能继续调用ClassLinker::DefineClass()函数,最终才能得到一个虚拟机实现中的Class对象表示mirror::Class*,因此这个流程仍然离不开虚拟机中的art::DexFile类。

2021-05-14_17-46.png

这还会引发出一个问题:
art虚拟机执行smail指令可以解释执行,这种模式叫Interpreter模式(很显然解释模式下需要dex文件存在于内存当中才能解释执行)
也可以执行oat以后elf文件中的本地机器指令,这种模式叫quick code模式。
在quick code模式中,dex编译出来的机器指令包含在dex2oat生成出来的oat文件中(对于app来说,oat文件以.odex结尾),那么对于quick code模式是否还需要dex文件呢?如果不需要dex文件,是不是就无法在内存中dump出dex文件了?

事实上quick code模式内存中也是会有原始的dex文件存在的,具体的执行逻辑在Runtime的GetOatFileManager().OpenDexFilesFromOat()函数中,在这个函数中如果走dex2oat流程的话就会执行oat_file_assistant.MakeUpToDate()调用dex2oat命令生成odex和vdex文件,并生成OatFile类的对象,这个类就代表着dex2oat以后oat文件在内存中的映射表示,oat文件其实就是可执行文件,只不过它有特殊的几个符号:oatdata,oatlastword,oatbss,oatbsslastword等,OatFile类中的成员变量oat_dex_files_storage_是个std::vector<const OatDexFile*>类型的变量,而OatDexFile类表示的是这个oat文件所对应的dex文件的信息(列表),通过OatDexFiledex_file_pointer_即可以找到对应的art::DexFile对象的地址。
在android8.0以后,oat文件存放着dex文件编译出来的可执行指令,而原始的dex内容其实存放在vdex文件中,这一点可以在后面的dump程序中看到。
在quick code模式下也需要原始dex的存在的原因是dex文件还存放着类相关的信息,如class_def_item,method_id_item,对于类方法的执行还离不开这些信息。

六. 整体脱壳实践:

既然知道虚拟机中的art::DexFile类是dex在内存中的表示,那么得到这个对象就可以dump出dex文件。
接下来就是寻找一个合适的点可以得到art::DexFile对象,得到对象以后通过hook的方式或者修改源码的方式都可以dump下来了。
下面是脱函数抽取型壳fart的作者寒冰大佬所提出的办法:
找到libart.so文件中所有导出函数中带有art::DexFile参数或者返回值的函数,那么这就是一个可以脱壳的点
我写了一个命令可以查找满足这种条件的函数:

arm64-readelf -s libart.so -W | tr -s ' ' | cut -f9 -d ' '| c++filt  | grep "art::DexFile" 

比如在art/runtime/dex_file.ccDexFile::OpenCommon或者DexFile::DexFile中添加如下代码即可脱壳:

 pid_t pid = getpid();
? char dexfilepath[100] = {0};
? sprintf(dexfilepath,"/sdcard/drdump_%d_%d_DexFile.dex",(int)size,(int) pid);
? int fd = open(dexfilepath, O_CREAT | O_RDWR , 666);
? if (fd > 0){
? ? int number = write(fd,base,size);
? ? if(number > 0){
? ? }
? ? close(fd);
? } 

这种是修改源码的方式来脱壳。


还有一种方式是通过frida hook来脱壳,它的优点是简单有效,不需要重新编译rom。
它的原理如下:
对于通过使用BaseDexClassLoader来加载的程序来说,DexFile.java类的mCookie变量在native层的表现其实是一个jlong类型的指针的数组,数组的个数为此ClassLoader加载的dex文件个数 + 1,第一个元素类型为OatFile*,剩余的元素为对应的art::DexFile*,因此可以通过获取mCookie变量来得到art::DexFile*列表,并且通过art::DexFile的begin_和size_来dump。
代码如下:

function hasOwnProperty(obj, name) {
    try {
        return obj.hasOwnProperty(name) || name in obj;
    } catch (e) {
        return obj.hasOwnProperty(name)
    }
}

function getHandle(object) {
    var result = null;
    if (hasOwnProperty(object, "$handle")) {
        result = object.$handle;
        if (result) {
            return result;
        }
    }
    if (hasOwnProperty(object, "$h")) {
        return object.$h;
    }

    return null;
}

function dump_dex(packagename, dexfilebegin, dexfilesize) {
    var dexfile_path = "/sdcard/my_frida_dump_" + packagename + "_" + dexfilesize + ".dex";
    var dexfile_handle = new File(dexfile_path, "w");
    if (dexfile_handle && dexfile_handle != null) {
        var dex_buffer = ptr(dexfilebegin).readByteArray(dexfilesize);
        dexfile_handle.write(dex_buffer);
        dexfile_handle.flush();
        dexfile_handle.close();
    }
}


function dealwithClassLoader(classloaderobj, packagename) {
    if (Java.available) {
        Java.perform(function () {
            try {
                var dexfileclass = Java.use("dalvik.system.DexFile");
                var BaseDexClassLoaderclass = Java.use("dalvik.system.BaseDexClassLoader");
                var DexPathListclass = Java.use("dalvik.system.DexPathList");
                var Elementclass = Java.use("dalvik.system.DexPathList$Element");
                var basedexclassloaderobj = Java.cast(classloaderobj, BaseDexClassLoaderclass);
                var tmpobj = basedexclassloaderobj.pathList.value;
                var pathlistobj = Java.cast(tmpobj, DexPathListclass);
                console.log("pathlistobj->" + pathlistobj);
                var dexElementsobj = pathlistobj.dexElements.value;
                console.log("dexElementsobj->" + dexElementsobj);
                for (var i in dexElementsobj) {
                    var obj = dexElementsobj[i];
                    var elementobj = Java.cast(obj, Elementclass);
                    console.log("elementobj->" + elementobj);
                    tmpobj = elementobj.dexFile.value;
                    var dexfileobj = Java.cast(tmpobj, dexfileclass);
                    var mCookie = dexfileobj.mInternalCookie.value;
                    var mInternalCookie = dexfileobj.mInternalCookie.value;
                    if (mCookie != null) {
                        var jnienv = Java.vm.tryGetEnv();
                        var cookiePtr = getHandle(mCookie);
                        var arrayLength = jnienv.getArrayLength(cookiePtr);
                        var long_data = jnienv.getLongArrayElements(cookiePtr, 0);
                        console.log("arrayLength:" + arrayLength + ",long_data:" + long_data);
                        for (var i = 1; i < arrayLength; i++) {
                            var dexfileptr = Memory.readPointer(ptr(long_data).add(8 * i));
                            var dexfilebegin = Memory.readPointer(ptr(dexfileptr).add(Process.pointerSize * 1));
                            var dexfilesize = Memory.readU32(ptr(dexfileptr).add(Process.pointerSize * 2));
                            console.log("pointer:" + dexfileptr + ",dexfilebegin:" + dexfilebegin + ",dexfilesize:" + dexfilesize);
                            dump_dex(packagename, dexfilebegin, dexfilesize);
                        }
                    }
                }
            } catch (e) {
                console.log(e);
            }

        });
    }


}

function tuoke(packagename) {
    if (Java.available) {
        Java.perform(function () {
            console.log("go into enumerateClassLoaders!");
            Java.enumerateClassLoadersSync().forEach(function (loader) {
                if (loader.toString().indexOf("BootClassLoader") >= 0) {
                    console.log("this is a BootClassLoader!")
                } else {
                    try {
                        console.log("classloader : " + loader);
                        dealwithClassLoader(loader, packagename);
                    } catch (e) {
                        console.log(e);
                    }
                }
            })
        });
### Android进阶资料

以下的资料是近年来,我和一些朋友面试收集整理了很多大厂的面试真题和资料,还有来自如阿里、小米、爱奇艺等一线大厂的大牛整理的架构进阶资料。在这里分享出来,希望可以帮助到大家。

**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**

**Android进阶核心笔记**

![](https://img-blog.csdnimg.cn/img_convert/dc74fd8dca1239002a68956f46435341.png)



**百万年薪必刷面试题**

![](https://img-blog.csdnimg.cn/img_convert/d2a94640e09cb96847925da9927de506.png)


**最全Android进阶学习视频**
.net/m0_60958482/android_p7)**

**Android进阶核心笔记**

[外链图片转存中...(img-6ZMJ5E12-1630836328374)]



**百万年薪必刷面试题**

[外链图片转存中...(img-4r0F8FcC-1630836328375)]


**最全Android进阶学习视频**
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-06 11:16:16  更:2021-09-06 11:17:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:57:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码