Android hook技术浅析与小实践

本文介绍了Android的hook技术,解释了沙箱机制、hook的含义和目的,以及代理的概念和分类。详细探讨了反射机制,并阐述了hook技术的分类,包括API hook和系统消息hook。文章通过一个小实践展示了如何进行API hook,涉及找到hook点、选择代理方式以及替换对象的过程,强调了hook技术需要结合反射、代理和Android底层知识来掌握。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么是沙箱机制?

沙箱是一个虚拟系统程序,沙箱提供的环境相对于每一个运行的程序的进程空间都是独立的,程序的运行互不干扰,而且不会对现有的系统产生影响。

二、什么是hook?

hook的意思是勾住。比如A发送消息给B,在消息过去之前,可以先把消息勾住,不让其传递,你可以先执行你的操作(或者说先执行你的钩子函数)。
专业的说法就是:hook技术,能够改变API执行的结果,将系统的API函数执行重定向。

三、为什么需要hook?

android系统的沙箱机制使我们不能通过一个程序改变其他程序的某些行为,但是hook技术正好可以解决此类问题。

四、代理是什么?

举例生活中,比如我经常需要买一些香港的东西,又不太现实每次自己亲自去,只能找一些代购帮我们去买。那代购就比较坑了,不仅坑你的钱,还可能坑你的货品,给你带回了假货回来。

五、代理分类?

1.静态代理
2.动态代理

六、反射机制?

Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。

七、hook技术分类?

分类1 —— API hook:
分类2 —— 系统消息hook:

八、小实践

以下我们对API hook举一个详细的例子:

步骤
1.寻找hook点。一般是静态变量或单例对象,尽量hook public的对象和方法,减轻因非public而导致的各版本不一致带来的工作。
2.选择合适的代理方式。动态代理适用于接口。
3.偷梁换柱。使用反射机制用代理对象替换掉被hook的对象

举例 hook startActivity:
1.寻找hook点。
》》对于Context.startActivity,由于Context的实现类为ContextImpl,因此直接分析ContextImpl类的startActivity()。

    @Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();
        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
                && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }

》》这里,实际上使用了ActivityThread类的mInstrumentation成员的execStartActivity方法;而ActivityThread 实际上是主线程,因为主线程一个进程只有一个,所以这里是一个良好的Hook点。
》》Hook主线程对象:要将这个主线程对象里面的mInstrumentation替换成修改过的代理对象;要替换主线程对象里面的字段,得先拿到主线程对象的引用,如何获取呢? 》》ActivityThread类里面有一个静态方法currentActivityThread,通过它可以拿到这个对象类;但ActivityThread是一个隐藏类,需用反射去获取。

2.选择合适代理方式
》》拿到currentActivityThread后,要修改它的mInstrumentation字段为修改后的代理对象。因此需要先实现这个代理对象,由于JDK动态代理只支持接口,而这个Instrumentation是一个类,因此只能手写一个静态代理类,用来覆盖掉原始的方法。

3.偷梁换柱
》》采用反射直接修改即可。

完整代码如下:
HookUtil

public class HookUtil {
    public static void  attachContext() throws Exception {
        //先获取到当前的ActivityThread对象
        Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
        Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
        currentActivityThreadMethod.setAccessible(true);
        Object currentActivityThread = currentActivityThreadMethod.invoke(null);
        // 拿到原始的 mInstrumentation字段
        Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
        mInstrumentationField.setAccessible(true);
        Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);
        // 创建代理对象
        Instrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation);
        //偷梁换柱
        mInstrumentationField.set(currentActivityThread, evilInstrumentation);
    }
}

EvilInstrumentation

public class EvilInstrumentation extends Instrumentation {
    private static final String TAG = "EvilInstrumentation";

    Instrumentation mBase;

    public EvilInstrumentation(Instrumentation mBase) {
        this.mBase = mBase;
    }
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

        Log.d(TAG, " Hook之前 打印 execStartActivity");

        // 开始调用原始的方法, 调不调用随你,但是不调用的话, 所有的startActivity都失效了.
        // 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法
        try {
            Method execStartActivity = Instrumentation.class.getDeclaredMethod(
                    "execStartActivity",
                    Context.class, IBinder.class, IBinder.class, Activity.class,
                    Intent.class, int.class, Bundle.class);
            execStartActivity.setAccessible(true);
            return (ActivityResult) execStartActivity.invoke(mBase, who,
                    contextThread, token, target, intent, requestCode, options);
        } catch (Exception e) {
            throw new RuntimeException("do not support!");
        }
    }
}

AActivity

public class AActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Button tv = new Button(this);
        tv.setText("Test");

        setContentView(tv);
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(AActivity.this, BActivity.class);
                i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );//必须添加
                getApplication().startActivity(i);
            }
        });
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        try {
            HookUtil.attachContext();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

BActivity


public class BActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }
}

运行程序可以看到会打印信息:Hook之前 打印 execStartActivity

注意:
不添加setFlags(Intent.FLAG_ACTIVITY_NEW_TASK )会报错噢。
在这里插入图片描述

九、总结

hook技术涉及到的知识点主要有反射、代理及android的一些底层知识,需要花更多的时间去学习和总结才能较好地掌握好hook相关的内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值