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;
}
};
真正调用的是
IActivityManager的startActivity,即是ActivityManagerService的startActivity方法。
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作用
- 权限校验、检查(
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静态字段sPackageManagerApplicationPackageManager对象里的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编程。













网友评论