--摘自《android插件化开发指南》
1.Activity的startActivity和Context的startActivity都是在app进程中通知AMS要启动哪个Activity,都是调用Instrumentation的execStartActivity。
方案一:
一般所有Activity都有一个基类BaseActivity,在BaseActivity中重写startActivityForResult
方案二:
Activity的mInstumentation进行Hook
public class EvilInstrumentation extends Instrumentation {
private static final String TAG = "EvilInstrumentation";
// ActivityThread中原始的对象, 保存起来
Instrumentation mBase;
public EvilInstrumentation(Instrumentation base) {
mBase = base;
}
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
Log.d(TAG, "XXX到此一游!");
// 开始调用原始的方法, 调不调用随你,但是不调用的话, 所有的startActivity都失效了.
// 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法
Class[] p1 = {Context.class, IBinder.class,
IBinder.class, Activity.class,
Intent.class, int.class, Bundle.class};
Object[] v1 = {who, contextThread, token, target,
intent, requestCode, options};
return (ActivityResult) RefInvoke.invokeInstanceMethod(
mBase, "execStartActivity", p1, v1);
}
}
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Instrumentation mInstrumentation = (Instrumentation) RefInvoke.getFieldObject(Activity.class, this, "mInstrumentation");
Instrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation);
RefInvoke.setFieldObject(Activity.class, this, "mInstrumentation", evilInstrumentation);
Button tv = new Button(this);
tv.setText("测试界面");
setContentView(tv);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
}
如果把这段Hook代码转移到BaseActivity中,那所有继承的Activity的mInstrumentation就都被Hook了
方案三:
对AMN的getDefault方法进行Hook
class MockClass1 implements InvocationHandler {
private static final String TAG = "MockClass1";
Object mBase;
public MockClass1(Object base) {
mBase = base;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {
Log.e("bao", method.getName());
return method.invoke(mBase, args);
}
return method.invoke(mBase, args);
}
}
public class AMSHookHelper {
public static final String EXTRA_TARGET_INTENT = "extra_target_intent";
public static void hookAMN() throws ClassNotFoundException,
NoSuchMethodException, InvocationTargetException,
IllegalAccessException, NoSuchFieldException {
//获取AMN的gDefault单例gDefault,gDefault是final静态的
Object gDefault = RefInvoke.getStaticFieldObject("android.app.ActivityManagerNative", "gDefault");
// gDefault是一个 android.util.Singleton<T>对象; 我们取出这个单例里面的mInstance字段
Object mInstance = RefInvoke.getFieldObject("android.util.Singleton", gDefault, "mInstance");
// 创建一个这个对象的代理对象MockClass1, 然后替换这个字段, 让我们的代理对象帮忙干活
Class<?> classB2Interface = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class<?>[] { classB2Interface },
new MockClass1(mInstance));
//把gDefault的mInstance字段,修改为proxy
RefInvoke.setFieldObject("android.util.Singleton", gDefault, "mInstance", proxy);
}
}
public class MainActivity extends Activity {
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
try {
AMSHookHelper.hookAMN();
} catch (Throwable throwable) {
throw new RuntimeException("hook failed", throwable);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button button = new Button(this);
button.setText("启动TargetActivity");
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, TargetActivity.class);
startActivity(intent);
}
});
setContentView(button);
}
}
如果在自定义Application的attachBaseContext方法中执行AMSHookHelper的hookAMN方法,将一劳永逸
方案四:
对H类的mCallback字段进行Hook
public class MockClass2 implements Handler.Callback {
Handler mBase;
public MockClass2(Handler base) {
mBase = base;
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
// ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100
// 本来使用反射的方式获取最好, 这里为了简便直接使用硬编码
case 100:
handleLaunchActivity(msg);
break;
}
mBase.handleMessage(msg);
return true;
}
private void handleLaunchActivity(Message msg) {
// 这里简单起见,直接取出TargetActivity;
Object obj = msg.obj;
Log.d("baobao", obj.toString());
}
}
public class HookHelper {
public static void attachBaseContext() throws Exception {
// 先获取到当前的ActivityThread对象
Object currentActivityThread = RefInvoke.getStaticFieldObject("android.app.ActivityThread", "sCurrentActivityThread");
// 由于ActivityThread一个进程只有一个,我们获取这个对象的mH
Handler mH = (Handler) RefInvoke.getFieldObject(currentActivityThread, "mH");
//把Handler的mCallback字段,替换为new MockClass2(mH)
RefInvoke.setFieldObject(Handler.class, mH, "mCallback", new MockClass2(mH));
}
}
问:为什么不直接Hook了ActivityThread的mH字段?
因为mH是H类型的,H类不对外暴露,也不是接口类型。所以既没办法伪造,Proxy.newProxyInstance也派不上用场
方案五:
再对Instrumentation字段进行Hook
public class EvilInstrumentation extends Instrumentation {
private static final String TAG = "EvilInstrumentation";
// ActivityThread中原始的对象, 保存起来
Instrumentation mBase;
public EvilInstrumentation(Instrumentation base) {
mBase = base;
}
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Log.d(TAG, "包建强到此一游!");
return mBase.newActivity(cl, className, intent);
}
public void callActivityOnCreate(Activity activity, Bundle bundle) {
Log.d(TAG, "到此一游!");
// 开始调用原始的方法, 调不调用随你,但是不调用的话, 所有的startActivity都失效了.
// 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法
Class[] p1 = {Activity.class, Bundle.class};
Object[] v1 = {activity, bundle};
RefInvoke.invokeInstanceMethod(
mBase, "callActivityOnCreate", p1, v1);
}
}
public class HookHelper {
public static void attachContext() throws Exception{
// 先获取到当前的ActivityThread对象
Object currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread", "currentActivityThread");
// 拿到原始的 mInstrumentation字段
Instrumentation mInstrumentation = (Instrumentation) RefInvoke.getFieldObject(currentActivityThread, "mInstrumentation");
// 创建代理对象
Instrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation);
// 偷梁换柱
RefInvoke.setFieldObject(currentActivityThread, "mInstrumentation", evilInstrumentation);
}
}
方案六:
对ActivityThread的mInstrumentation字段进行Hook
public class EvilInstrumentation extends Instrumentation {
private static final String TAG = "EvilInstrumentation";
// ActivityThread中原始的对象, 保存起来
Instrumentation mBase;
public EvilInstrumentation(Instrumentation base) {
mBase = base;
}
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
Log.d(TAG, "XXX到此一游!");
// 开始调用原始的方法, 调不调用随你,但是不调用的话, 所有的startActivity都失效了.
// 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法
Class[] p1 = {Context.class, IBinder.class,
IBinder.class, Activity.class,
Intent.class, int.class, Bundle.class};
Object[] v1 = {who, contextThread, token, target,
intent, requestCode, options};
return (ActivityResult) RefInvoke.invokeInstanceMethod(
mBase, "execStartActivity", p1, v1);
}
}
public class HookHelper {
public static void attachContext() throws Exception{
// 先获取到当前的ActivityThread对象
Object currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread", "currentActivityThread");
// 拿到原始的 mInstrumentation字段
Instrumentation mInstrumentation = (Instrumentation) RefInvoke.getFieldObject(currentActivityThread, "mInstrumentation");
// 创建代理对象
Instrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation);
// 偷梁换柱
RefInvoke.setFieldObject(currentActivityThread, "mInstrumentation", evilInstrumentation);
}
}
只有方案三,能同时满足Activity的startActivity()和Context的startActivity()方法
***启动没有在AndroidManifest.xml中申明的Activity
Hook1:
将Activity替换为一个在AndroidManifest中申明的StubActivity,绕过AMS的检查(这里选用对AMN进行Hook)
class MockClass1 implements InvocationHandler {
private static final String TAG = "MockClass1";
Object mBase;
public MockClass1(Object base) {
mBase = base;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e("bao", method.getName());
if ("startActivity".equals(method.getName())) {
// 只拦截这个方法
// 替换参数, 任你所为;甚至替换原始Activity启动别的Activity偷梁换柱
// 找到参数里面的第一个Intent 对象
Intent raw;
int index = 0;
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Intent) {
index = i;
break;
}
}
raw = (Intent) args[index];
Intent newIntent = new Intent();
// 替身Activity的包名, 也就是我们自己的包名
String stubPackage = raw.getComponent().getPackageName();
// 这里我们把启动的Activity临时替换为 StubActivity
ComponentName componentName = new ComponentName(stubPackage, StubActivity.class.getName());
newIntent.setComponent(componentName);
// 把我们原始要启动的TargetActivity先存起来
newIntent.putExtra(AMSHookHelper.EXTRA_TARGET_INTENT, raw);
// 替换掉Intent, 达到欺骗AMS的目的
args[index] = newIntent;
Log.d(TAG, "hook success");
return method.invoke(mBase, args);
}
return method.invoke(mBase, args);
}
}
public class AMSHookHelper {
public static final String EXTRA_TARGET_INTENT = "extra_target_intent";
/**
* Hook AMS
* 主要完成的操作是 "把真正要启动的Activity临时替换为在AndroidManifest.xml中声明的替身Activity",进而骗过AMS
*/
public static void hookAMN() throws ClassNotFoundException,
NoSuchMethodException, InvocationTargetException,
IllegalAccessException, NoSuchFieldException {
//获取AMN的gDefault单例gDefault,gDefault是final静态的
Object gDefault = RefInvoke.getStaticFieldObject("android.app.ActivityManagerNative", "gDefault");
// gDefault是一个 android.util.Singleton<T>对象; 我们取出这个单例里面的mInstance字段
Object mInstance = RefInvoke.getFieldObject("android.util.Singleton", gDefault, "mInstance");
// 创建一个这个对象的代理对象MockClass1, 然后替换这个字段, 让我们的代理对象帮忙干活
Class<?> classB2Interface = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class<?>[] { classB2Interface },
new MockClass1(mInstance));
//把gDefault的mInstance字段,修改为proxy
Class class1 = gDefault.getClass();
RefInvoke.setFieldObject("android.util.Singleton", gDefault, "mInstance", proxy);
}
/**
* 由于之前我们用替身欺骗了AMS; 现在我们要换回我们真正需要启动的Activity
* 不然就真的启动替身了, 狸猫换太子...
* 到最终要启动Activity的时候,会交给ActivityThread 的一个内部类叫做 H 来完成
* H 会完成这个消息转发; 最终调用它的callback
*/
public static void hookActivityThread() throws Exception {
// 先获取到当前的ActivityThread对象
Object currentActivityThread = RefInvoke.getStaticFieldObject("android.app.ActivityThread", "sCurrentActivityThread");
// 由于ActivityThread一个进程只有一个,我们获取这个对象的mH
Handler mH = (Handler) RefInvoke.getFieldObject(currentActivityThread, "mH");
//把Handler的mCallback字段,替换为new MockClass2(mH)
RefInvoke.setFieldObject(Handler.class, mH, "mCallback", new MockClass2(mH));
}
}
Hook2:
在即将启动时,把StubActivity替换为原先的Activity(这里的方案是对H类的mCallback字段进行Hook或者对ActivityThread的mInstrumentation进行Hook)
class MockClass2 implements Handler.Callback {
Handler mBase;
public MockClass2(Handler base) {
mBase = base;
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
// ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100
// 本来使用反射的方式获取最好, 这里为了简便直接使用硬编码
case 100:
handleLaunchActivity(msg);
break;
}
mBase.handleMessage(msg);
return true;
}
private void handleLaunchActivity(Message msg) {
// 这里简单起见,直接取出TargetActivity;
Object obj = msg.obj;
// 把替身恢复成真身
Intent intent = (Intent) RefInvoke.getFieldObject(obj, "intent");
Intent targetIntent = intent.getParcelableExtra(AMSHookHelper.EXTRA_TARGET_INTENT);
intent.setComponent(targetIntent.getComponent());
}
}
然后第一种:对H类的mCallback字段进行Hook
public class HookHelper {
public static final String EXTRA_TARGET_INTENT = "extra_target_intent";
public static void attachBaseContext() throws Exception {
// 先获取到当前的ActivityThread对象
Object currentActivityThread = RefInvoke.getStaticFieldObject("android.app.ActivityThread", "sCurrentActivityThread");
// 由于ActivityThread一个进程只有一个,我们获取这个对象的mH
Handler mH = (Handler) RefInvoke.getFieldObject(currentActivityThread, "mH");
//把Handler的mCallback字段,替换为new MockClass2(mH)
RefInvoke.setFieldObject(Handler.class, mH, "mCallback", new MockClass2(mH));
}
}
第二种:对ActivityThread的mInstrumentation进行Hook
public class HookHelper {
public static final String EXTRA_TARGET_INTENT = "extra_target_intent";
public static void attachContext() throws Exception{
// 先获取到当前的ActivityThread对象
Object currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread", "currentActivityThread");
// 拿到原始的 mInstrumentation字段
Instrumentation mInstrumentation = (Instrumentation) RefInvoke.getFieldObject(currentActivityThread, "mInstrumentation");
// 创建代理对象
Instrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation);
// 偷梁换柱
RefInvoke.setFieldObject(currentActivityThread, "mInstrumentation", evilInstrumentation);
}
}
public class EvilInstrumentation extends Instrumentation {
private static final String TAG = "EvilInstrumentation";
// 替身Activity的包名, 也就是我们自己的包名
String packageName = "jianqiang.com.hook1";
// ActivityThread中原始的对象, 保存起来
Instrumentation mBase;
public EvilInstrumentation(Instrumentation base) {
mBase = base;
}
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
// 把替身恢复成真身
Intent rawIntent = intent.getParcelableExtra(HookHelper.EXTRA_TARGET_INTENT);
if(rawIntent == null) {
return mBase.newActivity(cl, className, intent);
}
String newClassName = rawIntent.getComponent().getClassName();
return mBase.newActivity(cl, newClassName, rawIntent);
}
}
以上可以欺骗AMS
欢迎关注我的微信公众号:安卓圈