美文网首页
AMS&PMS Hook

AMS&PMS Hook

作者: Res2013 | 来源:发表于2018-05-12 17:10 被阅读136次

AMS

AMS作用

  1. startActivity最终调用AMSstartActivity(),实现了Activity启动Activity生命周期的回调也是在AMS中完成的。
  2. startServicebindService最终调用AMSstartServicebindService
  3. 动态广播的注册和接收是在AMS中完成的。
  4. getContentResolver最终调用AMSgetContentProvider

AMS获取

Activity启动

  1. Context类的startActivity:这种启动方式没有Activity栈,必须加上FLAG_ACTIVITY_NEW_TASK这个flag
  2. Activity类的startActivity

Android Studio中使用快捷键Ctrl + H可以查看Context类继承关系图,可以看到Activity继承了ContextThemeWrapper,而ContextThemeWrapper继承了ContextWrapper。查看ContextWrapper代码可知:

public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }

...

    @Override
    public void startActivity(Intent intent) {
        mBase.startActivity(intent);
    }
}

分析Activity启动流程可知,mBaseContextImpl类对象。核心代码如下所示:

// ActivityThread.java
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    Application app = r.packageInfo.makeApplication(false, mInstrumentation);
}

// LoadedApk.java
    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        ...
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        app = mActivityThread.mInstrumentation.newApplication(
                        cl, appClass, appContext);
}
 
// Instrumentation.java
    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        return newApplication(cl.loadClass(className), context);
    }

    static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
          Application app = (Application)clazz.newInstance();
          app.attach(context);
          return app;
    }

