天天看點

Android對startActivity方法進行Hook1 Hook Activity的startActivity方法2 Hook Context 的 startActivity 方法3 Hook startActivity 總結

文章目錄

  • 1 Hook Activity的startActivity方法
  • 2 Hook Context 的 startActivity 方法
  • 3 Hook startActivity 總結

startActivity 方法的兩種形式:

  1. startActivity 有兩種寫法, 最常見的是下面這種, 使用 Activity 自帶的 startActivity

    方法:

//第一個是Activity的startActivity方法
Intent intent= new Intent(MainActivity.this ,,SecondActivity.class) ;
startActivity(intent);
           
  1. 還有一種寫法是使用Context的startActivity 方法,我們使用 getApplicationContext方法擷取Context 對象,如下所示:
第二個是Context的startActivity方法
Intent intent= new Intent(MainActivity.this ,,SecondActivity.class) ;
getApplicationContext() .startActivity(intent );
           

兩種寫法殊途同歸,它們都是在App 程序中通知AMS要啟動哪個Activity。Context 内部的 startActivity 方法,其實也是在調用 Instrumentation 的execStartActivity 方法,後面的流程,兩種方式就都是一樣的了 。

1 Hook Activity的startActivity方法

public class InstrumentationProxy extends Instrumentation {

    private static final String TAG = "InstrumentationProxy";

    Instrumentation mInstrumentation;

    public InstrumentationProxy(Instrumentation instrumentation) {
        mInstrumentation = instrumentation;
    }

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

        Log.d(TAG,  "Hook 成功"  + " who :" + who);

        // 開始調用原始的方法, 調不調用随你,但是不調用的話, 所有的startActivity都失效了.
        // 由于這個方法是隐藏的,是以需要使用反射調用;首先找到這個方法
        Class[] pareTyples = {Context.class, IBinder.class, IBinder.class,
                Activity.class, Intent.class, int.class, Bundle.class};

        Object[] pareVaules = {who, contextThread, token, target,
                intent, requestCode, options};

        String methodName = "execStartActivity";

        try {
            Method execStartActivity = mInstrumentation.getClass().getDeclaredMethod(methodName, pareTyples);
            execStartActivity.setAccessible(true);
            return (ActivityResult) execStartActivity.invoke(mInstrumentation, pareVaules);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}
           
public class Hook51MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hook51_main);

        Class clazz = Activity.class;

        try {
            Field field = clazz.getDeclaredField("mInstrumentation");
            field.setAccessible(true);
            Instrumentation mInstrumentation = (Instrumentation) field.get(this);
            Instrumentation instrumentationProxy = new InstrumentationProxy(mInstrumentation);
            field.set(this, instrumentationProxy);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void toHook51SecondActivity(View view) {
        Intent intent = new Intent(Hook51MainActivity.this,Hook51SecondActivity.class);
        startActivity(intent);
    }
}
           

調用toHook51SecondActivity()方法後,輸出:

InstrumentationProxy: Hook 成功 who :[email protected]
           

2 Hook Context 的 startActivity 方法

public class Hook51MainActivity extends AppCompatActivity {

    @Override
    protected void attachBaseContext(Context context) {
        super.attachBaseContext(context);
        try {
            // 在這裡進行Hook
            String className = "android.app.ActivityThread";
            String methodName = "currentActivityThread";
            Class[] pareTyples = new Class[]{};
            Object[] pareVaules = new Object[]{};
            Class clazz = Class.forName(className);
            Method method = clazz.getDeclaredMethod(methodName, pareTyples);
            method.setAccessible(true);
            Object currentActivityThread = method.invoke(null, pareVaules);// 先擷取到目前的ActivityThread對象

            String filedName = "mInstrumentation";
            Field field = clazz.getDeclaredField(filedName);
            field.setAccessible(true);
            Instrumentation mInstrumentation = (Instrumentation) field.get(currentActivityThread); // 拿到原始的 mInstrumentation字段
            // 建立代理對象
            Instrumentation instrumentationProxy = new InstrumentationProxy(mInstrumentation);
            // 偷梁換柱
            field.set(currentActivityThread, instrumentationProxy);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hook51_main);
    }

    public void toHook51SecondActivity(View view) {
        Intent intent = new Intent(Hook51MainActivity.this,Hook51SecondActivity.class);
        intent.addFlags(Intent . FLAG_ACTIVITY_NEW_TASK) ;
        getApplicationContext (). startActivity (intent );
    }
}

           

輸出:

InstrumentationProxy: Hook 成功 who :[email protected]
           

3 Hook startActivity 總結

Hook Context 的 startActivity 方法和 Hook Activity 的 startActivity 方法最大的差別就是替換的 Instrumentation 不同 , 前者是 ActivityThread 中的 Instrumentation ,後者是 Activity中的Instrumentation。另外有一點需要注意的是,在 MainActivity 的onCreate 方法中進行Instrumentation替換的,未必是最佳的替換時間點 ,是以在 Activity 的 attachBaseContext 方法中進行Instrumentation 替換,因為這個方法要先于 Activity 的 onCreate 方法被調用。講到這裡,我們知道了如何使用代理來 Hook startActivity 方法,簡單說就是找到 Hook 點,再用代理對象來替換 Hook 點 。