【胖虎的逆向之路】04——脱壳(一代壳)原理脱壳相关概念详解

news2024/11/17 9:52:47

【胖虎的逆向之路】04——脱壳(一代壳)原理&脱壳相关概念详解

【胖虎的逆向之路】01——动态加载和类加载机制详解
【胖虎的逆向之路】02——Android整体加壳原理详解&实现
【胖虎的逆向之路】03——Android一代壳脱壳办法&实操


文章目录

  • 【胖虎的逆向之路】04——脱壳(一代壳)原理&脱壳相关概念详解
  • 前言
  • 一、Dex加载流程
  • 二、Dex2Oat编译流程
  • 三、类加载流程
  • 四、DexFile详解
    • (1)直接查找法
    • (2)间接查找法
  • 五、ArtMethod详解
  • 总结
  • 参考文献


前言

提示:这里可以添加本文要记录的大概内容:

在上文中,我们讲解了关于Android脱壳的基本办法和实际操作,现在我们来针对脱壳(一代壳)的原理和脱壳相关的基础知识介绍,由于作者能力有限,会尽力的详细描述 一代壳脱壳 的流程及原理,如本文中有任何错误,烦请指正,感谢~


一、Dex加载流程

在日常分析脱壳点过程中,Dex加载的基本流程也是要明白熟悉的
在这里插入图片描述

DexPathList:该类主要用来查找Dex、SO库的路径,并这些路径整体呈一个数组
Element:根据多路径的分隔符“;”将dexPath转换成File列表,记录所有的dexFile
DexFile:用来描述Dex文件,Dex的加载以及Class的查找都是由该类调用它的native方法完成的

我们依次来分析这个过程中的源码

DexPathList

/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
public DexPathList(ClassLoader definingContext, String dexPath,
            String librarySearchPath, File optimizedDirectory) {
**********************      
   this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                         suppressedExceptions, definingContext);    
**********************  
            }

makeDexElements

private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
          List<IOException> suppressedExceptions, ClassLoader loader) {
**********************            
       DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements);    
**********************         
          }

loadDexFile

private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader,
                                       Element[] elements)
            throws IOException {
        if (optimizedDirectory == null) {
            return new DexFile(file, loader, elements);
        } else {
           String optimizedPath = optimizedPathFor(file, optimizedDirectory);
            return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
        }
    }

loadDex

static DexFile loadDex(String sourcePathName, String outputPathName,
      int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
      return new DexFile(sourcePathName, outputPathName, flags, loader, elements);
  }

DexFile

/libcore/dalvik/src/main/java/dalvik/system/DexFile.java
DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
        mCookie = openDexFile(fileName, null, 0, loader, elements);
        mInternalCookie = mCookie;
        mFileName = fileName;
        //System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
    }

这里出现的mCookie,mCookie在C/C++层中是DexFile的指针,我们在下面详细讲解

openDexFile

private static Object openDexFile(String sourceName, String outputName, int flags,
        ClassLoader loader, DexPathList.Element[] elements) throws IOException {
       // Use absolute paths to enable the use of relative paths when testing on host.
        return openDexFileNative(new File(sourceName).getAbsolutePath(),
                                 (outputName == null)
                                    ? null
                                   : new File(outputName).getAbsolutePath(),
                               	   flags,
                                   loader,
                                   elements);
    }

这里就进入了C/C++层

openDexFileNative

在这里插入图片描述

为了节约篇幅,我们快速分析,中间再经过一些函数

OpenDexFilesFromOat()
MakeUpToDate()
GenerateOatFileNoChecks()
Dex2Oat()

最后进入了Dex2Oat,这就进入了Dex2Oat的编译流程

反之如果我们在下面Dex2Oat的流程中通过Hook相关方法或execv或execve导致dex2oat失败,我们就会返回到OpenDexFilesFromOat

OpenDexFilesFromOat

在这里插入图片描述

会先在HasOriginalDexFiles里尝试加载我们的Dex,也就是说,倘若我们的壳阻断了dex2oat的编译流程,然后又调用了DexFile的Open函数。

DexFile::Open

在这里插入图片描述

校验dex的魔术字字段,然后调用DexFile::OpenFile

DexFile::OpenFile

