首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android Hook告诉你 如何启动未注册的Activity

Android Hook告诉你 如何启动未注册的Activity

作者头像
黄林晴
发布2019-05-14 10:22:00
1K0
发布2019-05-14 10:22:00
举报
文章被收录于专栏:代码男人代码男人

前言

Android Hook 插件化其实已经不是什么新鲜的技术了,不知你有没有想过,支付宝中那么多小软件:淘票票 ,火车票等软件,难道是支付宝这个软件自己编写的吗?那不得写个十年,软件达到几十G,但是并没有,玩游戏时那么多的皮肤包肯定时用户使用哪个就下载哪个皮肤包。

一 未在配置文件中注册的Activity可以启动吗?

       从0学的时候就知道Activity必须在配置文件中注册,否则无法启动且报错。但是Hook告诉你的是,未在配置文件中注册Activity是可以启动的,惊不惊喜?意不意外?

通过本文你可以学到:

1.通过对startActivity方法进行Hook,实现为startActivity方法添加日志。

    1.1 通过对Instrumentation进行Hook

    1.2 通过对AMN进行Hook

2.如何启动一个未在配置文件中注册的Activity实现插件化

本片文章基础建立在 Java反射机制App启动流程解析,建议不太了解的小伙伴可以先移步至这两篇文章。

二 对startActivity方法进行Hook

    通过对查阅startActivity的源码可以看出startActivity最终都会走到startActivityFoResult方法中

public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
    if(this.mParent == null) {
        ActivityResult ar = this.mInstrumentation.execStartActivity(this, this.mMainThread.getApplicationThread(), this.mToken, this, intent, requestCode, options);
        if(ar != null) {
            this.mMainThread.sendActivityResult(this.mToken, this.mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData());
        }

        if(requestCode >= 0) {
            this.mStartedActivity = true;
        }
    } else if(options != null) {
        this.mParent.startActivityFromChild(this, intent, requestCode, options);
    } else {
        this.mParent.startActivityFromChild(this, intent, requestCode);
    }

}

通过mInstrumentation.execStartActivity调用(ps:详细的源码解析已在上篇文章中讲解),再看mInstrumentation.execStartActivity方法源码如下:

public Instrumentation.ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
    IApplicationThread whoThread = (IApplicationThread)contextThread;
    if(this.mActivityMonitors != null) {
        Object e = this.mSync;
        synchronized(this.mSync) {
            int N = this.mActivityMonitors.size();

            for(int i = 0; i < N; ++i) {
                Instrumentation.ActivityMonitor am = (Instrumentation.ActivityMonitor)this.mActivityMonitors.get(i);
                if(am.match(who, (Activity)null, intent)) {
                    ++am.mHits;
                    if(am.isBlocking()) {
                        return requestCode >= 0?am.getResult():null;
                    }
                    break;
                }
            }
        }
    }

    try {
        intent.setAllowFds(false);
        intent.migrateExtraStreamToClipData();
        int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);
        checkStartActivityResult(var16, intent);
    } catch (RemoteException var14) {
        ;
    }

    return null;
}

最终会交给 int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent,...处理,所以如果我们想对startActivity方法进行Hook,可以从这两个地方入手(其实不止这两个地方,我们只讲解着两个地方,下面使用的反射封装类也在上篇文章中给出)。

  •  2.1 对mInstrumentation进行Hook

  在Activity.class类中定义了私有变量

private Instrumentation mInstrumentation;

我们首先要做的就是修改这个私有变量的值,在执行方法前打印一行日志,首先我们通过反射来获取这一私有变量。

Instrumentation instrumentation = (Instrumentation) Reflex.getFieldObject(Activity.class,MainActivity.this,"mInstrumentation");

我们要做的是将这个Instrumentation替换成我们自己的Instrumentation,所以下面我们新建MyInstrumentation继承自MyInstrumentation,并且MyInstrumentation的execStartActivity方法不变。

public class MyInstrumentation extends Instrumentation {

    private Instrumentation instrumentation;

    public MyInstrumentation(Instrumentation instrumentation) {
        this.instrumentation = instrumentation;
    }

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


        Log.d("-----","啦啦啦我是hook进来的!");
        Class[] classes = {Context.class,IBinder.class,IBinder.class,Activity.class,Intent.class,int.class,Bundle.class};
        Object[] objects = {who,contextThread,token,target,intent,requestCode,options};
        Log.d("-----","啦啦啦我是hook进来的!!");
        return (ActivityResult) Reflex.invokeInstanceMethod(instrumentation,"execStartActivity",classes,objects);

    }

我们直接通过反射调用这个方法 参数就是 Class[] classes = {Context.class,IBinder.class,IBinder.class,Activity.class,Intent.class,int.class,Bundle.class}与方法名中一致

