引子
Hook技术在android开发领域算是一项黑科技,那么一个新的概念进入视线,我们最关心的3个问题就是,它是什么,有什么用,怎么用
本系列将由浅入深 手把手讲解这三大问题。
本文是第三篇,
高级篇。
前面两篇Hook博文,写了两个demo,一个是hook入门,一个是略微复杂的Activity启动流程的hook。
那么玩点更高端的吧, 正常开发中,所有
Activity
都要在AndroidManifest.xml
中进行注册,才可以正常跳转,通过hook
,可以绕过系统对activity
注册的检测,即使不注册,也可以正常跳转。
鸣谢
感谢大神的博文 https://www.jianshu.com/p/eb4121b037e2
正文大纲
一.整体思路
二.源码索引
三.hook核心代码
四.最终效果
Demo地址:
https://github.com/18598925736/ActivityHookDemo/tree/startActivityWithoutRegiste
正文
提示:本文所有源码索引图,都基于 SDK28-android9.0
系统.
一.整体思路
在之前Activity启动流程的hook的Demo里,我进行了
Activity
流程的hook
,最终采用的方案,是Hook
了AMS
,实现了全局的startActivity
动作的劫持
. 现在就从这个AMS的hook
为起点,来实现无清单启动Activity
.
在 Activity
启动流程的 hook
的Demo里,最后实现的效果是,每次跳转 Activity
,都能看到这个日志:
那么,我们既然侦测到了 startActivity
这个方法的调用,那么自然就可以拿到里面的实参,比如, Intent
。Intent
是跳转意图,从哪里来,跳到哪里去的信息,都包含在 Intent
里面.而, manifestActivity
的检测,也是要根据 Intent
里面的信息来的.所以,要骗过系统,要假装我们跳的 Activity
是已经注册过的,那么只需要将 Intent
里面的信息换成 已经在 manifest
中注册的某个 Activity
就可以了( 这里可能就有人像抬杠了,你怎么知道manifest里面一定有注册Activity....如果一个Activity都没有,你的app是怎么启动的呢,至少得有一个LauncherActivity吧--!
).
确定思路: 1.在AMS的hook函数中,将 真实的Intent中的信息,替换成manifest中已有的Activity信息. 骗过系统的检测机制。2.虽然骗过了系统的检测机制,但是这么一来,每一次的跳转,都会跳到
"假"
的Activity
,这肯定不是我们想要的效果,那么就必须,在真正的跳转时机之前,将 真实的Activity信息,还原回去, 跳到原本该去的Activity
.对应的核心代码,其实也就两句话:
二.源码索引
下图大致画出了:从 Activity.startActivity
动作开始,到最终 跳转动作的最终执行者
全过程.
下面开始看源码,从 Activity.startActivity
开始:
这里开始分支:if(mParent==null)
,但是两个分支最终执行如下:
很显然,两者都是同样的调用过程:
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(....);
mMainThread.sendActivityResult(...);
第一句,
execStartActivity
是 对一些参数的合法性校验,如果不合法,那就会直接抛出异常,比如之前的第二句,
sendActivityResult
才是真正的跳转动作执行者
先进入第一句Instrumentation.ActivityResultar=mInstrumentation.execStartActivity
看看,既然是合法性校验,且看他是如何校验的。
这是
Instrumentation
的execStartActivity
方法结论:它是通过AMS去校验的,AMS startActivity会返回一个int数值,随后,
checkStartActivityResult
方法会根据这个int
值,抛出响应的异常,或者什么都不做.
再进入第二句 mMainThread.sendActivityResult
看真正的跳转动作是如何执行的:ps:这里其实有个诀窍,既然我们的终极目标是要骗过系统的Activity Intent检测,那么,跟着Intent这个变量,就不会偏离方向.
既然intent被封装到了 ClientTransaction
,交给了 mAppThread
,那么继续:
前方有坑,请注意:
androidStudio
里并不能直接跳转,所以要手动,找到下图中的方法,这个ClientTransactionHandler
是ActivityThread
的父类.上图中,调用了
sendMessage(int,Object)
,在ActivityThread
中找到这个方法的实现:找它的最终实现:
找到另一个关键点:
mH
,H
类的定义:(太长了,我就不完整截图了,留下关键的信息)
final H mH = new H();
class H extends Handler {
...
public static final int EXECUTE_TRANSACTION = 159;
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION";
case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
}
}
return Integer.toString(code);
}
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
// Client transactions inside system process are recycled on the client side