【Android 插件化】Hook 插件化框架 ( Hook 实现思路 | Hook 按钮点击事件 )

简介: 【Android 插件化】Hook 插件化框架 ( Hook 实现思路 | Hook 按钮点击事件 )

文章目录

Android 插件化系列文章目录

前言

一、Hook 实现思路

二、Hook 按钮点击事件

1、按钮点击事件

2、熟悉底层源码

3、获取 View 的 ListenerInfo mListenerInfo 成员

4、分析 Hook 点

5、反射 ListenerInfo 并设置新的 OnClickListener 监听器

三、完整代码示例

四、博客资源

前言

在上一篇博客 【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 ) 中 , 对 Hook 技术进行了简要介绍 , Android 中的 Hook 技术主要是通过


反射

代理模式 ( 动态代理 / 静态代理 )


实现的 ;



之所以使用 Hook 技术 , 是因为反射系统的源码时 , 会出现问题 , Google 官方对 Android 的反射进行了限制 ;


反射出现问题时 , 必须找到一个可以反射的反射点挂钩子 , 如在 A 位置无法进行反射 , 就在 B 位置挂 Hook 钩子 ;



最终要实现的是使用 Hook , 影响 Activity 的启动流程 , 在启动流程中注入我们想要的业务逻辑 , 干涉启动流程 , 以达到能启动插件包 APK 中的 Activity 的目的 ;






一、Hook 实现思路


Hook 点选择规则 : Hook 技术的关键是找好 Hook 点 , 将钩子挂在哪 , 勾住哪个方法 , 需要遵循一定的规则 :


静态变量 / 单例 : 优先选择 静态变量 , 单例对象 , 这些对象一旦创建 , 基本不会改变 , 容易定位 ;



Hook 点实现思路 :


① 查找 Hook 点 : 用于下钩子 ;


② 选择代理模式 : 静态代理 / 动态代理 ;


③ 代理替换 : 通过反射 , 将钩子替换成开发者自定义的代理 , 一般是在原有调用的基础上 , 不影响原来功能的前提下 , 注入新的逻辑 ;






二、Hook 按钮点击事件



1、按钮点击事件


获取布局文件的按钮 , 并为其设置点击事件 , 该点击事件 public void onClick(View v) 就是需要 Hook 的方法 , 我们使用 Hook 技术 , 使用动态代理 , 替换掉该 onClick 方法 , 注入额外的业务逻辑 ;


// 获取按钮 , 并未按钮组件设置点击事件
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Log.i(TAG, "Button OnClickListener onClick");
    }
});



2、熟悉底层源码


使用 Hook 的前提是 , 必须熟悉要 Hook 功能的底层源码 , 如 : Hook 按钮点击事件 , 必须熟悉 View 组件的 OnClickListener 相关源码 ;


先分析 View 的 setOnClickListener 方法的源码 , 传入 OnClickListener l 监听器 , 将该监听器赋值给 getListenerInfo().mOnClickListener 值 ;


public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    @UnsupportedAppUsage
    ListenerInfo mListenerInfo;
    /**
     * Register a callback to be invoked when this view is clicked. If this view is not
     * clickable, it becomes clickable.
     *
     * @param l The callback that will run
     *
     * @see #setClickable(boolean)
     */
    public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }
    @UnsupportedAppUsage
    ListenerInfo getListenerInfo() {
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }
}
getListenerInfo().mOnClickListener = l;


位置作为钩子的 Hook 点 , 勾住该方法 ;



3、获取 View 的 ListenerInfo mListenerInfo 成员


先使用反射获取 View 组件的 getListenerInfo 方法 ;


// 获取 View 的 getListenerInfo 方法
Method getListenerInfo = null;
try {
    getListenerInfo = View.class.getDeclaredMethod("getListenerInfo");
} catch (NoSuchMethodException e) {
    e.printStackTrace();
}


设置 getListenerInfo 方法的可见性 , 之后要调用该方法 , 否则会报错 ;


// 执行所有的反射方法 , 设置成员变量 之前 , 都要设置可见性

getListenerInfo.setAccessible(true);


执行 反射获取的 getListenerInfo 方法 , 并获取 ListenerInfo mListenerInfo 成员 ;


// 执行 View view 对象的 getListenerInfo 方法
Object object = null;
try {
    object = getListenerInfo.invoke(view);
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InvocationTargetException e) {
    e.printStackTrace();
}