// Application.java
    /* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

    ...

    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

继续分析ContextImpl类中的startActivity方法,如下:

@Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();

        // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
        // generally not allowed, except if the caller specifies the task id the activity should
        // be launched in.
        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
                && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }
  1. ActivityContext里启动Activity需要使用FLAG_ACTIVITY_NEW_TASK标志
  2. 流程转到InstrumentationexecStartActivity()方法里

Instrumentation中相关代码如下:

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        ...
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
}

...

// 单例(Hook点)
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };

真正调用的是IActivityManagerstartActivity,即是ActivityManagerServicestartActivity方法。

Activity启动.png

Anroid API-25有以上不同的流程,之后的具体执行流程可参考《Android艺术开发探索》一书。

Activity中启动

Activity类中startActivity方法直观很多,就是调用startActivityForResult方法,这方法核心代码如下:

Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);

后续的调用流程同ContextImpl类启动Activity无本质不同。

Hook AMS

由上述分析流程可知,AMS代理对象引用是用单例保存起来的,那么我们只需要
简单地Hook这个单例即可。示例代码如下所示:

public class AMSHookUtils {

    public void attachAMS() throws Exception {
        Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
        Field gDefaultFiedld = activityManagerNativeClass.getDeclaredField("gDefault");
        gDefaultFiedld.setAccessible(true);
        Object gDefault = gDefaultFiedld.get(null);

        Class<?> singletonClass = Class.forName("android.util.Singleton");
        Field mInstanceField = singletonClass.getDeclaredField("mInstance");
        mInstanceField.setAccessible(true);
        // 原始IActivityManager对象
        Object rawIActivityManager = mInstanceField.get(gDefault);

        Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager");
        Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<?>[]{ iActivityManagerInterface },
                new ActivityManagerHandler(rawIActivityManager));
        // 偷梁换柱
        mInstanceField.set(gDefault, proxy);
    }
}

...

public class ActivityManagerHandler implements InvocationHandler {

    private static final String TAG = "ActivityManagerHandler";

    private Object mBase;

    public ActivityManagerHandler(Object base) {
        mBase = base;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.e(TAG, "hook IActivityManager");
        Log.e(TAG, "method name is:" + method.getName() + ";args is:" + Arrays.toString(args));
        return method.invoke(mBase, args);
    }
}

Android Framework层对四大组件的处理最终都是通过IActivityManager对象来完成对远程AMS的访问。

PMS

PMS作用

  1. 权限校验、检查(checkPermissioncheckUidPermission)
  2. Apk meta信息获取(getApplicationInfo)
  3. 四大组件信息获取(query系列方法)
  4. 静态广播的注册和接收在PMS中完成

PMS获取过程

PMS获取是通过Context对象调用getPackageManager方法完成的,又由上分析AMS获取过程可知,最终是调用ContextImpl类对象调用的getPackageManager方法,代码如下:

// ContextImpl.java
@Override
    public PackageManager getPackageManager() {
        if (mPackageManager != null) {
            return mPackageManager;
        }

        IPackageManager pm = ActivityThread.getPackageManager();
        if (pm != null) {
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        }

        return null;
    }

// ApplicationPackageManager.java
private final IPackageManager mPM;

...

ApplicationPackageManager(ContextImpl context,
                              IPackageManager pm) {
        mContext = context;
        mPM = pm;
    }

// ActivityThread.java
static volatile IPackageManager sPackageManager;

...

public static IPackageManager getPackageManager() {
        if (sPackageManager != null) {
            //Slog.v("PackageManager", "returning cur default = " + sPackageManager);
            return sPackageManager;
        }
        IBinder b = ServiceManager.getService("package");
        //Slog.v("PackageManager", "default service binder = " + b);
        sPackageManager = IPackageManager.Stub.asInterface(b);
        //Slog.v("PackageManager", "default service = " + sPackageManager);
        return sPackageManager;
    }

接下来只需要Hook掉这两个地方即可:

  1. ActivityThread静态字段sPackageManager
  2. ApplicationPackageManager对象里的mPM字段

Hook PMS

代码如下:

public class PMSHookUtils {

    public static void attachPMS(Context context) throws Exception {
        Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
        Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
        currentActivityThreadMethod.setAccessible(true);
        // ActivityThread
        Object currentActivityThread = currentActivityThreadMethod.invoke(null);

        Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");
        sPackageManagerField.setAccessible(true);
        // sPackageManager
        Object sPackageManager = sPackageManagerField.get(currentActivityThread);

        Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager");
        Object proxy = Proxy.newProxyInstance(iPackageManagerInterface.getClassLoader(),
                new Class<?>[]{ iPackageManagerInterface },
                new PMSHookHandler(sPackageManager));
        // 1. 偷梁换柱(替换掉sPackageManager字段)
        sPackageManagerField.set(currentActivityThread, proxy);

        // 2. 替换掉ApplicationPackageManager里的mPM字段
        PackageManager pm = context.getPackageManager();
        Field mPMField = pm.getClass().getDeclaredField("mPM");
        mPMField.setAccessible(true);
        mPMField.set(pm, proxy);
    }
}

...

public class PMSHookHandler implements InvocationHandler {

    private static final String TAG = "PMSHookHandler";

    private Object mBase;

    public PMSHookHandler(Object base) {
        mBase = base;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.e(TAG, "hook IActivityManager");
        Log.e(TAG, "method name is:" + method.getName() + ";args is:" + Arrays.toString(args));
        return method.invoke(mBase, args);
    }
}

Context实现类里没有使用静态全局变量来保存PMS的代理对象,而是每个Context实例持有一个PMS代理对象的引用,所以如果我们需要完全HookPMS,需要精确控制整个进程内部创建的Context对象。由此可见,Hook掉一个实例变量是很麻烦的!

总结

一个干净、透明的框架少不了AOP,而AOP少不了HookHook除了使用反射和动态代理技术外,还可以使用更加强大的字节码编织,比如cglibasmjavassist等框架也可以进行AOP编程。

相关文章

网友评论

      本文标题:AMS&PMS Hook

      本文链接:https://www.haomeiwen.com/subject/fchhdftx.html