首页
社区
课程
招聘
[原创]Android下的art的ArtMethod一些学习分享
发表于: 6天前 1538

[原创]Android下的art的ArtMethod一些学习分享

6天前
1538

为什么会写这个呢?其实很多东西都是之前写的,最近又整理了一下,也算是一个温故而知新吧!
之前一直找看一下传说中的goto类抽取,也想看看到底是怎么实现的,以及对比bangbang/ijm等有什么区别。
也问了很多人,也委托几个大佬帮我留意一下样本,也是没了后续。
直到今年又有了几个新的样本,也蛮有意思的,分析完了就找到了那个goto样本,分析之后。这篇文章也就对我最近捣鼓的样本的一些总结吧!
如果看下去的话,请不要嫌弃文章的啰嗦,因为要照顾一些刚接触的。所以我会尽可能地完整的去过一遍流程。为了方便学习,这里以Android8.1,在Android12等高版本就有有很多的不适用性,但相应的也出现了一些配套的Api,这里不展开细说。

本篇文章以一个Java的方法执行的全过程为切入点的,即如下的伪代码

谈到安卓的Java虚拟机,那必定离不开安卓的art虚拟机。早期还有dalvikvm虚拟器。不过也成为了一种过去式的历史。不过现在也一另外的形式存在。那就是dexvmp
那么我么今天就以Java的代码如何在Android的上运行做一个分享。

img_4.png

在Android中有几个版本跨域比较大,那就是4.4.4/7.12/8.1/12/15,art虚拟机就是在Android的4.4.4中发布,伴随这art虚拟机的引入,安卓也走入了一个全新的时代,即从解释型虚拟机到编译后的二进制文件。

如果有学习过Java的开发,那都会遇到jar,dex就是在Android上的“jar”。姑且可以这样子理解(jar可以直接改为.zip打开,dex不行,jar可以放一些资源,dex只能放smail文件)
涉及到jar/dex,那么就会有一个属性的东西类加载器“ClassLoader”,这个在上一次分享中,有介绍过不同JDK实现有提过类加载的一个过程,这次会比较详细的分享他的一些过程。

img_7.png

img_6.png

他的默认实现,从这里也很生动的体现他的“双亲委派机制”解决类加载器的重复加载问题,从中也可以发现,loadClass的会调用findClass进行查找,以及他们的权限修饰符,可以通过继承的形式进行覆盖。
下面三个就是他在Android的三个实现子类

img.png

img_1.png

img_2.png

通过上面的图片来看,他们并没有在这里实现什么,甚至也没什么区别,都是继承于BaseDexClassLoader这个类,那么我们重点就在这了。
上面三个实现ClassLoader的子类,我们也比较容易理解他们的作用。

BootClassLoader
这个看名字就知道啦,所以跳过咯

img_3.png

通过ClassLoader的代码分析,我们知道loadClass会调用findClass去查找并加载类,那么我们只需要关注findClass的实现即可
img_8.png

在这里我们可以看到会调用pathList的findClass调用,以及下面的方法,addDexPath,也是调用pathList的.addDexPath方法

pathList的定义
img_9.png

img_10.png
这里我们可以看到DexPathList的findClass也是调用dexElements数组的中实现的findClass方法

dexElements初始化刚好在DexPathList的构造函数中,也就是在makeInMemoryDexElements这个函数中
img_14.png

img_15.png
dexElements是makeInMemoryDexElements返回的,Element初始化的过程中还会创建一个DexFile对象,

img_16.png
可以通过这两个相邻的函数可以发现,这就是加载两种不同形式的dex的函数,上面的是通过ByteBuffer[],下面的是通过List

那我们以文件加载的形式去分析Dex的加载,那也就是会调用makeDexElements方法中的loadDexFile中返回

那也就是
img_17.png

img_18.png
我们跟一下DexFile的构造函数,这里也很明显可以看到有两个不同的构造函数,一个是传递的是ByteBuffer,一个是String的fileName

然后我们回到Element的findClass
Element
img_12.png

那么最终的类加载就会变成
DexFile的defineClassNative
img_19.png

正常逆向分析的思维就是,一路跟到底,最后才是看如何构造一个全链路。
先到初始化,再到查找嘛,????
这里我们就换一下,先看他如何出初始化再捣鼓的

