Android程序员日常学习:Android Hook技术小实践

136 篇文章 1 订阅
98 篇文章 1 订阅

概述

在学习Android插件化的过程中有用到Hook相关技术,本篇文章对Hook相关技术做也给简单的介绍,并写两个小Demo,当你了解了Hook之后可能会对你以后的碰到问题时多了一个解题思路

定义

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SRlPGJ7s-1573798185565)(https://upload-images.jianshu.io/upload_images/3117364-ed16679706bcec55.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

Hook单词的意思就是钩子,那我们在什么时候用到这个钩子呢,如上图所示,在一个事件或者动作执行的过程中,截获相关事件或者动作,加入自己的代码或者替换装自己的代理对象,这就叫Hook

Hook的原理

本文主要是采用java反射机制拿到要执行的对象或者方法就行修改或者替换

**关注点:**在hook的时候我们首先需要找到要Hook的对象,什么样的对象比较好Hook呢,那就是单例和静态变量,单例和静态变量在进程中不容易发生变化,相对容易被定位到,二普通象则比价容易发生变化(随时有可能被销毁),。我们根据这个原则找到所谓的Hook点

以上就是我对Hook的理解,且是还挺简单的,但实践是检验真理的唯一标准,下面我会写两个小Demo

Demo1

本例子Hook的是一个工具类

/**
 * 打印机工具类,提供黑白打印和彩色打印
 */
public class PrintUtil {
    private static IPrint colorPrint = new ColorPrint(); //彩色打印机
    private static IPrint blackWhitePrint = new BlackWhitePrint(); //黑白打印机

    public static void colorPrint(String content){
        colorPrint.print(content);
    }

    public static void blackWhitePrint(String content){
        blackWhitePrint.print(content);
    }

}
复制代码

工具类如上

    private void operate4(){
//        HookHelper.hookPrint();
        PrintUtil.blackWhitePrint("黑白内容");
        PrintUtil.colorPrint("彩色内容");
    }
复制代码

image.png

正常结果如上 ,下面我们对PrintUtil进行hook ,首先我们先找Hook点,在PrintUtil中有两个静态变量,这就是我们要找的Hook点 具体代码如下

/**
     * 对printUtil进行hook处理
     */
    public static void hookPrint(){
        try {
            Class<?> printClass = Class.forName("com.example.shiyagang.myapplication.util.PrintUtil");
            Field colorPrintField= printClass.getDeclaredField("colorPrint");
            Field blackWhitePrintField = printClass.getDeclaredField("blackWhitePrint");
            colorPrintField.setAccessible(true);
            blackWhitePrintField.setAccessible(true);
            colorPrintField.set(null,new BlackWhitePrint());
            blackWhitePrintField.set(null,new ColorPrint());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
复制代码

我们通过反射对PrintUtil的两个静态变量进行替换

替换完执行结果如下

image.png

彩色打印机打出了黑白内容,我们成功了,嘿嘿

Demo2

这个例子我们在context.startActivity的调用链,找到相关的hook点进行替换。我们首先分下context.startActivity的流程,Context.startActivity其实走到了ContextImpl的startActivity

[图片上传中…(image-318cbb-1573653549464-1)]

如上图所示最终调用了ActivityThread类的mInstrumentation成员的execStartActivity方法;注意到,ActivityThread 实际上是主线程,而主线程一个进程只有一个,因此这里是一个良好的Hook点

  • 我们要拿到ActivityThread的mInstrumentation ,首先得拿到ActivityThread的实例
  • ActivityThread类里面有一个静态方法currentActivityThread可以帮助我们拿到ActivityThread的实例

通过以上步骤我们就能进行相关hook了

 /**
     * 对activityThread进行Hook
     * 
     */
    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的代理对象如下:

/**
 * Instrumentation 的静态代理类
 */
public class EvilInstrumentation extends Instrumentation {
    private static final String TAG = EvilInstrumentation.class.getSimpleName();

    // 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.e(TAG, "我们Hook了 Activity的启动流程");
        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("出问题了,去适配吧");
        }
    }
}
复制代码

下面我们看下Activity的代码 ,我们在attachBaseContext中进行Hook

private void operate3(){
        Intent intent = new Intent(getApplicationContext(),SecondActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        getApplicationContext().startActivity(intent);
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        try {
            // 在这里进行Hook
            HookHelper.attachContext();
        } catch (Exception e) {
            e.printStackTrace();![整合资料.jpg](https://upload-images.jianshu.io/upload_images/3117364-779ed9dc78882285.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

        }
    }
复制代码

image.png

入上图所示我们已经成功了,我们在这只是打印了一个日志,当然你可以干任何事情

总结

到此已经对Hook做了简单的介绍

  • 我们需要先找到Hook点 ,静态变量和单例比较好Hook
  • 植入我们的代码,可以采用代理的方式进行植入
  • 进行偷梁换柱

想要Android学习PDF内容,可以【点这里】

image.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Plc27Jf2-1573798185568)(https://upload-images.jianshu.io/upload_images/3117364-dd1e365a37ab2b65.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值