4、分析 Hook 点


分析 View 组件的 setOnClickListener 方法 , 最终将 OnClickListener l 点击监听器设置到哪 ?


getListenerInfo() 获取的是 ListenerInfo 类型的对象 , 其中就封装了 OnClickListener mOnClickListener 成员 , 点击监听器就是设置在这里 ;


知道了定义位置 , 就可以通过反射获取 mOnClickListener 对应的成员字段 Field ;


public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    static class ListenerInfo {
        /**
         * Listener used to dispatch click events.
         * This field should be made private, so it is hidden from the SDK.
         * {@hide}
         */
        @UnsupportedAppUsage
        public OnClickListener mOnClickListener;
  }
    /**
     * Interface definition for a callback to be invoked when a view is clicked.
     */
    public interface OnClickListener {
        /**
         * Called when a view has been clicked.
         *
         * @param v The view that was clicked.
         */
        void onClick(View v);
    }
}


5、反射 ListenerInfo 并设置新的 OnClickListener 监听器


获取 ListenerInfo 中的 public OnClickListener mOnClickListener 成员 , 并重新设置新的成员 , 注入业务逻辑 ;


① 先根据全类名获取 android.view.View$ListenerInfo 字节码对象 ;


// ① 先根据全类名获取 ListenerInfo 字节码
Class<?> clazz = null;
try {
    clazz = Class.forName("android.view.View$ListenerInfo");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}


② 获取 android.view.View$ListenerInfo 中的 mOnClickListener 成员


// ② 获取 android.view.View.ListenerInfo 中的 mOnClickListener 成员
Field field = null;
try {
    field = clazz.getField("mOnClickListener");
} catch (NoSuchFieldException e) {
    e.printStackTrace();
}


③ 设置该字段访问性, 执行所有的反射方法 , 设置成员变量 之前 , 都要设置可见性 ;


// ③ 设置该字段访问性, 执行所有的反射方法 , 设置成员变量 之前 , 都要设置可见性
field.setAccessible(true);


④ 获取 mOnClickListener 成员变量 ;


// ④ 获取 mOnClickListener 成员变量

View.OnClickListener mOnClickListener = null;
try {
    mOnClickListener = (View.OnClickListener) field.get(mListenerInfo);
} catch (IllegalAccessException e) {
    e.printStackTrace();
}


⑤ 修改 View 的 ListenerInfo 成员的 mOnClickListener 成员 , 重新设置一个自定义的 View.OnClickListener 监听器 , 在该监听器的 onClick 方法中 , 调用之前获取的 监听器的 onClick 方法 , 此外还可以在该点击方法前后注入开发者自定义的业务逻辑 ;


// ⑤ 修改 View 的 ListenerInfo 成员的 mOnClickListener 成员
try {
    View.OnClickListener finalMOnClickListener = mOnClickListener;
    field.set(mListenerInfo, new View.OnClickListener(){
        @Override
        public void onClick(View v) {
            Log.i(TAG, "Hook Before");
            finalMOnClickListener.onClick(view);
            Log.i(TAG, "Hook After");
        }
    });
} catch (IllegalAccessException e) {
    e.printStackTrace();
}






三、完整代码示例


完整代码示例 :


