首页
社区
课程
招聘
从 App 启动流程看 Android 整体加固
发表于: 3小时前 83

从 App 启动流程看 Android 整体加固

3小时前
83

从 App 启动流程看 Android 整体加固

一、前言

​ 最近在学习安卓加固的时候遇到了一些问题不太明白,比如说**为什么一定要替换掉mClassLoader而不是别的东西,**于是就是想着通过源码分析一下看看有没有什么突破点,这里做一下记录。由于 Android Framework 体系庞大且逻辑复杂,本人也比水,个人理解难免存在局限或偏差。如果在阅读过程中发现任何错误或疏漏,恳请各位大佬不吝赐教。

​ 这篇笔记基于Android-13.0.0_r83源码进行分析。可以从该网站找到:XRefAndroid - Support Android 16.0 & OpenHarmony 6.0 (AndroidXRef/AospXRef)

二、Application的启动

Application源码分析

​ 这里从Application的启动过程开始分析。这里直接从ActivityThread.javamain函数开始分析。这个是整个Android应用进程的真正入口点。

p1

​ 接着就需要跟进到attach当中进一步分析了:
p2

该函数当中会调用attachApplicationLocked的函数,该函数非常的长,内部会调用thread.bindApplication
p3

​ 一个是测试模式的启动,一个是正常模式的启动。通过bindApplication,将一系列的参数,消息封装好发送给主线程进行处理。进入该函数内部之后就可以发现将数据打包,最后通过 sendMessage消息类型消息数据发送出去了:
p4

​ 这里sendMessage将消息发送到了主线程的消息队列,那么由谁来对这个消息进行处理呢?可以看到H.BIND_APPLICATION,跟进去查看一下这个H
p5

这个H继承的是Handler(一个ActivityThread的内部类),内部有一个handleMessage来处理这些对应的消息,这里传入的是BIND_APPLICATION类型的消息,那只需要找到case即可:
p6

​ 这里就是BIND_APPLICATION消息的处理了,内部接收到data之后会调用handleBindApplication来处理这些消息,这是一个最重要的位置了,继续跟进这个handleBindApplication进行分析:

​ 在这个函数当中,有一个非常重要的地方,就是下面这里:
p7

​ 通过这个getPackageInfoNoCheck获取LoadedApk,这个LoadedApk是一个加固的重点,这里简单说一下:LoadedApk就是一个容器对象,它将硬盘上的APK文件解构成了内存当中可以使用的类加载器、资源管理器、配置信息等,供系统随时调用。

​ 这个data.info类型就是LoadedApk
p8

在加固的时候我们同样需要获取这个LoadedApk,所以接下来需要跟进这个getPackageInfoNoCheck来看看到底是怎么获取这个LoadedApk,以及这个函数里面做了什么操作:
p9

​ 调用的是getPackageInfo,继续跟进:
p10

​ 可以看到这里的:

1
LoadedApk packageInfo = ref != null ? ref.get() : null;

如果ref不为空,则表示LoadedApk不为空,则复用LoadedApk。通过这里也可以大致了解到,一个app有且只有一个LoadedApk。接着就继续往下分析:
p11

​ 这里可以看到,如果没有LoadedApk则直接new一个出来,接着要来看看这个LoadedApk是怎么工作的:
p12

如图所示,会初始化一些必要的东西,例如mPackageName。只不过这里没有说明要怎么从别的地方(比如我们自己的app)中获取这个LoadedApk,这个后续再看。

​ 现在让我们回到handleBindApplication继续往下分析,下一个里程碑的位置就是这个位置:
p13

这里通过LoadedApk获取了一个Application的类。进入分析一下这个makeApplicationInner
p14

​ 接着会查看缓存sApplications,如果之前创建过则直接复用。接着就是获取Application的类名
p15

​ 接着就是获取类加载器,创建Application的上下文,接着创建Application并实例attach这里是用户代码第一次执行的地方
p16

跟进这个newApplication分析:
p17

p18

​ 可以看到这里attach当中调用了attachBaseContext,这个attachBaseContext非常重要,这就是获取到**Context**后,用户代码最早执行的地方。由于这个特性可以对这个地方做很多文章。

​ 最终makeApplicationInner会返回app这个Application。现在回到handleBindApplication继续分析。接下来就是通过callApplicationOnCreate来调用Application.OnCreate()这个函数了
p19

​ 到这里就差不多完成了handleBindApplication这个函数了,下一步就可以处理EXECUTE_TRANSACTION这个消息了,这个消息处理的是activity的启动

Application小结

​ 通过对Application的启动流程可以看出用户代码的执行流程如下: attachBaseContext -> Application.onCreate。实际上这两个之间还有一个 ContentProvider.onCreate这里还没有提及。不过attachBaseContext基本上就是用户代码最早执行的一个地方,整体加固的壳代码会在这个位置进行解壳,然后恢复环境。

三、Activity的启动

Activity源码分析

1. 怎么从Application过渡到Activity

​ 首先先来分析一下,应用在执行完Application.OnCreateActivity究竟发生了什么。想了解这个问题得接着继续往下看

接下来回到attachApplicationLocked继续往下看,会看到这段代码:
p20

