【胖虎的逆向之路】04——脱壳(一代壳)原理脱壳相关概念详解
【胖虎的逆向之路】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.ccint 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加载有何不同:
(1)ClassLoader.loadClass也能加载一个类,但是不会触发类的初始化(也就是说不会对类的静态变量,静态代码块进行初始化操作)
(2)Class.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
【胖虎的逆向之路】04——脱壳(一代壳)原理脱壳相关概念详解相关推荐
- 【胖虎的逆向之路】01——动态加载和类加载机制详解
胖虎的逆向之路 01--动态加载和类加载机制详解 一.前言 二.类的加载器 1. 双亲委派模式 2. Android 中的类加载机制 1)Android 基本类的预加载 2)Android类加载器层级 ...
- 【胖虎的逆向之路】03——Android一代壳脱壳办法罗列实操
[胖虎的逆向之路]03--Android脱壳办法罗列&脱壳原理详解 [胖虎的逆向之路]01--动态加载和类加载机制详解 [胖虎的逆向之路]02--Android整体加壳原理详解&实现 ...
- 【胖虎的逆向之路】02——Android整体加壳原理详解实现
[胖虎的逆向之路](02)--Android整体加壳原理详解&实现 Android Apk的加壳原理流程及详解 文章目录 [胖虎的逆向之路](02)--Android整体加壳原理详解& ...
- 【胖虎的逆向之路】如何绕过 Android11新特性之 “包的可见性“
前言 距离Android11 发布已经过去了,当初我有大概了解过一些Android 11上的行为变更,总体变化虽然不少,但是要求我们必须去适配的地方并不算多.对于我而言可能需要注意的是文件相关权限,譬 ...
- 【胖虎的逆向之路】Android 7.0 上Magisk配合Xposed的相关问题
基础环境 1.Android 7.1.0(硬件小米6 sagit): 2.Magisk V23.0 3.Xposed (由Magisk-模块-搜索下载) 安装 首先android刷机.Magisk R ...
- 脱壳工具:BlackDex的使用详解
1.BlackDex:是一个运行在Android手机上的脱壳工具,支持5.0-12,无需依赖任何环境任何手机都可以使用,包括模拟器.只需几秒,即可对已安装包括未安装的APK进行脱壳. 备注:Black ...
- 脱壳工具:ZjDroid的使用详解
一. ZjDroid概述 ZjDroid是基于Xposed Framewrok的动态逆向分析模块,可以完美解决二代加固. github地址:https://github.com/halfkiss/Zj ...
- Ubuntu18.04没有WiFi怎么解决(图文详解)
博主一个月前在安装Ubuntu后出现了没有WiFi的问题,参考了很多教程,才成功解决这个问题,中间做了很多无用功,所以在此总结几位大佬的方法,希望对大家有帮助,少走弯路. 1.问题描述 登录Ubunt ...
- windows 10远程连接ubuntu 18.04 Gnome桌面:NoMachine工具使用详解
文章目录 Gnome桌面环境 NoMachine工具 实际操作 Ubuntu操作 Windows操作 远程连接设置 总结 做为系统管理员,远程连接到各种服务器算是常规操作.如果本地是windows环境 ...
最新文章
- 0x61.图论 - 最短路
- Oracle 11.2 安装Oracle 11.1的HR schoma
- 排序---初级排序算法(选择排序、插入排序和希尔排序)
- 图书查找java_java第三季第一章:查找图书信息实现
- 仿京东左侧二级导航条
- [html] 你知道短链接的生成原理吗?
- 腾讯云连续四年排名中国音视频解决市场第一,头部厂商中RTC增速第一
- linux的odbc数据库连接失败,在Linux上通过ODBC进行Delphi SQLConnection无法连接到SQL
- NOIP 2000 进制转换
- 解决Tomcat严重: Parse error in application web.xml file at jndi:/localhost/ipws/WEB-INF/web.xml java.lan
- ms office excel2013教程 - 套用表格样式
- STM32 学习笔记 expected a type specifier
- gcc之 -ffunction-sections
- android:ListView的局部刷新
- Idea设置Java类注释模板和方法注释模板
- win10玩cf不能全屏_神奇的工作室ghost 不能启动
- elasticsearch win10 安装
- 长微博工具:菊子曰自动微博文字转图片,突破微博140字限制,一键发布完成...
- Windows 开发之VC++垃圾清理程序软件
- js filter 多条件过滤适合对象属性