Andorid Hook进阶

Android Hook入门

上一篇文章学习了Hook的简单用,这次来做个稍微麻烦一点的,我们知道新建一个Activity之后我们需要在manifest中注册,否则启动的时候就会崩溃,现在使用Hook的方法绕过检查来启动一个没有注册的Activity

如果我们不注册的话就会报下面的错误

1
2
3
android.content.ActivityNotFoundException: Unable to find explicit activity class
{com.chs.hookplugin/com.chs.hookplugin.LoginActivity};
have you declared this activity in your AndroidManifest.xml?

然后找一下这个错误是在哪里报出来的,我们就在检查报错的前面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
13
public 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
17
public 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
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
 ActivityManager.getService()

public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
public abstract class Singleton<T> {
private T mInstance;

protected abstract T create();

public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}

可以看到Singleton是一个系统的单例类,getService()方法调用的时候,就会create方法,最终会调用IActivityManagerSingleton 中的create方法创建一个IActivityManager返回。

IActivityManager就是ActivityManagerService在本地的代理对象。用来进行进程间的Binder通信。

我们来Hook IActivityManager,替换成我们自己的。

先定义一个空的ProxyActivity,并在AnroidManifest中注册

1
2
3
4
5
6
7
public class ProxyActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Toast.makeText(this, "我是代理Activity", Toast.LENGTH_SHORT).show();
}
}

然后在Application中Hook住AMS

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
46
public 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
2
3
4
5
6
7
public class ClientTransaction implements Parcelable, ObjectPoolItem {

/** A list of individual callbacks to a client. */
private List<ClientTransactionItem> mActivityCallbacks;

...
}

ClientTransaction内部有一个ClientTransactionItem的集合,在前面realStartActivityLocked方法中可以看到将一个LaunchActivityItem添加到ClientTransaction中的集合中,也就是mActivityCallbacks中。

1
2
3
4
5
6
7
8
9
10
public 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
2
3
4
5
6
7
8
9
10
11
12
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

Activity的启动最终会执行handleMessage方法,而在这个之前有一个判断,如果mCallback不为null就执行(mCallback.handleMessage(msg)方法,所以我们可以给它传一个我们自己的CallBack,在内部将mIntent给改了,然后返回false它还是会继续执行下面的handleMessage方法,这样就完成了替换。

在Application中在写一个hookActivityThread方法

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
46
47
48
49
50
51
private void hookActivityThread() throws Exception{
//拿到mH对象
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Field mHField = activityThreadClass.getDeclaredField("mH");
mHField.setAccessible(true);

//拿到ActivityThread对象
Object currentActivityThreadObj = activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null);
Object mHObj = mHField.get(currentActivityThreadObj);

//拿到mCallback替换成我们自己的
Field mCallbackField = Handler.class.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
mCallbackField.set(mHObj,new MyCallBack());
}

private class MyCallBack implements Handler.Callback{

@Override
public boolean handleMessage(Message msg) {
Object clientTransactionObj = msg.obj;
//拿到intent ProxyActivity
try {
Class launchActivityItemClass = Class.forName("android.app.servertransaction.LaunchActivityItem");
//从mActivityCallbacks中取出mLaunchActivityItem的对象
Field mActivityCallbacksField = clientTransactionObj.getClass().getDeclaredField("mActivityCallbacks");
mActivityCallbacksField.setAccessible(true);
List activityCallbackList = (List) mActivityCallbacksField.get(clientTransactionObj);
if (activityCallbackList.size() == 0) {
return false;
}
Object mLaunchActivityItem = activityCallbackList.get(0);
if (!launchActivityItemClass.isInstance(mLaunchActivityItem)) {
return false;
}
//找到mIntent字段准备替换
Field mIntentField = launchActivityItemClass.getDeclaredField("mIntent");
mIntentField.setAccessible(true);
//获取代理的Intent
Intent ProxyIntent = (Intent) mIntentField.get(mLaunchActivityItem);
//获取传过来的当前的Intent
Intent targetIntent = ProxyIntent.getParcelableExtra("targetIntent");
if(targetIntent!=null){
mIntentField.set(mLaunchActivityItem,targetIntent);
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}

OK这样就完成了效果如下

# 架构

コメント

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×