​ 这里调用的是mAtmInternal.attachApplication这个函数,要注意的是,这里并不是调用ActivityManagerService这个类当中的attachApplication函数,先来看看这个mAtmInternal是哪个类的:
p21

​ 可以看到是这个ActivityTaskManagerInternal这个类的,而这个类的实现在ActivityTaskManagerService这个类当中的LocalService私有内部类,接下来得看看这个类当中attachApplication的实现了:
p22

​ 过来之后又发现将wpc委托给了mRootWindowContainerattachApplication函数,这个mRootWindowContainerRootWindowContainer类的实例,其中attachApplication函数定义如下:
p23

​ 接着可以看到的是调用了mAttachApplicationHelper当中的process函数,这个mAttachApplicationHelper是该RootWindowContainer类当汇总的一个私有内部类,最后可以找到这个process
p24

接着调用getChildAt(displayNdx).forAllRootTask,遍历该屏幕下所有的“根任务”。然后通过回调this的函数来处理,这里的调用路径是RootWindowContainer --> WindowContainer --> Task下的forAllRootTask
p25

​ 也就是说调用的是mAttachApplicationHelper当中的accept,而accept当中还调用了rootTask.forAllActivities函数
p26

​ 如果进入这个位置,最后就会调用test,这个test当中有一个realStartActivityLocked函数:
p27

进入该函数分析,该函数当中又调用了scheduleTransaction
p28

这里会通过ClientTransactionHandler来发送EXECUTE_TRANSACTION,接下来就是loop循环对这个消息进行处理,启动activity了。
p29

2. Activity的启动

​ 消息循环获取到EXECUTE_TRANSACTION之后就开始通过 mTransactionExecutor来进行事务逻辑的分发
p30

此时需要跟进execute进行代码跟踪:
p31

​ 对于这个函数来说,内部执行过程中需要关注这两个函数:executeCallbacksexecuteLifecycleState。首先来看看这个executeCallbacks函数

executeCallbacks

p32

​ 进来函数第一步首先将transaction(也就是sendMessage传入的data),的callBack取出来,然后在下方循环中,通过callbacksitem取出来,再调用item.execute继续向下调用,这里要注意的是item并不是 ClientTransactionItem类型,这里使用多态,item真正的类型是:LaunchActivityItem

​ 接下里进入到LaunchActivityItem.execute进行分析:
p33

​ 首先对ActivityClientRecord r这个变量进行数据封装,然后进入到client.handleLaunchActivity这个函数,这个是真正的执行入口,标志着逻辑从“事务管理层”(Transaction 框架)正式进入了“Activity 线程管理层”(ActivityThread)。

​ 这里的client在运行的时候其实就是ActivityThread,所以这里使用的是ActivityThread当中的handleLaunchActivity。接下来就需要对handleLaunchActivity进行分析:
p34

该函数中performLaunchActivity这里是最最重要的一部分,通过该函数会输出一个初始化完毕、正在运行的 Activity 对象。接下来该好好分析这个performLaunchActivity函数了

performLaunchActivity

p35

​ 首先就是通过createBaseContextForActivity获取ContextImpl,然后通过appContextgetClassLoader来获取类加载器,这里需要跟进查看一下这个getClassLoader加载器是怎么获取的,对后续加壳原理的理解非常有帮助。
p36

​ 可以看到getClassLoader是通过mPackageInfo.getClassLoader()来获取的,而这个mPackageInfo类型其实是LoadedApk,该类的getClassLoader()是返回mClassLoader
p37

​ 接下来就是mInstrumentation.newActivity了,这行代码过程,内存中就存在了一个Activity的对象了
p38

​ 来看看这个newActivity的调用链newActivity --> getFactory(pkg).instantiateActivity --> cl.loadClass(className).newInstance
p39

​ 这里直接使用了反射调用activity的构造函数。这个new Activity结束之后,下一步就是用这个创建的activity对象进行操作了:
p40

​ 接下来activity使用attach来做一些初始化工作,这一步会完成window的创建以及Context的绑定,接下来就是最后一步了:
p41

这里就会调用Activity.OnCreate,接下来就是一些状态设置以及后续activity的启动了。这里就不继续往下分析了。

3. Activity小结

​ 本阶段梳理了 Activity 的实例化过程,揭示了 LoadedApk.mClassLoader 在其中的决定性作用。源码显示,系统在 performLaunchActivity 中强制使用 appContext.getClassLoader() 获取加载器,并通过反射 (loadClass().newInstance()) 创建 Activity 实例。 这一机制解释了为何加固壳必须替换 mClassLoader:只有“偷梁换柱”,将解密后的 DEX 注入到这个特定的 LoadedApk 实例中,才能骗过系统的检查,让原本不存在于 base.apk 中的 Activity 被成功加载和启动。

四、总结

​ 小小的总结了一下:加固的本质就是在 Application.attachBaseContext 阶段,利用 LoadedApk 的单例特性,通过“偷梁换柱”的方式替换 mClassLoader,从而欺骗系统在后续 Activity 启动时去加载我们解密后的代码。
还有就是app的启动流程:attachBaseContext --> ContentProvider.onCreate --> Application.OnCreate --> Activity.OnCreate
​ 后续再根据这个逻辑来实现一个简单的整体加固


[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回