AMS
AMS作用
startActivity
最终调用AMS
的startActivity()
,实现了Activity
的启动,Activity
生命周期的回调也是在AMS
中完成的。startService
和bindService
最终调用AMS
的startService
和bindService
- 动态广播的注册和接收是在
AMS
中完成的。getContentResolver
最终调用AMS
的getContentProvider
。
AMS获取
Activity启动
Context
类的startActivity
:这种启动方式没有Activity
栈,必须加上FLAG_ACTIVITY_NEW_TASK
这个flag
。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
启动流程可知,mBase
是ContextImpl类对象。核心代码如下所示:
// 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);
}
- 非
Activity
的Context
里启动Activity
需要使用FLAG_ACTIVITY_NEW_TASK
标志- 流程转到
Instrumentation
的execStartActivity()
方法里
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;
}
};
Activity启动.png真正调用的是
IActivityManager
的startActivity
,即是ActivityManagerService
的startActivity
方法。
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作用
- 权限校验、检查(
checkPermission
,checkUidPermission
)Apk meta
信息获取(getApplicationInfo
)- 四大组件信息获取(
query
系列方法)- 静态广播的注册和接收在
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掉这两个地方即可:
ActivityThread
静态字段sPackageManager
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
代理对象的引用,所以如果我们需要完全Hook
住PMS
,需要精确控制整个进程内部创建的Context
对象。由此可见,Hook掉一个实例变量是很麻烦的!
总结
一个干净、透明的框架少不了
AOP
,而AOP
少不了Hook
。Hook
除了使用反射和动态代理技术外,还可以使用更加强大的字节码编织,比如cglib
、asm
和javassist
等框架也可以进行AOP
编程。
网友评论