一. 前言
本文部分内容来自于《android进阶解密》这本书,不同的是书中实现的是android9.0之前的hook,在android9.0中,activity的启动过程会有些不同,因此本文不会对activity启动流程做具体分析,主要是讲解9.0的hook.
二. 寻找hook点
这里需要寻找两个地方的hook点,一个是对Intent中Activity的替换,一个是对Intent中Activity的还原
1. Activity替换
android8和9启动流程中,到获得AMS进行远程调用的过程都是一样的,这里可以对AMS进行动态代理,也可以对Instrumentation
进行代理,android8中对AMS的hook过程和9有点不一样,网上有太多对android8的AMS的hook文章,这里只介绍对9的处理
AMS的代理类
public class IActivityManagerProxy implements InvocationHandler {
private Object activityManager;
private static final String TAG="IActivityManagerProxy";
public IActivityManagerProxy(Object activityManager){
this.activityManager=activityManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("startActivity")){
Intent intent =null;
int index=0;
for(int i=0;i<args.length;i++){
if(args[i] instanceof Intent){
index=i;
break;
}
}
intent = (Intent) args[index];
String packageName = "com.suny.hooktest";
Intent subIntent = new Intent();
//替换activity为已经注册的占坑activity,这里第一个参数为application所在的包,第二//个参数为Activity所在的包+activityName
subIntent.setClassName(packageName,packageName+".SubActivity");
//同时将真正的intent保存在subintent中,绕过AMS的检查后,将真正的intent替换回来
subIntent.putExtra("target_intent",intent);
args[index]=subIntent;
Log.d(TAG, "invoke: subIntent="+subIntent+"inteent="+intent);
}
return method.invoke(activityManager,args);
}
}
hook操作
public class HookHelper {
public static void hookAms() throws Exception {
Class clazz = ActivityManager.class;
Field singletonIAMS = clazz.getDeclaredField("IActivityManagerSingleton");
singletonIAMS.setAccessible(true);
Object defultSingleton = singletonIAMS.get(null);
Class singletonClazz = Class.forName("android.util.Singleton");
Field mInstance = singletonClazz.getDeclaredField("mInstance");
mInstance.setAccessible(true);
Object iAMs = mInstance.get(defultSingleton);
Class iAmClazz =Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{iAmClazz},new IActivityManagerProxy(iAMs));
mInstance.set(defultSingleton,proxy);
}
}
2. Activity还原
对启动流程不做更多分析,在启动过程中,由于是通过AMS跨进程调用的,所以最后一步都是ActivityStackSupervisor
中相关方法,通过ApplicationThread
进行跨进程调用
private class ApplicationThread extends IApplicationThread.Stub
如果你对aidl
熟悉的话,你可以看到,这和aidl
帮我们生成的类的结构一样
(1). 8.0中的处理
在ActivityStackSupervisor
通过调用realStartActivityLocked
final boolean realStartActivityLocked (...) {
...
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, !andResume,
mService.isNextTransitionForward(), profilerInfo);
...
}
AppThreadApplication
通过handle发送了一条消息
public final void scheduleLaunchActivity(...) {
...
sendMessage(H.LAUNCH_ACTIVITY, r);
...
}
对消息的处理
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
break;
...
可以看到在Android8中,对Activity的还原可以在这里进行,因为ActivityClientRecord
中包含有
intent
这个变量
(2) 9.0中的处理
intent的查找
在ActivityStackSupervisor
通过调用realStartActivityLocked
final boolean realStartActivityLocked (...) {
...
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, mService.isNextTransitionForward(),
profilerInfo));
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
...
}
mService.getLifecycleManager()
即为ClientLifecycleManager
,ClientLifecycleManager.scheduleTrasaction
方法的调用过程
void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
//注意这个 Client,即为ApplicationThread
final IApplicationThread client = transaction.getClient();
transaction.schedule();
if (!(client instanceof Binder)) {
// If client is not an instance of Binder - it's a remote call and at this point it is
// safe to recycle the object. All objects used for local calls will be recycled after
// the transaction is executed on client in ActivityThread.
transaction.recycle();
}
}
里边调用了ClientTransaction
的schedule
方法
public void schedule() throws RemoteException {
mClient.scheduleTransaction(this);
}
mClient
即为ApplicationThread
,这样调用过程又通过跨进程的方法调用,回到本地了
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
ActivityThread.this.scheduleTransaction(transaction);
}
这里ActivityThread
继承了ClientTransactionHandler
,也是通过handler发送了一条消息
void scheduleTransaction(ClientTransaction transaction) {
transaction.preExecute(this);
sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}
接着看消息的处理过程
public void handleMessage(Message msg) {
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
// Client transactions inside system process are recycled on the client side
// instead of ClientLifecycleManager to avoid being cleared before this
// message is handled.
transaction.recycle();
}
// TODO(lifecycler): Recycle locally scheduled transactions.
break;
}
这里可以看到和8中消息处理已经不太一样了,查看ClientTransaction
,这个类中并没有intent这个字段,不过,intent肯定在其中,不然ActivityThread怎么进行解析呢,沿着方法调用查找,TransactionExecutor
的execute
方法
public void execute(ClientTransaction transaction) {
...
executeCallbacks(transaction);
executeLifecycleState(transaction);
...
}
在executeCallbacks
方法中
public void executeCallbacks(ClientTransaction transaction) {
...
final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
...
for (int i = 0; i < size; ++i) {
...
final ClientTransactionItem item = callbacks.get(i);
...
item.execute(mTransactionHandler, token, mPendingActions);
item.postExecute(mTransactionHandler, token, mPendingActions);
...
}
}
我们发现item.execute()
可能是启动流程中的一个关键方法,在这里边可能会去调用ActivityThread
的相关方法来完成Activity的创建工作,但是这个ClientTransactionItem
是一个抽象类,必须要找到他的一个实现类
public abstract class ClientTransactionItem implements BaseClientRequest, Parcelable
还记得9的启动过程中吗,在ActivityStackSupervisor
中的realStartActivityLocked
方法中在调用scheduleTransaction
之前有这个一段代码
final boolean realStartActivityLocked (...) {
...
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, mService.isNextTransitionForward(),
profilerInfo));
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
...
}
可以看到clientTransaction
添加了一个LaunchActivityItem
的实现类,并且把intent,放进了这个实现类中。
总结一下:
在9中,intent是在ClientTransaction
中List<ClientTransactionItem> mActivityCallbacks
这个成员变量中元素的一个实现类LaunchActivityItem
的成员变量中
hook操作将intent还原
主要是对ActivityThread
中的Handler(mH)
成员变量进行hook,设置一个callback
public class HookHelper {
public static void hookHandler() throws Exception{
Class acThreadClazz = Class.forName("android.app.ActivityThread");
Field currentActivityThread = acThreadClazz.getDeclaredField("sCurrentActivityThread");
currentActivityThread.setAccessible(true);
Object currThread = currentActivityThread.get(null);
Field mHField = acThreadClazz.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mH = (Handler) mHField.get(currThread);
Field mCallback = Handler.class.getDeclaredField("mCallback");
mCallback.setAccessible(true);
//为mH,添加mCallback,那么mH在handleMessage时,就会走我们自己定义的callback中的handleMessage方法
mCallback.set(mH,new HCallback(mH));
}
}
问题:在ActivityThread
中sCurrentActivityThread
虽然是静态变量,但是初始值是null,会导致我们通过反射获取到的值为null吗?
答案是不会,初始化时我们一般最早会在application
的attachBaseContext
中进行初始化,而sCurrentActivityThread
的赋值,是在ActivityThread
的main
方法中,通过attach
方法进行赋值,而application的创建在赋值之后
private void attach(boolean system) {
//赋值
sCurrentActivityThread = this;
...
//application的创建在这里,又是一次远程调用后,会调用bindApplication方法,
//进行ContentProvider的初始化,和application的创建,随便提一下,在这里可以看出
//ContentProvider的onCreate在Application的onCreate之前执行,
//但是Application的attachBaseContext在ContentProvider的onCreate之前执行
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
HCallbck
public class HCallback implements Handler.Callback{
private final String TAG="HCallback";
private Handler mHandler;
public HCallback(Handler handler){
mHandler=handler;
}
@Override
public boolean handleMessage(Message msg) {
//这里为159,是因为EXECUTE_TRANSACTION字段的值为159
if(msg.what==159){
//r实际为clienttransaction
Object r= msg.obj;
try {
Class clientClazz = r.getClass();
Field fCallbacks = clientClazz.getDeclaredField("mActivityCallbacks");
fCallbacks.setAccessible(true);
//得到transactionz中的callbacks,为一个list,其中元素为LaunActivityItem
List<?> lists = (List) fCallbacks.get(r);
for(int i=0;i<lists.size();i++){
Object item = lists.get(i);
Class itemClazz = item.getClass();
//拿到LaunActivityItem中的intent,进行替换
Field mIntent = itemClazz.getDeclaredField("mIntent");
mIntent.setAccessible(true);
Intent intent = (Intent) mIntent.get(item);
Intent target = intent.getParcelableExtra("target_intent");
if(target!=null){
intent.setComponent(target.getComponent());
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
mHandler.handleMessage(msg);
return true;
}
}
最后
在application中初始化即可
public class MyApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
try {
HookHelper.hookAms();
HookHelper.hookHandler();
} catch (Exception e) {
e.printStackTrace();
}
}
}
三. 思考
其实hookActivity主要就是绕过AMS的验证,因此在调用AMS的startActivity方法之前,都可以进行intent的替换,在AMS过后都可以进行intent的还原,因此有很多种hook方法,比如hook,Instrumatention进行intent的替换,对于intent的还原,目前我只想到在handlerMeassage中进行还原,因为ActivityThread中有一个静态变量sCurrentActivityThread,可以获取activitythread对象,从而可以改变mH这个变量,或许还有更好的hook点