package com.example.plugin_hook;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 获取按钮 , 并未按钮组件设置点击事件
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(TAG, "Button OnClickListener onClick");
            }
        });
        hook(button);
    }
    /**
     * hook Button 组件的 getListenerInfo 方法
     * @param view
     */
    private void hook(View view){
        // 获取 View 的 getListenerInfo 方法
        Method getListenerInfo = null;
        try {
            getListenerInfo = View.class.getDeclaredMethod("getListenerInfo");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        // 执行所有的反射方法 , 设置成员变量 之前 , 都要设置可见性
        getListenerInfo.setAccessible(true);
        // 执行 View view 对象的 getListenerInfo 方法
        Object mListenerInfo = null;
        try {
            mListenerInfo = getListenerInfo.invoke(view);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        // 反射获取 OnClickListener 成员
        // ① 先根据全类名获取 ListenerInfo 字节码
        Class<?> clazz = null;
        try {
            clazz = Class.forName("android.view.View$ListenerInfo");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // ② 获取 android.view.View.ListenerInfo 中的 mOnClickListener 成员
        Field field = null;
        try {
            field = clazz.getField("mOnClickListener");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        // ③ 设置该字段访问性, 执行所有的反射方法 , 设置成员变量 之前 , 都要设置可见性
        field.setAccessible(true);
        // ④ 获取 mOnClickListener 成员变量
        View.OnClickListener mOnClickListener = null;
        try {
            mOnClickListener = (View.OnClickListener) field.get(mListenerInfo);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        // ⑤ 修改 View 的 ListenerInfo 成员的 mOnClickListener 成员
        // 其中 ListenerInfo 成员 是
        try {
            View.OnClickListener finalMOnClickListener = mOnClickListener;
            field.set(mListenerInfo, new View.OnClickListener(){
                @Override
                public void onClick(View v) {
                    Log.i(TAG, "Hook Before");
                    finalMOnClickListener.onClick(view);
                    Log.i(TAG, "Hook After");
                }
            });
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}



执行结果 :


2021-06-17 11:19:07.513 12251-12251/com.example.plugin_hook I/MainActivity: Hook Before
2021-06-17 11:19:07.513 12251-12251/com.example.plugin_hook I/MainActivity: Button OnClickListener onClick
2021-06-17 11:19:07.513 12251-12251/com.example.plugin_hook I/MainActivity: Hook After






四、博客资源


博客资源 :


GitHub : https://github.com/han1202012/Plugin_Hook


目录
相关文章
|
14天前
|
前端开发 Java 编译器
当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
73 36
当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
|
3月前
|
开发框架 Dart Android开发
安卓与iOS的跨平台开发:Flutter框架深度解析
在移动应用开发的海洋中,Flutter作为一艘灵活的帆船,正引领着开发者们驶向跨平台开发的新纪元。本文将揭开Flutter神秘的面纱,从其架构到核心特性,再到实际应用案例,我们将一同探索这个由谷歌打造的开源UI工具包如何让安卓与iOS应用开发变得更加高效而统一。你将看到,借助Flutter,打造精美、高性能的应用不再是难题,而是变成了一场创造性的旅程。
|
3月前
|
算法 JavaScript Android开发
|
4月前
|
Java 程序员 API
Android|集成 slf4j + logback 作为日志框架
做个简单改造,统一 Android APP 和 Java 后端项目打印日志的体验。
197 1
|
5月前
|
前端开发 Java 数据库
💡Android开发者必看!掌握这5大框架,轻松打造爆款应用不是梦!🏆
在Android开发领域,框架犹如指路明灯,助力开发者加速应用开发并提升品质。本文将介绍五大必备框架:Retrofit简化网络请求,Room优化数据库访问,MVVM架构提高代码可维护性,Dagger 2管理依赖注入,Jetpack Compose革新UI开发。掌握这些框架,助你在竞争激烈的市场中脱颖而出,打造爆款应用。
536 3
|
5月前
|
编译器 Android开发 开发者
带你了解Android Jetpack库中的依赖注入框架:Hilt
本文介绍了Hilt,这是Google为Android开发的依赖注入框架,基于Dagger构建,旨在简化依赖注入过程。Hilt通过自动化的组件和注解减少了DI的样板代码,提高了应用的可测试性和可维护性。文章详细讲解了Hilt的主要概念、基本用法及原理,帮助开发者更好地理解和应用Hilt。
139 8
|
6月前
|
物联网 区块链 vr&ar
未来已来:探索区块链、物联网与虚拟现实技术的融合与应用安卓与iOS开发中的跨平台框架选择
【8月更文挑战第30天】在科技的巨轮下,新技术不断涌现,引领着社会进步。本文将聚焦于当前最前沿的技术——区块链、物联网和虚拟现实,探讨它们各自的发展趋势及其在未来可能的应用场景。我们将从这些技术的基本定义出发,逐步深入到它们的相互作用和集成应用,最后展望它们如何共同塑造一个全新的数字生态系统。
|
Java Android开发 容器

热门文章

最新文章

  • 1
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
  • 2
    Android历史版本与APK文件结构
  • 3
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 4
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 5
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 6
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
  • 7
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 8
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 9
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
  • 10
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 1
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
    14
  • 2
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    28
  • 3
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    34
  • 4
    Android历史版本与APK文件结构
    121
  • 5
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    29
  • 6
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    23
  • 7
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
    60
  • 8
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    37
  • 9
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
    73
  • 10
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    121