--摘自《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

欢迎关注我的微信公众号:安卓圈

startActivity进行Hook的更多相关文章

  1. 小白也能看懂的插件化DroidPlugin原理(三)-- 如何拦截startActivity方法

    前言:在前两篇文章中分别介绍了动态代理.反射机制和Hook机制,如果对这些还不太了解的童鞋建议先去参考一下前两篇文章.经过了前面两篇文章的铺垫,终于可以玩点真刀实弹的了,本篇将会通过 Hook 掉 s ...

  2. 插件开发之360 DroidPlugin源码分析(四)Activity预注册占坑

    请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52258434 在了解系统的activity,service,broa ...

  3. [Hook] 免root,自己进程内,startActivity hook的几种姿势

    首先关于startActivity 我们平时会经常使用到 在activity内 直接startActivity(intent) 其实这里还有一种start方式 我们可能没怎么用过 getApplica ...

  4. Android Hook技术

    原文:http://blog.csdn.net/u011068702/article/details/53208825 附:Android Hook 全面入侵监听器 第一步.先爆项目demo照片,代码 ...

  5. 小白也能看懂的插件化DroidPlugin原理(二)-- 反射机制和Hook入门

    前言:在上一篇博文<小白也能看懂的插件化DroidPlugin原理(一)-- 动态代理>中详细介绍了 DroidPlugin 原理中涉及到的动态代理模式,看完上篇博文后你就会发现原来动态代 ...

  6. 代理与hook

    参考:Java 动态代理 代理是什么 为什么需要代理呢?其实这个代理与日常生活中的“代理”,“中介”差不多:比如你想海淘买东西,总不可能亲自飞到国外去购物吧,这时候我们使用第三方海淘服务比如惠惠购物助 ...

  7. Hook技术--Activity的启动过程的拦截

    1.寻找Hook点的原则 Android中主要是依靠分析系统源码类来做到的,首先我们得找到被Hook的对象,我称之为Hook点:什么样的对象比较好Hook呢?自然是容易找到的对象.什么样的对象容易找到 ...

  8. [Hook] 免root,自己进程内,binder hook (ClipboardManager)

    cp from : http://weishu.me/2016/02/16/understand-plugin-framework-binder-hook/ Android系统通过Binder机制给应 ...

  9. Android插件化原理解析——Hook机制之动态代理

    转自 http://weishu.me/2016/01/28/understand-plugin-framework-proxy-hook/ 使用代理机制进行API Hook进而达到方法增强是框架的常 ...

随机推荐

  1. iOS UIDatePicker设置为中文的方法

    UIDatePicker *datePicker = [[UIDatePicker alloc] initWithFrame:CGRectMake(0, 20, 200, 30)]; datePick ...

  2. vuforia unity 识别图片出模型

    ARCamera设置: 然后设置ImageTarge

  3. Confluence 6 外部参考

    一个外部参考的意思是任何站点链接到你 Confluence 的实例.任何时候当 Confluence 的用户单击这个外部链接的时候,Confluence 可以记录这次单击为参考. 在默认的情况下,外部 ...

  4. Confluence 6 在数据源连接中启用校验查询

    确定 Confluence 在数据库连接池中校验数据库连接: 停止 Confluence. 编辑 <installation-directory>/conf/server.xml 文件(或 ...

  5. 关于在CentOS上,绘图丢失部分中文字的问题

    官方的system.drawing.common 第三方的 zkweb.system.drawing,都用的是libgdiplus 只要是自己编译libgdiplus,都会有这个问题, 问题 : 这里 ...

  6. HTML&javaSkcript&CSS&jQuery&ajax(十)

    HTML 1.SVG直接嵌入HTML网页 ,SVG 是使用XML描述2D图像的语言,Canvas通过JavaScript来绘制2D <svg xmlns="http://www.w3. ...

  7. react 为组件添加样式

    width/height/fontSize:可以直接写数字: style={ width:200,height:200 } 其他带数字的可以:数字+'px' style={ lineHeight:20 ...

  8. django----利用Form 实现两次密码输入是否一样 ( 局部钩子和全局钩子 )

    from django import forms # 导入表单模块 from django.core.exceptions import ValidationError class RegisterF ...

  9. ruby安装sass和compass步骤

    依赖ruby,所以需要安装Ruby 如何安装Ruby呢?在windows下通过RubyInstaller来安装,安装过程中需要选择第二项 1.ruby -v 2.gem install sass (如 ...

  10. Appium 九宫格 手势解锁

    分析九宫格定位 整个九宫格是一个 view   self.driver.find_element_by_id("com.elc:id/gesturepwd_create_lockview&q ...