文章目錄
-
- 1 Hook IActivityManager 方案實作
- 2 Hook Instrumentation 方案實作
四大元件的插件化是插件化技術的核心知識點,而 Activity 插件化更是重中之重,Activity 插件化主要有 3 種實作方式,分别是反射實作、接口實作和 Hook 技術實作。反射實作會對性能有所影響,主流的插件化架構沒有采用此方式,關于接口實作可以閱讀dynamic-load-apk 的源碼,目前 Hook 技術實作是主流,是以本章主要介紹Hook 技術實作。
Hook 技術實作主要有兩種解決方案, 一種是通過 Hook IActivityManager 來實作,另一種是 Hook Instrumentation 實作。
1 Hook IActivityManager 方案實作
<application
android:name=".MyApplication"
......
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".StubActivity"/>
</application>
import java.lang.reflect.Field;
public class FieldUtil {
public static Object getField(Class clazz,Object target,String name)throws Exception{
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(target);
}
public static Field getField(Class clazz,String name)throws Exception{
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field;
}
public static void setField(Class clazz,Object target,String name ,Object value)throws Exception{
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
field.set(target,value);
}
}
import android.content.Intent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 替換IActivityManager的代理類
*/
public class IActivityManagerProxy implements InvocationHandler {
private static final String TAG = "IActivityManagerProxy";
private Object mActivityManager;
public IActivityManagerProxy(Object activityManager) {
this.mActivityManager = activityManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {//1
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];
Intent subIntent = new Intent();//2
String packageName = "com.hongx.unuse";
subIntent.setClassName(packageName, packageName + ".StubActivity");//3
subIntent.putExtra(HookHelper.TARGET_INTENT, intent);//4
args[index] = subIntent;//5
}
return method.invoke(mActivityManager, args);
}
}
import android.os.Build;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
public class HookHelper {
public static final String TARGET_INTENT = "target_intent";
public static void hookAMS() throws Exception{
Object defaultSingleton = null;
if(Build.VERSION.SDK_INT >= 26){//1
Class<?> activityManageClazz = Class.forName("android.app.ActivityManager");
defaultSingleton = FieldUtil.getField(activityManageClazz,null,"IActivityManagerSingleton");
}else{
Class<?> activityManageClazz = Class.forName("android.app.ActivityManagerNative");
defaultSingleton = FieldUtil.getField(activityManageClazz,null,"gDefault");
}
Class<?> singletonClazz = Class.forName("android.util.Singleton");
Field mInstanceField = FieldUtil.getField(singletonClazz,"mInstance");//2
Object iActivityManager = mInstanceField.get(defaultSingleton);//3
Class<?> iAcitivityManagerClazz = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader()
,new Class<?>[]{iAcitivityManagerClazz}
,new IActivityManagerProxy(iActivityManager));
mInstanceField.set(defaultSingleton,proxy);
}
}
import android.app.Application;
import android.content.Context;
public class MyApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
try {
HookHelper.hookAMS();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv_hook = findViewById(R.id.tv_hook);
tv_hook.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this,TargetActivity.class));
}
});
}
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
public class HCallback implements Handler.Callback {
public static final int LAUNCH_ACIVITY = 100;
Handler mHandler;
public HCallback(Handler handler){
mHandler = handler;
}
@Override
public boolean handleMessage(Message msg) {
if(msg.what == LAUNCH_ACIVITY){
Object r = msg.obj;
try {
Intent intent = (Intent) FieldUtil.getField(r.getClass(),r,"intent");
Intent target = intent.getParcelableExtra(HookHelper.TARGET_INTENT);
intent.setComponent(target.getComponent());
} catch (Exception e) {
e.printStackTrace();
}
}
mHandler.handleMessage(msg);
return true;
}
}
在HookHelper.java中添加hookHandler方法
public static void hookHandler() throws Exception {
Class<?> activityThreadClass = Class.forName( "android.app.ActivityThread");
Object currentActivityThread= FieldUtil.getField (activityThreadClass ,null ,"sCurrentActivityThread" ) ; //1
Field mHField = FieldUtil.getField (activityThreadClass,"mH" ) ; //12
Handler mH = (Handler) mHField.get(currentActivityThread);//3
FieldUtil.setField(Handler.class , mH,"mCallback",new HCallback(mH));
}
2 Hook Instrumentation 方案實作
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.text.TextUtils;
import java.lang.reflect.Method;
import java.util.List;
public class InstrumentationProxy extends Instrumentation {
private Instrumentation mInstrumentation;
private PackageManager mPackageManager;
public InstrumentationProxy(Instrumentation instrumentation, PackageManager packageManager) {
mInstrumentation = instrumentation;
mPackageManager = packageManager;
}
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
List<ResolveInfo> infos = mPackageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL);
if (infos == null || infos.size() == 0) {
intent.putExtra(HookHelper.TARGET_INTENT, intent.getComponent().getClassName()); //1
intent.setClassName(who, "com.hongx.plugin.StubActivity");//2
}
try {
Method execMethod = Instrumentation.class.getDeclaredMethod(
"execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class,
Intent.class, int.class, Bundle.class);
return (ActivityResult) execMethod.invoke(mInstrumentation, who,
contextThread, token, target, intent, requestCode, options);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public Activity newActivity(ClassLoader cl,String className,Intent intent) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
String intentName = intent.getStringExtra(HookHelper.TARGET_INTENT);
if(!TextUtils.isEmpty(intentName)){
return super.newActivity(cl,intentName,intent);
}
return super.newActivity(cl,className,intent);
}
}
public static void hookInstrumentation(Context context) throws Exception{
Class<?> contextImplClass = Class.forName("android.app.ContextImpl");
Field mMainThreadField = FieldUtil.getField(contextImplClass,"mMainThread");
Object activityThread = mMainThreadField.get(context);
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Field mInstrumentation = FieldUtil.getField(activityThreadClass,"mInstrumentation");
FieldUtil.setField(activityThreadClass
,activityThread
,"mInstrumentation"
,new InstrumentationProxy((Instrumentation)mInstrumentation.get(activityThread),context.getPackageManager()));
}
public class MyApplication extends Application {
@Override
protected void attachBaseContext(Context context) {
super.attachBaseContext(context);
try {
// HookHelper.hookAMS();
// HookHelper.hookHandler();
HookHelper.hookInstrumentation(context);
} catch (Exception e) {
e.printStackTrace();
}
}