/art/runtime/dex_file.cc
std::unique_ptr<const DexFile> DexFile::OpenFile(int fd,
                                                const std::string& location,
                                                bool verify,
                                                bool verify_checksum,
                                                std::string* error_msg) {
 **************************************
 std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
                                                map->Size(),
                                                location,
                                                dex_header->checksum_,
                                                kNoOatDexFile,
                                                verify,
                                                verify_checksum,
                                                error_msg);   
  **************************************
                                                
                                                }

OpenCommon
在这里插入图片描述

最后又再次回到DexFile类,这里我们的dex文件加载基本流程分析完毕

二、Dex2Oat编译流程

Dex2oat是google公司为了提高编译效率的一种机制,从Android8.0开始实施,一些加壳厂商实现抽取壳往往会禁用Dex2oat,而针对整体加壳没有禁用的Dex2Oat也成为了脱壳点

在这里插入图片描述
Exec

/art/runtime/exec_utils.cc
bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) {
  int status = ExecAndReturnCode(arg_vector, error_msg);
  if (status != 0) {
    const std::string command_line(android::base::Join(arg_vector, ' '));
    *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
                              command_line.c_str());
    return false;
  }
  return true;
}

ExecAndReturnCode

在这里插入图片描述

而我们就可以通过Hook execv或execve来禁用Dex2Oat,而如果我们不禁用dex2oat,execve函数是用来调用dex2oat的二进制程序实现对dex文件的加载,我们这时候找到dex2oat.cc这个文件,找到main函数