####### 插眼

openDexFile
这个是在DexFile的初始化的时候,可能会执行,取决于是传递byte[]还是dexPath,也就是不同类加载器的区别了。
现在我们以DexClassLoader的形式去加载的话(PathClassLoader也是一样的)去进行分析,也就是会执行dexPath的这个构造函数

img_54.png

最终调用到native的方法,那么我们继续跟去
dalvik_system_DexFile.cc

DexFile_openDexFileNative
img_21.png
这里会调用OpenDexFilesFromOat返回,然后通过ConvertDexFilesToJavaArray函数处理之后返回到Java层

OpenDexFilesFromOat

img_53.png

会先判断是否加载oat相关的代码。这里我们默认第一次执行,也就不会涉及到oat/odex/vdex等相关的东西了,因为可以通过这里dump到完整的dex,壳代码会禁止相关的优化,或者是屏蔽了。。。
屏蔽也有一些好处,有一些inline的优化,也是很头疼,屏蔽之后就可以一劳永逸了,代价没那么快了。
这里的快的概念,是对于正常开发者的,而对于我们学习来说,也就没那么重要了。

img_24.png

img_25.png
所以上面的dex_files就会为空,从而执行DexFile::Open

,这里就不一路分析到底了,感兴趣的可以继续分析下去
OatFile* OatFile::Open
LoadDexFiles
OatFileAssistant::LoadDexFiles
std::unique_ptr
OatFileBase* OatFileBase::OpenOatFile

DexFile::Open
这里的DexFile::Open也是一路执行到DexFile::OpenFile

DexFile::OpenFile

img_26.png
在这里我们就会进入到一个特殊的点了,开始将dex文件映射到内存中去。至于为什么特殊?
等下会解释。

MemMap* MapFile
img_27.png
MapFile也是调用MapFileAtAddress

MemMap::MapFileAtAddress
img_28.png
MapFileAtAddress进一步调用MapInternal

MemMap::MapInternal
MapInternal之后就到了我们遇到的特殊点了,使用mmap去映射内存
img_55.png
我们可以往上翻,会注意到他的权限是PROT_READ,

img_29.png

DexFile::OpenCommon

到这里我们就看完分配内存了,我们接下来往下看DexFile::OpenCommon

img_30.png

中也没存做啥,只是创建个DexFile对象,然后返回

ConvertDexFilesToJavaArray
img_22.png·
我们可以看到他是传递个oat_file以及DexFile的vec
细心的看的话,就知道她他会创建个数据,第一个元素放到的是oat的对象的地址,第二个就是DexFile(一个或多个)
然后返回,最终就是DexFile的mCookie对象了,下面会有图片比较直观的体现

dalvik_system_DexFile.h

img_74.png
img_75.png

刚才我们去看了DexFile的创建以及初始化过程,接下来我们继续看类的定义

DexFile_defineClassNative
img_20.png
调用class_linker->DefineClass的

class_linker.cc

ClassLinker::DefineClass
img_31.png
DefineClass中也是进一步调用LoadClass

ClassLinker::LoadClass
img_32.png
LoadClass进一步调用LoadClassMembers

ClassLinker::LoadClassMembers
img_33.png

在这里我们就可以看到比较的多东西了

ClassLinker::LoadMethod
img_34.png

static void LinkCode

在这里我们就可以触发第二个彩蛋

Class_newInstance
img_56.png
类实例的创建

img_57.png
这里我们能看到类的初始化入口

或者这个路径(没有测试,只是代码搜素搜到这个)

GetFieldID
img_36.png

FindFieldID

img_37.png

EnsureInitialized

img_35.png

最终也是会执行到ClassLinker::EnsureInitialized

ClassLinker::EnsureInitialized

img_38.png
ClassLinker::EnsureInitialized调用ClassLinker::InitializeClass

ClassLinker::InitializeClass

img_39.png
ClassLinker::InitializeClass调用ClassLinker::FixupStaticTrampolines

ClassLinker::FixupStaticTrampolines

img_40.png

到这里我们就可以看到他的参数了klass,至于这个是什么,后面会有测试进行的说明

既然我们说了类是怎么加载的,然后到类定义,方法的加载,类的初始化,那么就顺便把最后的一个:方法的执行

