Andorid Hook进阶

189 阅读6分钟

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

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

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这个方法开始找

 @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方法。

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方法

 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了

 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中注册

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

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中进行最后的处理。

//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

public class ClientTransaction implements Parcelable, ObjectPoolItem {

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

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

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方法

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方法

 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这样就完成了效果如下