首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Android安全
发新帖
5
4
从 App 启动流程看 Android 整体加固
发表于: 2026-2-11 20:59
2753
从 App 启动流程看 Android 整体加固
x0rrrrr
1
2026-2-11 20:59
2753
# 从 App 启动流程看 Android 整体加固 ## 一、前言 最近在学习安卓加固的时候遇到了一些问题不太明白,比如说**为什么一定要替换掉`mClassLoader`而不是别的东西,**于是就是想着通过源码分析一下看看有没有什么突破点,这里做一下记录。由于 Android Framework 体系庞大且逻辑复杂,本人也比水,个人理解难免存在局限或偏差。如果在阅读过程中发现任何错误或疏漏,恳请各位大佬不吝赐教。 这篇笔记基于**Android-13.0.0_r83源码**进行分析。可以从该网站找到:<a href="elink@903K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5M7X3g2X3j5h3&6V1M7X3!0A6k6q4)9J5k6h3y4G2L8g2)9J5c8R3`.`.">XRefAndroid - Support Android 16.0 & OpenHarmony 6.0 (AndroidXRef/AospXRef)</a> ## 二、`Application`的启动 ### `Application`源码分析 这里从`Application`的启动过程开始分析。这里直接从`ActivityThread.java`的`main`函数开始分析。这个是整个`Android`应用进程的真正入口点。  接着就需要跟进到`attach`当中进一步分析了:  该函数当中会调用`attachApplicationLocked`的函数,该函数非常的长,内部会调用`thread.bindApplication`  一个是测试模式的启动,一个是正常模式的启动。通过`bindApplication`,将一系列的参数,消息封装好发送给主线程进行处理。进入该函数内部之后就可以发现将数据打包,最后通过 `sendMessage`将**消息类型**和**消息数据**发送出去了:  这里`sendMessage`将消息发送到了主线程的消息队列,那么由谁来对这个消息进行处理呢?可以看到`H.BIND_APPLICATION`,跟进去查看一下这个`H`:  这个`H`继承的是`Handler`(**一个`ActivityThread`的内部类**),内部有一个`handleMessage`来处理这些对应的消息,这里传入的是`BIND_APPLICATION`类型的消息,那只需要找到`case`即可:  这里就是`BIND_APPLICATION`消息的处理了,内部接收到`data`之后会调用`handleBindApplication`来处理这些消息,这是一个最重要的位置了,继续跟进这个`handleBindApplication`进行分析: 在这个函数当中,有一个非常重要的地方,就是下面这里:  通过这个`getPackageInfoNoCheck`来**获取`LoadedApk`**,这个`LoadedApk`是一个加固的重点,这里简单说一下:**`LoadedApk`**就是一个容器对象,它将硬盘上的APK文件解构成了内存当中可以使用的**类加载器、资源管理器、配置信息等**,供系统随时调用。 这个`data.info`类型就是`LoadedApk`:  **在加固的时候我们同样需要获取这个LoadedApk**,所以接下来需要跟进这个`getPackageInfoNoCheck`来看看到底是怎么获取这个`LoadedApk`,以及这个函数里面做了什么操作:  调用的是`getPackageInfo`,继续跟进:  可以看到这里的: ~~~java LoadedApk packageInfo = ref != null ? ref.get() : null; ~~~ 如果ref不为空,则表示`LoadedApk`不为空,则复用`LoadedApk`。通过这里也可以大致了解到,一个`app`有且只有一个`LoadedApk`。接着就继续往下分析:  这里可以看到,如果没有`LoadedApk`则直接`new`一个出来,接着要来看看这个`LoadedApk`是怎么工作的:  如图所示,会初始化一些必要的东西,例如`mPackageName`。只不过这里没有说明要怎么从别的地方(比如**我们自己的app**)中获取这个`LoadedApk`,这个后续再看。 现在让我们回到`handleBindApplication`继续往下分析,下一个里程碑的位置就是这个位置:  这里通过`LoadedApk`获取了一个`Application`的类。进入分析一下这个`makeApplicationInner`:  接着会查看缓存`sApplications`,如果之前创建过则直接复用。接着就是获取`Application`的类名  接着就是获取类加载器,创建`Application`的上下文,接着创建`Application`并实例`attach`(**这里是用户代码第一次执行的地方**)  跟进这个`newApplication`分析:   可以看到这里`attach`当中调用了`attachBaseContext`,这个`attachBaseContext`**非常重要**,这就是获取到**`Context`**后,用户代码最早执行的地方。由于这个特性可以对这个地方做很多文章。 最终`makeApplicationInner`会返回`app`这个`Application`。现在回到`handleBindApplication`继续分析。接下来就是通过`callApplicationOnCreate`来调用`Application.OnCreate()`这个函数了  到这里就差不多完成了`handleBindApplication`这个函数了,下一步就可以处理`EXECUTE_TRANSACTION`这个消息了,这个消息**处理的是`activity`的启动** ### `Application`小结 通过对`Application`的启动流程可以看出用户代码的执行流程如下: `attachBaseContext` -> `Application.onCreate`。实际上这两个之间还有一个 `ContentProvider.onCreate`这里还没有提及。不过`attachBaseContext`基本上就是用户代码最早执行的一个地方,整体加固的壳代码会在这个位置进行解壳,然后恢复环境。 ## 三、Activity的启动 ### `Activity`源码分析 #### 1. 怎么从`Application`过渡到`Activity` 首先先来分析一下,应用在执行完`Application.OnCreate`到`Activity`究竟发生了什么。想了解这个问题得接着继续往下看 **接下来**回到`attachApplicationLocked`继续往下看,会看到这段代码:  这里调用的是`mAtmInternal.attachApplication`这个函数,要注意的是,这里并不是调用`ActivityManagerService`这个类当中的`attachApplication`函数,先来看看这个`mAtmInternal`是哪个类的:  可以看到是这个`ActivityTaskManagerInternal`这个类的,而这个类的实现在`ActivityTaskManagerService`这个类当中的`LocalService`私有内部类,接下来得看看这个类当中`attachApplication`的实现了:  过来之后又发现将`wpc`委托给了`mRootWindowContainer`的`attachApplication`函数,这个`mRootWindowContainer`是`RootWindowContainer`类的实例,其中`attachApplication`函数定义如下:  接着可以看到的是调用了`mAttachApplicationHelper`当中的`process`函数,这个`mAttachApplicationHelper`是该`RootWindowContainer`类当汇总的一个私有内部类,最后可以找到这个`process`:  接着调用`getChildAt(displayNdx).forAllRootTask`,遍历该屏幕下所有的“根任务”。然后通过回调`this`的函数来处理,这里的调用路径是`RootWindowContainer --> WindowContainer --> Task`下的`forAllRootTask`:  也就是说调用的是`mAttachApplicationHelper`当中的`accept`,而`accept`当中还调用了`rootTask.forAllActivities`函数  如果进入这个位置,最后就会调用`test`,这个`test`当中有一个`realStartActivityLocked`函数:  进入该函数分析,该函数当中又调用了`scheduleTransaction`:  这里会通过`ClientTransactionHandler`来发送`EXECUTE_TRANSACTION`,接下来就是`loop`循环对这个消息进行处理,启动`activity`了。  #### 2. `Activity`的启动 消息循环获取到`EXECUTE_TRANSACTION`之后就开始通过 `mTransactionExecutor`来进行**事务逻辑的分发**  此时需要跟进`execute`进行代码跟踪:  对于这个函数来说,内部执行过程中需要关注这两个函数:`executeCallbacks`和`executeLifecycleState`。首先来看看这个`executeCallbacks`函数 ##### executeCallbacks  进来函数第一步首先将`transaction`(也就是`sendMessage`传入的data),的`callBack`取出来,然后在下方循环中,通过`callbacks`将`item`取出来,再调用`item.execute`继续向下调用,这里要注意的是`item`并不是 `ClientTransactionItem`类型,这里使用多态,`item`真正的类型是:`LaunchActivityItem`。 接下里进入到`LaunchActivityItem.execute`进行分析:  首先对`ActivityClientRecord r`这个变量进行数据封装,然后进入到`client.handleLaunchActivity`这个函数,这个是真正的执行入口,标志着逻辑从“事务管理层”(`Transaction` 框架)正式进入了“Activity 线程管理层”(`ActivityThread`)。 这里的`client`在运行的时候其实就是`ActivityThread`,所以这里使用的是`ActivityThread`当中的`handleLaunchActivity`。接下来就需要对`handleLaunchActivity`进行分析:  该函数中`performLaunchActivity`这里是最最重要的一部分,通过该函数会输出一个初始化完毕、正在运行的 `Activity` 对象。接下来该好好分析这个`performLaunchActivity`函数了 ###### performLaunchActivity  首先就是通过`createBaseContextForActivity`获取`ContextImpl`,然后通过`appContext`的`getClassLoader`来获取类加载器,这里需要跟进查看一下这个`getClassLoader`加载器是怎么获取的,对后续加壳原理的理解非常有帮助。  可以看到`getClassLoader`是通过`mPackageInfo.getClassLoader()`来获取的,而这个`mPackageInfo`类型其实是`LoadedApk`,该类的`getClassLoader()`是返回`mClassLoader`:  接下来就是`mInstrumentation.newActivity`了,这行代码过程,内存中就存在了一个Activity的对象了  来看看这个`newActivity`的调用链`newActivity --> getFactory(pkg).instantiateActivity --> cl.loadClass(className).newInstance`:  这里直接使用了反射调用`activity`的构造函数。这个`new Activity`结束之后,下一步就是用这个创建的`activity`对象进行操作了:  接下来`activity`使用`attach`来做一些初始化工作,这一步会完成`window`的创建以及`Context`的绑定,接下来就是最后一步了:  这里就会调用`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` 后续再根据这个逻辑来实现一个简单的整体加固
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
#基础理论
收藏
・
5
点赞
・
4
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
我的小拇指啊
你的分享对大家帮助很大,非常感谢!
2026-3-13 17:45
x_req
感谢你分享这么好的资源!
2026-2-23 11:26
拾海
感谢你的积极参与,期待更多精彩内容!
2026-2-17 20:11
geoffzh
谢谢你的细致分析,受益匪浅!
2026-2-11 21:19
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
1
)
Imxz
雪 币:
112
活跃值:
(9420)
能力值:
( LV2,RANK:10 )
在线值:
发帖
6
回帖
752
粉丝
9
关注
私信
Imxz
2
楼
tql
2026-2-16 20:38
1
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
x0rrrrr
1
7
发帖
14
回帖
70
RANK
关注
私信
他的文章
[原创]APP整体加固原理
42366
[原创]记一次银狐样本分析
15391
[原创]libmsaoaidsec.so Frida检测、反调与绕过实战
9471
[原创]某软件ollvm混淆登录参数分析
51823
整体加固Demo及加壳工具的编写
2501
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部