/art/dex2oat/dex2oat.cc
 int main(int argc, char** argv) {
  int result = static_cast<int>(art::Dex2oat(argc, argv));
  if (!art::kIsDebugBuild && (RUNNING_ON_MEMORY_TOOL == 0)) {
    _exit(result);
  }
  return result;

这里我们调用了Dex2oat

Dex2Oat

/art/dex2oat/dex2oat.cc
static dex2oat::ReturnCode Dex2oat(int argc, char** argv) {
   **************************************
   dex2oat::ReturnCode setup_code = dex2oat->Setup();
    dex2oat::ReturnCode result;
  if (dex2oat->IsImage()) {
    result = CompileImage(*dex2oat);
  } else {
    result = CompileApp(*dex2oat);
 }
   **************************************
}

Dex2oat中会对dex文件进行逐个类逐个函数的编译,setup()函数完成对dex的加载

然后顺序执行,就会进入CompileApp

编译过程中会按照逐个函数进行编译,就会进入CompileMethod

在这里插入图片描述

到这里Dex2oat的基本流程就分析完毕

三、类加载流程

要理解DexFile为什么如此重要,首先我们要清除Android APP的类加载流程。Android的类加载一般分为两类隐式加载和显式加载

1.隐式加载:
    (1)创建类的实例,也就是new一个对象
    (2)访问某个类或接口的静态变量,或者对该静态变量赋值
    (3)调用类的静态方法
    (4)反射Class.forName("android.app.ActivityThread")
    (5)初始化一个类的子类(会首先初始化子类的父类)
2.显示加载:
    (1)使用LoadClass()加载
    (2)使用forName()加载

我们详细看一下显示加载:

Class.forName 和 ClassLoader.loadClass加载有何不同:
(1ClassLoader.loadClass也能加载一个类,但是不会触发类的初始化(也就是说不会对类的静态变量,静态代码块进行初始化操作)2Class.forName这种方式,不但会加载一个类,还会触发类的初始化阶段,也能够为这个类的静态变量,静态代码块进行初始化操作

我们在详细来看一下在类加载过程中的流程:

java层

在这里插入图片描述

我们可以发现类加载中关键的DexFile,该类用来描述Dex文件,所以我们的脱壳对象就是DexFile

这里从DexFile进入Native层中,还有一个关键的字段就是mCookie

在这里插入图片描述
后面我们详细的介绍mCookie的作用

我们进一步分析,进入Native层

Native层

/art/runtime/native/[dalvik_system_DexFile.cc

在这里插入图片描述

ConvertJavaArrayToDexFiles对cookie进行了处理

在这里插入图片描述
通过这里的分析,我们可以知道mCooike转换为C/C++层指针后,就是dexfile的索引

我们继续分析DefineClass

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) {
***************
LoadClass(self, *new_dex_file, *new_class_def, klass);
***************
}

LoadClass

art/runtime/class_linker.cc
void ClassLinker::LoadClass(Thread* self,
3120                            const DexFile& dex_file,
3121                            const DexFile::ClassDef& dex_class_def,
3122                            Handle<mirror::Class> klass) {
3123  const uint8_t* class_data = dex_file.GetClassData(dex_class_def);
3124  if (class_data == nullptr) {
3125    return;  // no fields or methods - for example a marker interface
3126  }
3127  LoadClassMembers(self, dex_file, class_data, klass);
3128}

LoadClassMembers

art/runtime/class_linker.cc
void ClassLinker::LoadClassMembers(Thread* self,
                                   const DexFile& dex_file,
                                   const uint8_t* class_data,
                                   Handle<mirror::Class> klass) {
***************
      LoadMethod(dex_file, it, klass, method);
      LinkCode(this, method, oat_class_ptr, class_def_method_index);
***************
}

LoadMethod

art/runtime/class_linker.cc
void ClassLinker::LoadMethod(const DexFile& dex_file,
                           const ClassDataItemIterator& it,
                            Handle<mirror::Class> klass,
                             ArtMethod* dst) {
}

LinkCode

在这里插入图片描述

我们可以发现这里就进入了从linkcode后就进入了解释器中,并对是否进行dex2oat进行了判断,我们直接进入解释器中继续分析

我们知道Art解释器分为两种:解释模式下和quick模式下,而我们又知道Android8.0开始进行dex2oat

如果壳没有禁用dex2oat,那类中的初始化函数运行在解释器模式下 
如果壳禁用dex2oat,dex文件中的所有函数都运行在解释器模式下
则类的初始化函数运行在解释器模式下

所以一般的加壳厂商会禁用掉dex2oat,这样可以是所有的函数都运行在解释模式下,所以一些脱壳点选在dex2oat流程中,可能针对禁用dex2oat的情况并不使用,我们这里主要针对整体加壳,就不展开讲述,最后我们得知解释器中会运行在Execute下

Execute

art/runtime/interpreter/interpreter.cc
static inline JValue Execute(
    Thread* self,
    const DexFile::CodeItem* code_item,
    ShadowFrame& shadow_frame,
    JValue result_register,
    bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_){

***************
      ArtMethod *method = shadow_frame.GetMethod();
***************
    
    }

这里我们大致分析完成了类加载的思路

四、DexFile详解

前面我们分析了很多,对dex加载、类加载等都已经有了一个很详细的了解,而最终一切的核心就是DexFile,DexFile就是我们脱壳所关注的重点,寒冰大佬在拨云见日:安卓APP脱壳的本质以及如何快速发现ART下的脱壳点中提到,在ART下只要获得了DexFile对象,那么我们就可以得到该dex文件在内存中的起始地址和大小,进而完成脱壳。

我们先查看一些DexFile的结构体

在这里插入图片描述
只要我们能获得起始地址begin和大小size,就可以成功的将dex文件脱取下来,这里我们记得DexFile含有虚函数表,所以根据C++布局,要偏移一个指针
在这里插入图片描述
而DexFile类还给我们提供了方便的API
在这里插入图片描述

这样只要我们找到函数中有DexFile对象,就可以通过调用API来进一步dump dex文件,由此按照寒冰大佬的思想,大量的脱壳点由此产生

(1)直接查找法

我们通过直接在Android源码中搜索DexFile,就可以获得海量的脱壳点

在这里插入图片描述
我们通过在IDA中搜索libart.so导出的DexFile,同样可以获得大量的脱壳点

在这里插入图片描述

(2)间接查找法

这里就是寒冰大佬在文章中提到的通过ArtMethod对象的getDexFile()获取到ArtMethod所属的DexFile对象的这种一级间接法,通过Thread的getCurrentMethod()函数首先获取到ArtMethod或者通过ShadowFrame的getMethod获取到ArtMethod对象,然后再通过getDexFile获取到ArtMethod对象所属的DexFile的二级间接法

getDexFile()
getMethod()

五、ArtMethod详解

上面我们已经详细分析了DexFile的文件结构,我们知道通过ArtMethod可以获得DexFile,那么为啥又要单独提ArtMethod呢,因为ArtMethod在抽取壳和VMP等壳中扮演了重要的角色

ArtMethod结构体
在这里插入图片描述

我们通过ArtMethod可以获得codeitem的偏移和方法索引,熟悉dex结构的朋友知道codeitem就是代码实际的值,而codeitem则再后续加壳技术扮演了至关重要的地址,而且ArtMethod还有非常丰富的方法,可以帮助大家实现很多功能,所以在脱壳工作中也是十分重要的

总结

以上就是今天要讲的内容,主要借鉴了 随风而行 大佬的一些思路及文章,将文章在复写一遍目的是为了加深记忆,在以后的学习过程中可以很方便的查看~

参考文献

https://security-kitchen.com/2022/12/04/Packer5/
https://bbs.kanxue.com/thread-254555.htm#msg_header_h2_2

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

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

相关文章

高速路如何避免ETC车辆漏计问题,ETC通道出入车辆校准看板

人群密集场所事故预防措施和应急管理方案的制定&#xff0c;对每一个交通枢纽和大型社会活动场所都显得尤为重要。对于交通管理部门来说&#xff0c;获取准确、可靠的交通数据已经变得越来越重要。 所以呢&#xff0c;ETC出入车辆校准看板是必要的。ETC出入车辆校准看板&#x…

如何使用路由策略解决 ISIS与OSPF双点双向产生的次优路径、环路隐患?

3.1.0 如何使用路由策略解决 ISIS与OSPF双点双向产生的次优路径、环路隐患 一、双点双向中的次优路径解决方法 双点双向中由于默认路由优先级造成次优路径的产生&#xff0c;而解决的方法就是修改路由优先级。 双点双向错误的解决方法 关于修改路由优先级&#xff0c;需要注…

2022 医疗卫生行业应急响应形势分析报告 脱敏板

声明 本文是学习2022医疗卫生行业网络安全分析报告. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 医疗卫生行业应急响应形势分析 2021年1-12月&#xff0c;奇安信集团安服团队共参与和处置全国范围内医疗卫生行业网络安全应急响应事件84起&#xf…

is not defined at HTMLInputElement.onclick

vue项目中一小部分功能想要使用原生的onclick 事件&#xff0c;发现报错Uncaught ReferenceError: 函数名 is not defined at HTMLInputElement.onclick搜索了相关blog&#xff0c;得出结论是onclick事件调用的方法必须是全局变量或者说是window的方法解决方法&#xff1a;将cl…

leetcode:43. 字符串相乘(附加一些C++string其他小练习)

目录 一.leetcode&#xff1a;43. 字符串相乘 1.问题描述 2.问题分析 3.问题求解 二. leetcode&#xff1a;541. 反转字符串 II 1.问题描述 2.题解 三. leetcode&#xff1a;125. 验证回文串 1.问题描述 2.双指针法求解 一.leetcode&#xff1a;43. 字符串相乘 43…

数据结构(括号匹配与表达式计算)

目录 括号配对 括号匹配算法 表达式计算 后缀式的计算 中缀式转后缀式 括号配对 编译器做语法检查的任务之一是检查符号是否配对&#xff0c;最简单的符号匹配问题是括号是否匹配&#xff0c;如开括号( 及{ 后面必须依次跟随相应的闭括号 }及 )。 如下段程序中的括号、引号…

谷歌竞价账户效果不好的原因?

很多外贸企业喜欢用谷歌竞价来推广自己的网站&#xff0c;提升自己的竞争力。是一种能很快看到效果的宣传方式。但是很多公司在谷歌竞价上投入了大量的资金&#xff0c;却收效甚微。为什么&#xff1f;下面我们来看看如何解决我们在google竞价推广中会遇到的一些问题。很多人在…

Linux的文件权限理解

目录 前言 1、用户、用户组、其他人 1.1用户、用户组、其他人之间的概念理解 1.2Linux系统中有用户身份与用户组记录的文件 2、Linux文件权限概念 第一列 第二列 第三列 第四列 第五列 第六列 第七列 Linux文件权限的重要性 3、Linux的目录与文件的权限意义 权限…

计算机图形学 第5章 二维变换与裁剪到Cohen-Sutherland直线裁剪算法

目录学习目标前驱知识规范化齐次坐标二维几何变换矩阵物体变换与坐标变换二维几何变换二维图形基本几何变换矩阵平移变换矩阵比例变换矩阵旋转变换矩阵反射变换矩阵错切变换矩阵二维仿射变换二维复合变换相对于任意方向的二维几何变换二维图形裁剪世界坐标系2.用户坐标系观察坐…

VSCode中设置Python语言自动格式化的方案

安装Python扩展 在VSCode的扩展&#xff08;Externsions&#xff09;中使用下面命令检索Python扩展 category:debuggers Python 打开一个Python文件&#xff0c;可以在VSCode的右下角看到运行环境。 安装PEP8 python3.10 -m pip install -U autopep8 安装Flake8 python3.10 …

上百个数字经济新场景 低代码完美搭建 实现项目落地

数字经济 自人类社会进入信息时代以来&#xff0c;数字技术的快速发展和广泛应用衍生出数字经济(Digital Economy)。与农耕时代的农业经济&#xff0c;以及工业时代的工业经济大有不同&#xff0c;数字经济是一种新的经济、新的动能&#xff0c;新的业态&#xff0c;其引发了社…

MicroBlaze系列教程(3):AXI_TIMER的使用

文章目录 @[toc]AXI_TIMER简介常用函数使用示例参考资料工程下载本文是Xilinx MicroBlaze系列教程的第3篇文章。 AXI_TIMER简介 AXI_TIMER支持两路可编程32位计数器,可以配置为中断、捕获、PWM模式,两个32位计数器可以级联为一个64位计数器。 IP核支持的最高频率: 常用…

nacos 服务发现获取列表源码分析

nacos 服务发现获取列表源码是注册中心最重要的技术点之一&#xff0c;其获取服务列表理论上是在首次接口调用时获取&#xff0c;有时候配置饥饿加载&#xff0c;即服务启动时就获取服务列表&#xff1a;今天我们从一个入口解析获取配置列表; 一、客户端源码 1、自动装配&…

推荐系统之推荐缓存服务

5.6 推荐缓存服务 学习目标 目标 无应用 无 5.6.1 待推荐结果的redis缓存 目的&#xff1a;对待推荐结果进行二级缓存&#xff0c;多级缓存减少数据库读取压力步骤&#xff1a; 1、获取redis结果&#xff0c;进行判断 如果redis有&#xff0c;读取需要推荐的文章数量放回&am…

(深度学习快速入门)第四章第一节:基础图像处理知识

文章目录一&#xff1a;位图和矢量图二&#xff1a;图像分辨率三&#xff1a;颜色模式&#xff08;1&#xff09;RGB&#xff08;2&#xff09;HSB&#xff08;2&#xff09;灰度图四&#xff1a;通道五&#xff1a;亮度、对比度和饱和度六&#xff1a;图像平滑和锐化&#xff…

D3股权穿透图

前言&#xff1a;最近做了一个项目&#xff0c;主要就是实现各种类似企查查的各种图谱&#xff0c;欢迎交流。后期将完成的谱图全部链接上&#xff0c;目前已大致实现了&#xff1a; 【企业关系图谱】、【企业构成图谱】、【股权穿透图】、【股权结构图】、【关联方认定图】 准…

【蓝桥杯基础题】2018年省赛—日志统计

&#x1f451;专栏内容&#xff1a;蓝桥杯刷题⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录一、题目描述1.问题描述2.输入格式3.输出格式4.一个例子二、题目分析1、暴力法2、双指针三、代码汇总1、暴力代码汇总2、双…

【Mysql第一期 数据库概述】

文章目录1. 为什么要使用数据库2. 数据库与数据库管理系统2.1 数据库的相关概念2.2 数据库特点2.3SQL优点3.常见的数据库介绍1.Oracle2.SQL Server3.MySQL4.Access5.DB26.PostgreSQL7.SQLite8.informix4. MySQL介绍4.1Mysql重大历史事件4.2 关于MySQL 8.04.3 Why choose MySQL?…

linux内核读文件代码分析

linux下“一切皆文件”,所有设备都可以被抽象成文件,用户态可以通过open、read、write、llseek等api操作一个文件,通过系统调用进入内核态,最终访问到pagecache/磁盘上的数据,然后返回给用户态。 kernel version:v6.2-rc4 社区master主干 用户态应用程序调用read接口,通…

【转载】车载传感器与云端数据交换标准SensorIS的理解与使用

原文 https://zhuanlan.zhihu.com/p/386277784 1、什么是SensorIS?SensorIS全称是Sensor Interface Specification&#xff0c;翻译为中文就是传感器接口规范&#xff0c;是由来自全球汽车行业的主机厂、地图和数据提供商、传感器制造商和电信运营商共同组成的开放团体发布的一…