Method

img_42.png

Method继承于Executable。这个需要考的,现在先记住这个就好了

invoke
img_41.png
这个就是Java的方法反射执行的入口了

Method_invoke
img_43.png
Method_invoke调用InvokeMethod

InvokeMethod

img_23.png
InvokeMethod调用InvokeWithArgArray
同时我们也看到了

这种奇怪的代码转换,在后面还会有很多,,哈哈哈

InvokeWithArgArray
img_47.png

InvokeWithArgArray做好准备参数检查的准备
就执行method->Invoke

img_48.png
ArtMethod::Invoke
img_49.png

到这一步我们就进入了ArtMethod的世界!
开源的类抽取还原框架fart,也就是以这个为入口进行虚假调用并触发方法的修复
这里我们可以看到一段描述

然后我们就执行到了
art::interpreter::EnterInterpreterFromInvoke
进入解释模式

EnterInterpreterFromInvoke
img_50.png
在这里会判断是否为jni方法

我们此次分析的是普通的Java函数,所以会执行Execute

Execute
img_51.png

这里应该是执行到一些优化之后的代码吧?我们要看的是解释模式,所以忽略

ArtInterpreterToCompiledCodeBridge

img_58.png
这里也是从新执行一遍method->Invoke,
最终应该会执行到EnterInterpreterFromInvoke的InterpreterJni去吗?

ExecuteSwitchImpl
img_52.png

在这里解析指令了咯?所以结束了?
不,这才刚开始。
上面的说那么多无非就是想引入一些点:LoadMethod、、DexFile::Open、Execute等等方法 还有mCookie等字段是什么?

e1cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5M7X3g2X3j5h3&6V1M7X3!0A6k6q4)9J5k6h3y4G2L8g2)9J5c8X3q4F1k6s2u0G2K9h3c8Q4x3X3b7^5i4K6u0W2x3g2)9J5k6e0m8Q4y4h3k6J5z5o6q4Q4x3V1k6^5M7X3g2X3i4K6u0r3j5i4u0@1i4K6u0r3M7Y4g2F1N6r3W2E0k6g2)9J5c8X3q4J5N6q4)9#2k6X3#2W2N6r3S2G2k6q4)9J5k6h3R3`.

77cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5M7X3g2X3j5h3&6V1M7X3!0A6k6q4)9J5k6h3y4G2L8g2)9J5c8X3q4F1k6s2u0G2K9h3c8Q4x3X3b7^5i4K6u0W2x3g2)9J5k6e0m8Q4y4h3k6J5z5o6q4Q4x3V1k6^5M7X3g2X3i4K6u0r3j5i4u0@1i4K6u0r3M7Y4g2F1N6r3W2E0k6g2)9J5c8X3#2A6M7Y4u0G2M7W2)9J5c8X3y4D9j5i4y4K6i4K6u0W2K9l9`.`.

a27K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5M7X3g2X3j5h3&6V1M7X3!0A6k6q4)9J5k6h3y4G2L8g2)9J5c8X3q4F1k6s2u0G2K9h3c8Q4x3X3b7^5i4K6u0W2x3g2)9J5k6e0m8Q4y4h3k6J5z5o6q4Q4x3V1k6^5M7X3g2X3i4K6u0r3j5i4u0@1i4K6u0r3M7Y4g2F1N6r3W2E0k6g2)9J5c8X3c8W2P5q4)9#2k6X3k6A6L8r3g2Q4x3X3g2Z5

376K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5M7X3g2X3j5h3&6V1M7X3!0A6k6q4)9J5k6h3y4G2L8g2)9J5c8X3q4F1k6s2u0G2K9h3c8Q4x3X3b7^5i4K6u0W2x3g2)9J5k6e0m8Q4y4h3k6J5z5o6q4Q4x3V1k6^5M7X3g2X3i4K6u0r3L8r3W2T1j5$3!0J5k6g2)9J5c8X3!0B7L8s2g2F1K9g2)9J5c8Y4y4J5j5#2)9J5c8X3#2S2K9h3&6Q4x3V1k6B7j5i4k6S2i4K6u0r3K9X3q4$3j5g2)9J5c8X3I4S2L8X3N6Q4x3V1k6o6L8r3q4K6M7#2)9J5k6h3A6S2N6X3p5`.