(ActivityResult) Reflex.invokeInstanceMethod(instrumentation,"execStartActivity",classes,objects)

如果我们这里不调用它本身的execStartActivity方法的话,那么startActivity就无效了。

然后我们将自定义的替换为原来的Instrumentation

Reflex.setFieldObject(Activity.class,this,"mInstrumentation",instrumentation1);

完整代码就是

Instrumentation instrumentation = (Instrumentation) Reflex.getFieldObject(Activity.class,MainActivity.this,"mInstrumentation");
MyInstrumentation instrumentation1 = new MyInstrumentation(instrumentation);
Reflex.setFieldObject(Activity.class,this,"mInstrumentation",instrumentation1);

   运行日志如下:

这个时候我们就成功的Hook了startActivity方法

2.2 对AMN进行Hook

execStartActivity方法最终会走到ActivityManagerNative.getDefault().startActivity方法

try {
    intent.setAllowFds(false);
    intent.migrateExtraStreamToClipData();
    int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);
    checkStartActivityResult(var16, intent);
} catch (RemoteException var14) {
    ;
}

你可能会说上面说过了啊,替换ActivityManagerNative.getDefault(),重写startActivity方法,我们来看ActivityManagerNative.getDefault()

public static IActivityManager getDefault() {
    return (IActivityManager)gDefault.get();
}
public final T get() {
    synchronized(this) {
        if(this.mInstance == null) {
            this.mInstance = this.create();
        }

        return this.mInstance;
    }
}

可以看出IActivityManager是一个接口,gDefault.get()返回的是一个泛型,上述方案我们无法入手,所以我们这里要用动态代理方案

我们定义一个AmsHookHelperUtils类,在AmsHookHelperUtils类中处理反射代码

gDefault是个final静态类型的字段,首先我们获取gDefault字段

Object gDefault = Reflex.getStaticFieldObject("android.app.ActivityManagerNative","gDefault");

gDefault是 Singleton<IActivityManager>类型的对象,Singleton是一个单例模式

public abstract class Singleton<T> {
    private T mInstance;

    public Singleton() {
    }

    protected abstract T create();

    public final T get() {
        synchronized(this) {
            if(this.mInstance == null) {
                this.mInstance = this.create();
            }

            return this.mInstance;
        }
    }
}

接下里我们来取出mInstance字段

Object mInstance = Reflex.getFieldObject("android.util.Singleton",gDefault,"mInstance");

然后创建一个代理对象

Class<?> classInterface = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(classInterface.getClassLoader(),
        new Class<?>[]{classInterface},new AMNInvocationHanlder(mInstance));

我们的代理对象就是new AMNInvocationHanlder(mInstance),(ps:代理模式分为静态代理和动态代理,如果对代理模式不了解可以百度一波,也可以关注我,等待我的代理模式相关文章)

public class AMNInvocationHanlder implements InvocationHandler {


    private String actionName = "startActivity";

    private Object target;

    public AMNInvocationHanlder(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if (method.getName().equals(actionName)){
            Log.d("---","啦啦啦我是hook AMN进来的");
            return method.invoke(target,args);
        }

        return method.invoke(target,args);
    }
}

所有的代理类都要实现InvocationHandler接口,在invoke方法中method.invoke(target,args);表示的就是 执行被代理对象所对应的方法。因为AMN Singleton做的事情比较多,所以这里只对startActivity方法hook

if (method.getName().equals(actionName)){
    Log.d("---","啦啦啦我是hook AMN进来的");
    return method.invoke(target,args);
}

然后我们将gDefault字段替换为我们的代理类

Reflex.setFieldObject("android.util.Singleton",gDefault,"mInstance",proxy);

AmsHookHelperUtils方法整体如下:

public class AmsHookHelperUtils {

    public static void hookAmn() throws ClassNotFoundException {
        Object gDefault = Reflex.getStaticFieldObject("android.app.ActivityManagerNative","gDefault");
        Object mInstance = Reflex.getFieldObject("android.util.Singleton",gDefault,"mInstance");

        Class<?> classInterface = Class.forName("android.app.IActivityManager");
        Object proxy = Proxy.newProxyInstance(classInterface.getClassLoader(),
                new Class<?>[]{classInterface},new AMNInvocationHanlder(mInstance));
        Reflex.setFieldObject("android.util.Singleton",gDefault,"mInstance",proxy);
    }
}

我们调用AmsHookHelperUtils.hookAmn();然后启动一个新的Activity,运行日志如下:

这样我们就成功Hook了AMN的getDefault方法。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年04月30日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一 未在配置文件中注册的Activity可以启动吗?
  • 二 对startActivity方法进行Hook
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档