【Android学习】近期壳相关回顾

工具学习

Posted by Corax on May 20, 2024

最近在补app壳的东西,东西很多,脑子很晕,故写个博客来整理整理

本篇仅作梳理和简单回顾,更多分析内容在前面几篇博客

大致分为:

前置知识
壳通识
一代壳 dexclassloader 我理解的落地加载
一代壳 inmemorydexclassloader 我理解的不落地加载
二代壳 dalvik下抽取壳的实现
二代壳 art下抽取壳的实现

前置知识

JVM的三种类加载器

image-20240528035745283

委托关系

image-20240528035836389

class加载流程

image-20240528035849192

class继承关系

image-20240528035909111

壳通识

attachbasecontext和oncreate的初识

了解一下如何进入我们的app代码的

image-20240528040302891

activitythread
handlebindapplication
makeapplication
newapplication
attach
attachbasecontext

image-20240528040346283

image-20240528040441851

解决dexclassloader加载的类的问题

image-20240528040548361

其实说白了,沟槽的也就是classloader不合适,要我们自己的classloader来才行

然后举了一个第一种方法反射替换的例子

oncreate

startTestActivityFirstMethodstar

replaceClassloader 通过反射 替换了目标dex当中mclassloader

startTestActivityFirstMethodstar 完成对目标dex目标activity的调用

注意manifest还要改

注意是替换组件的classloader

image-20240528040847183

第二种呢

image-20240528040712566

其实也是通过反射来实现插入的,

DexClassLoader dexClassLoader=new DexClassLoader(dexfilepath,optfile.getAbsolutePath(),libfile.getAbsolutePath(),bootClassloader);

把插入的dexclassloader的parent变成现在classloader的parent

把现在的parent变成dexclassloader

加壳

加壳,有两种方式,

本身没有application的声明,只要自己进行声明,然后壳程序实现解密,加载就可以

另外的一种要额外去调用原apk的application

8以上的inmemoryclassloader从内存中读,避免直接被找到文件

4左右的 opendexfile是文件形式 安全性低

壳发展

动态加载

是dex加壳,插件化,热更新的一个基础

什么是热加载 就是用到的时候再去加载

image-20240528040935566

其实我觉得dex加密 有分落地和不落地的

vmp 解释器 找到映射关系

dex2c 语义转换很恶心

要关注JNI相关的调用

一代壳 dexclassloader 脱壳方案

image-20240528041100329

文件加载和内存加载两种表现形式

核心总是在内存当中把dex文件dump下来

多数输在看源码了

image-20240528041731992

image-20240528050915681

image-20240528050927393

一代壳 inmemorydexclassloader 脱壳

inmemory

image-20240528042605486

image-20240528042613207

dexclassloader

其实应该不是放在不落地加载壳里的。。之前那个落地加载壳基于安卓四的opendexfile,上面那个是安卓8art8下inmemory的不落地加载,这个是安卓8art下dexclassloader生成oat时的情况

image-20240528050344392

在art进行抽取实现的时候也要阻塞这个dex2oat

阻塞成功后的分支

opendexfilesfromoat不成功
	dexfile::open 这还之前的open还不一样 有openandreadmagic等
		opencommon
		mapfile

image-20240528050627503

image-20240528050939106

一个整体壳的脱壳方法

一种是inmemorydexclassloader
一种是禁用掉dex2oat之后用dexclassloader

甚于说对class加载或者method列的时候,都可以,只要对内存中dex文件有操作,都可以尝试。

二代壳 dalvik下抽取壳的实现

记住恢复时间永远要早于这个被调用时机

image-20240528051125626

这些里面还是有很多的

image-20240528051138742

以dexclassloader的loadclass为例

loadclass
	findclass
		loadclassbinaryname
			defineclass
				dvmgetrawdexfiledex
				dvmdefineclass
					findclassnoinit
						dvmlookupclass
							dexfindclass 也是姜维师傅找到得以抽取函数复原的一个时机点

二代壳 art下抽取壳的实现

俩事

  1. 干掉dex2oat
  2. 实现抽取函数的还原

干掉dex2oat

为什么要干掉oat?

就是这个dex2oat的产物oat,如果生成了这个oat,后续所用代码会从这个文件里获得,以至于我们动态修改的dex的smali就不会生效了。

其实也有俩方案

禁用dex2oat 阻止掉dex2oat的过程
还原时机早于dex2oat

我们选第一个

需要回顾到dex2oat的部分

generateoatfilenochecks
	dex2oat
		exec
			execandreturncode
				execve

上面的流程中的打断都会阻断oat的生成,所以尝试hook 试execve

image-20240528051758869

实现抽取函数的还原

对classmethod的流程

image-20240528051852919

其中有个东西至关重要 codeitem

loadmethod里面完成赋值,所以这就是一个时机

image-20240528052033533

深入看看, Artmethod里面有个变量 很重要

image-20240528052054435

这就是我们说的codeitem

当一个class加载好之后,就准备好了这个每个函数对于的artmethod对象,每个artmethod对象都和java层的method 一一对应

当我们禁用了de2oat 就都在解释模式下运行

就可以找到这个codeitem

image-20240528052118820

根据dex结构,找到codeitem位置 加上16字节为十六进制

image-20240528052206137

通过先调用原loadmethod获得codeitem,再判断是不是想要的方法,在进行还原

image-20240528052248181