Android Hook入门
上一篇文章学习了Hook的简单用,这次来做个稍微麻烦一点的,我们知道新建一个Activity之后我们需要在manifest中注册,否则启动的时候就会崩溃,现在使用Hook的方法绕过检查来启动一个没有注册的Activity
如果我们不注册的话就会报下面的错误
1 | android.content.ActivityNotFoundException: Unable to find explicit activity class |
然后找一下这个错误是在哪里报出来的,我们就在检查报错的前面Hook一下,给他传入一个正常的Activity,在检查之后在Hook一下,替换回我们要去的Activity就好了。
下面的源码是基于Android9.0的,每个版本的源码可能不一样
寻找第一个Hook点
从startActivity这个方法开始找1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 @Override
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, null);
}
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
...
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
...
}
一路点击跟进,最后进入到了Instrumentation这个类中的execStartActivity方法。1
2
3
4
5
6
7
8
9
10
11
12
13public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
...
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
...
}
ActivityManager.getService()是拿到拿到ActivityManagerService服务在本地的代理对象,然后通过它操作ActivityManagerService执行startActivity方法,最后返回一个结果,最后执行checkStartActivityResult方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public static void checkStartActivityResult(int res, Object intent) {
if (!ActivityManager.isStartResultFatalError(res)) {
return;
}
switch (res) {
case ActivityManager.START_INTENT_NOT_RESOLVED:
case ActivityManager.START_CLASS_NOT_FOUND:
if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
throw new ActivityNotFoundException(
"Unable to find explicit activity class "
+ ((Intent)intent).getComponent().toShortString()
+ "; have you declared this activity in your AndroidManifest.xml?");
throw new ActivityNotFoundException(
"No Activity found to handle " + intent);
...
在checkStartActivityResult方法中可以看到,当res返回是START_CLASS_NOT_FOUND的时候就会报出一开始的错误了。因为我们传过去的Activity,ActivityManagerService找不到。
所以我们就可以把检查方法之前的ActivityManager.getService().startActivity
作为第一个Hook点,我们给它随便传一个注册过的Acivity,这样就可以欺骗ActivityManagerService了
1 | ActivityManager.getService() |
可以看到Singleton是一个系统的单例类,getService()
方法调用的时候,就会create方法,最终会调用IActivityManagerSingleton 中的create方法创建一个IActivityManager返回。
IActivityManager就是ActivityManagerService在本地的代理对象。用来进行进程间的Binder通信。
我们来Hook IActivityManager,替换成我们自己的。
先定义一个空的ProxyActivity,并在AnroidManifest中注册1
2
3
4
5
6
7public class ProxyActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Toast.makeText(this, "我是代理Activity", Toast.LENGTH_SHORT).show();
}
}
然后在Application中Hook住AMS1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46public class PluginApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
try {
hookAms();
} catch (Exception e) {
e.printStackTrace();
}
}
private void hookAms() throws Exception{
Class<?> activityManagerClass = Class.forName("android.app.ActivityManager");
Field iActivityManagerSingletonField = activityManagerClass.getDeclaredField("IActivityManagerSingleton");
iActivityManagerSingletonField.setAccessible(true);
//静态方法不用穿参数
Object iActivityManagerSingleton = iActivityManagerSingletonField.get(null);
Class<?> singletonClass = Class.forName("android.util.Singleton");
Field mInstance = singletonClass.getDeclaredField("mInstance");
mInstance.setAccessible(true);
Class<?> iActivityManagerClass = Class.forName("android.app.IActivityManager");
Method getServiceMethod = activityManagerClass.getDeclaredMethod("getService");
final Object iActivityManagerObj = getServiceMethod.invoke(null);
//定义我们自己的IActivityManager
Object proxyIActivityManager = Proxy.newProxyInstance(getClassLoader(),
//需要监听的 IActivityManager
new Class[]{iActivityManagerClass}
, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("startActivity".equals(method.getName())){
Intent proxyIntent = new Intent(PluginApplication.this,
ProxyActivity.class);
proxyIntent.putExtra("targetIntent",(Intent) args[2]);
args[2] = proxyIntent;
}
return method.invoke(iActivityManagerObj, args);
}
});
//需要两个参数 IActivityManagerSingleton 和 我们自己的动态代理对象
mInstance.set(iActivityManagerSingleton,proxyIActivityManager);
}
我们的目的很清楚,通过反射拿到IActivityManager的实例,然后把它替换成我们自己的proxyIActivityManager。动态代理对象中,我们把intent替换成一个注册过的Activity也就是ProxyActivity。现在我们就拦截住了,当我们跳转到LoginActivity这个没有注册的Activity的时候,就会先跳转到该Activity
效果:
当然这不是我们想要的效果,我们需要在检查完之后再给它替换回来,所以在检查完后还要Hook一个地方给它换回来。
第二个Hook点
熟悉Activity的启动流程的都知道,ActivityManagerService处理完成之后,会执行到realStartActivityLocked(可以看之前的文章:Activity启动流程(上) 和 **Activity启动流程(下))最终会回到ActivityThread类中的mH这个Handler中进行最后的处理。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45//frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
......
// 为Activity的launch创建 transaction
final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
r.appToken);
//创建一个LaunchActivityItem对象,并传添加到事物中
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, mService.isNextTransitionForward(),
profilerInfo));
//设置Activity的最终状态
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
......
}
//ActivityThread中 mH 这个Handler中的handleMessage
public void handleMessage(Message msg) {
...
switch (msg.what) {
...
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
//系统流程中的客户端事务在客户端回收
// instead of ClientLifecycleManager to avoid being cleared before this
transaction.recycle();
}
break;
...
}
在realStartActivityLocked方法中创建了一个LaunchActivityItem方法,添加到到事物中,最终在handler中执行真正的启动。msg.obj中封装的是ActivityManagerService传过来的对象信息,强转成ClientTransaction
1 | public class ClientTransaction implements Parcelable, ObjectPoolItem { |
ClientTransaction内部有一个ClientTransactionItem的集合,在前面realStartActivityLocked方法中可以看到将一个LaunchActivityItem添加到ClientTransaction中的集合中,也就是mActivityCallbacks中。1
2
3
4
5
6
7
8
9
10public class LaunchActivityItem extends ClientTransactionItem {
private Intent mIntent;
private int mIdent;
private ActivityInfo mInfo;
private Configuration mCurConfig;
private Configuration mOverrideConfig;
private CompatibilityInfo mCompatInfo;
....
LaunchActivityItem中存储了Activity的各种信息,这里有一个mIntent参数,它现在的跳转是我们在上一个Hook点改变成的ProxyActivity,所以这里我们需要重新给他还原会我们的LoginActivity,这样才能顺利跳转到LoginActivity中
所以我们需要在执行Handler中的handleMessage方法之前将它给改了。
我们知道Handler的消息分发机制中有一个dispatchMessage方法
1 | public void dispatchMessage(Message msg) { |
Activity的启动最终会执行handleMessage方法,而在这个之前有一个判断,如果mCallback不为null就执行(mCallback.handleMessage(msg)方法,所以我们可以给它传一个我们自己的CallBack,在内部将mIntent给改了,然后返回false它还是会继续执行下面的handleMessage方法,这样就完成了替换。
在Application中在写一个hookActivityThread方法
1 | private void hookActivityThread() throws Exception{ |
OK这样就完成了效果如下