img_59.png

看到这图,是不是一切都豁然开朗了?
那么费力的去讲解这么多的流程,无非就是想解释,最终的数据加载到内存了,都成了什么样子了?
到这里我们是不是会有很多奇怪的想法?

那我们以一个demo为例:
img_60.png

比如我们可以拿到一个Method,进一步我们可以拿到他的类定义、然后就是dex缓存,从而拿到dexFile,然后是就是计算code_item的偏移?然后拿到指令?
对的,就顺便脱壳,也做了类修复
img_67.png
img_66.png

既然可以拿到指令? 那我是否可以修改指令?
是的,上面也提到了类加载的内存映射只有READ的权限,也就需要hook去修改,或者使用下面的方式(详情可以看dpt-shell的作者分析的文章)
img_65.png
然后就根据实际需求去修改就好了

或者替换偏移?
img_64.png

如果指令大小不够了怎么办?
直接mmap一个区域也给他弄个偏移就好了
img_63.png

或者我们可以做一个动态的指令生成? 如何动态的生成指令?
img_62.png
直接编译拿到之后直接整个方法定义长得差不多的,直接拿过来就可以用了(fid,mid,这些可能会错乱哈哈啊哈)

如何直接写指令去实现?
img_61.png
直接用asm去写就好了
这里的参数传递的规则有点奇怪,得从3/4开始的

上面的一个整体效果:::

img_76.png

输出
这两个是偏移替换的,可以看到run执行了fake/fake2的代码
img_77.png
img_78.png

......
动态修改&执行?提高我们的风控难度?对吧?

Constructor
img_46.png
这里的是构造函数,同理分析也是差不多的,所以跳过吧!哈哈哈

Executable

Executable的定义
img_45.png

img_44.png
artMethod指针

private long artMethod;
这个东西就很关键了,那就是artMethod的地址

还能有栈回溯方便吗?

img_73.png

new Exception().printStackTrace();
abort

img_72.png

Instruction::DumpString

ArtMethod *artmeth = reinterpret_cast<ArtMethod *>(methid);
还有上面的,很多的都是可以直接转换的了


[培训]传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 6天前 被陈可牛编辑 ,原因: 上传图片
收藏
免费 66
支持
分享
最新回复 (32)
雪    币: 6234
活跃值: (5597)
能力值: ( LV5,RANK:76 )
在线值:
发帖
回帖
粉丝
2
前三是我的
6天前
0
雪    币: 6234
活跃值: (5597)
能力值: ( LV5,RANK:76 )
在线值:
发帖
回帖
粉丝
3
前三是我的
6天前
0
雪    币: 6234
活跃值: (5597)
能力值: ( LV5,RANK:76 )
在线值:
发帖
回帖
粉丝
4
前三是我的
6天前
0
雪    币: 2855
活跃值: (3639)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
图片是不是挂了呀
6天前
0
雪    币: 6234
活跃值: (5597)
能力值: ( LV5,RANK:76 )
在线值:
发帖
回帖
粉丝
6
SomeMx 图片是不是挂了呀
是的,在私信处理了,这个我也不知道咋弄
6天前
0
雪    币: 2
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
感谢分享
6天前
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
1111111111111111
6天前
0
雪    币: 34
活跃值: (1742)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
hhh
6天前
0
雪    币: 8033
活跃值: (4493)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
学习学习
6天前
0
雪    币: 852
活跃值: (2800)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
6
6天前
0
雪    币: 371
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
666
6天前
0
雪    币: 212
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
学习学习
6天前
0
雪    币: 1
活跃值: (91)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
666666666666
6天前
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
666
5天前
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
666
5天前
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
感谢分享
5天前
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
666
5天前
0
雪    币: 534
活跃值: (1743)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
666
5天前
0
雪    币: 183
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
666
5天前
0
雪    币: 5444
活跃值: (8925)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
好文章
3天前
0
雪    币: 5538
活跃值: (3804)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
666
3天前
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
666
3天前
0
雪    币: 246
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
24
666
3天前
0
雪    币: 3549
活跃值: (3700)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
6
3天前
0
游客
登录 | 注册 方可回帖
返回