--摘自《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. easyui datagrid 隔行变色

    easyui datagrid  隔行变色 一:实现样图 二:实现代码 $('#dataGrid').datagrid({ rowStyler:function(index,row){ if (row ...

  2. Confluence 6 手动运行和修改

    手动运行一个任务 希望手动运行一个计划任务,进入计划任务的列表中,找到你希望手动运行的计划任务,在这个计划任务的边上选择 运行(Run).这个计划任务将会马上执行. 不是所有的计划任务都可以手动运行的 ...

  3. index_select ,clamp,detach

    1.torch.clamp(input,min,max,out=None)-> Tensor 将input中的元素限制在[min,max]范围内并返回一个Tensor 2.index_selec ...

  4. Mybaits动态Sql

    什么是动态SQL? MyBatis的强大之处便是它的动态SQL,如果你使用JDBC那么在根据不同条件查询时,拼接SQL语句是多么的痛苦. 比如查询一个学生信息,可以根据学生的姓名,性别,班级,年龄,学 ...

  5. 【linux】centos6.9通过virtualenv安装python3.5

    参考:http://www.linuxidc.com/Linux/2015-08/121352.htm wget https://www.python.org/ftp/python/3.5.4/Pyt ...

  6. Burp Scanner Report

    1.使用application web 漏洞平台,除此之外还有一款类似的工具 叫做mulidata,其实mulidata功能更好一点. 2.配置之前的问题处理 安装之前要确认 自己之前是否安装过 Ap ...

  7. Django入门基础详解

    本次使用django版本2.1.2 安装django 安装最新版本 pip install django 安装指定版本 pip install django==1.10.1 查看本机django版本 ...

  8. spring cloud Hystix熔断机制--基本熔断配置和Fegin client熔断配置

    所谓的熔断机制和日常生活中见到电路保险丝是非常相似的,当出现了问题之后,保险丝会自动烧断,以保护我们的电器, 那么如果换到了程序之中呢? 当现在服务的提供方出现了问题之后整个的程序将出现错误的信息显示 ...

  9. Python零基础入门之Tkinter的对话框

    这篇博客主要是总结一下Tkinter中的对话框的使用,值得一提的是自从python3.0之后关于关于对话框的模块(messagebox.filedialog.colorchooser)都被收归到了tk ...

  10. Python迷宫游戏(基础版)

    # 画地图map_data = [ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 1, 1, 1, 1, 1, 1, 1, 1], [1, 2, 1, 0, 0, 0, ...