Android Hook式插件化教程(一)Hook从入门到精通

1.hook的定义

hook,顾名思义就是钩子。而在我们开发中通俗来讲就是劫持,就是某段SDK源码逻辑执行的过程中,通过代码手段劫持拦截执行该逻辑,加入自己的代码逻辑。

2hook的价值

hook是中级开发通往高级开发的必经之路。
如果把谷歌比喻成 安卓的造物主,那么安卓SDK源码里面就包含了万事万物的本源。
中级开发者,只在利用万事万物,浮于表层,而高级开发者能从本源上去改变万事万物,深入核心

3.hook的学习技巧

  • java反射 熟练掌握类Class,方法Method,成员Field的使用方法
    源码内部,很多类和方法都是@hide的,外部直接无法访问,所以只能通过反射,去创建源码中的类,方法,或者成员.
  • 阅读安卓源码的能力
    hook的切入点都在源码内部,不能阅读源码,不能理清源码逻辑,则不用谈hook.
    其实使用 androidStudio来阅读源码有个坑,,有时候会看到源码里面 “一片飘红”,看似是有什么东西没有引用进来,其实是因为有部分源码没有对开发者开放,解决起来很麻烦,最好是下载缺失的源码然后导入AS中浏览就行。

4.hook的通用思路

一句话总结,就是倒序写代码。顺序写代码,容易写错,而且这跟背代码没什么区别。
例如下面一段代码

package com.xxx.
public class A {private B b;
}

我们通过Hook替换掉A中b的变量。则我们可通过一下代码

Class Aclass = Class.forName("com.xxx.A");
Field bField = Aclass.getDeclaredField("b");
bField.setaccessible(truee)
bField.set(@1,@2)//其中@1为Aclass的对象,@2是你自定义的变量

其中我们可以通过bField.set(@1,@2)去反向退出@1对象怎么去获取,然后再倒叙写出@1对象的获取方法。。

5 hook 使用案例分享

接下来 我们hook来实现一个效果,效果是这样的,给一个按钮实现点击事件,在点击事件里Toast出当前的按钮的文字,然后我们用hook去拦截这一事件,做我们自己的逻辑。由于布局代码比较简单,就不贴出布局了。下面是MainActivity里面的代码

 Button button = (Button) findViewById(R.id.button);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(MainActivity.this, "" + ((Button) v).getText(), Toast.LENGTH_SHORT).show();}});

思路很简单,就是通过监听OnClickListener发的回调方法,然后拦截onClick放假加入我们的逻辑。简单来说就是用我们记得回调替换掉系统的OnClickListener,接下来就是查看android源码了我们追踪一下setOnClickListener里到底做了什么

    /*** 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;}

可以看出OnClickListener直接赋值给了getListenerInfo(),再看看getListenerInfo()做了什么

ListenerInfo getListenerInfo() {if (mListenerInfo != null) {return mListenerInfo;}mListenerInfo = new ListenerInfo();return mListenerInfo;}

这个方法返回了mListenerInfo对象,我们可以看看这个类的声明

static class ListenerInfo {/*** Listener used to dispatch focus change events.* This field should be made private, so it is hidden from the SDK.* {@hide}*/protected OnFocusChangeListener mOnFocusChangeListener;/*** Listeners for layout change events.*/private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;protected OnScrollChangeListener mOnScrollChangeListener;/*** Listeners for attach events.*/private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;/*** Listener used to dispatch click events.* This field should be made private, so it is hidden from the SDK.* {@hide}*/public OnClickListener mOnClickListener; //替换这个对象/*** Listener used to dispatch long click events.* This field should be made private, so it is hidden from the SDK.* {@hide}*/protected OnLongClickListener mOnLongClickListener;/*** Listener used to dispatch context click events. This field should be made private, so it* is hidden from the SDK.* {@hide}*/protected OnContextClickListener mOnContextClickListener;/*** Listener used to build the context menu.* This field should be made private, so it is hidden from the SDK.* {@hide}*/protected OnCreateContextMenuListener mOnCreateContextMenuListener;private OnKeyListener mOnKeyListener;private OnTouchListener mOnTouchListener;private OnHoverListener mOnHoverListener;private OnGenericMotionListener mOnGenericMotionListener;private OnDragListener mOnDragListener;private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;OnCapturedPointerListener mOnCapturedPointerListener;private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners;}

所以一目了然了,只要我们把ListenerInfo的mOnClickListener替换成我们自己的动态代理,那问题就解决了,接下来开始实现,用倒叙写代码的方式助于我们理清思路

        //获取ListenerInfo ClassClass listenerInfoClass = Class.forName("android.view.View$ListenerInfo");//mOnClickListener FieldField onClicktenerField = listenerInfoClass.getField("mOnClickListener");//由于mOnClickListener是Public 不用授权访问final Object onClicktenerObj = onClicktenerField.get(listenerInfoObj);

所以问题在于listenerInfoObj这个对象我们怎样才能获取了,所以向上反推listenerInfoObj的由来。上述我们提到 listenerInfo是有以下方法得来的

ListenerInfo getListenerInfo() {if (mListenerInfo != null) {return mListenerInfo;}mListenerInfo = new ListenerInfo();return mListenerInfo;}

所以只有通过执行这个方法我们才能得到listenerInfo对象,所以通过反射去获取

        //获取View对象Class viewClass = Class.forName("android.view.View");//获取getListenerInfo方法Method getListenerInfoMethod = viewClass.getDeclaredMethod("getListenerInfo");//由于getListenerInfo不是公开的,所以必须授权虚拟机去访问getListenerInfoMethod.setAccessible(true);//传入hook的按钮对象获取Object listenerInfoObj = getListenerInfoMethod.invoke(view);

所以得到listenerInfoObj这个对象,然后执行以下方法

        //将系统的onclictener替换成我写的动态代理onClicktenerField.set(listenerInfoObj, @2);

而@2是我们要替换的动态代理,而动态代理就是监听onclicklistener的方法回调。

// 1.监听 onClick,当用户点击按钮的时候-->onClick, 我们自己要先拦截这个事件// 动态代理// mOnClickListener 本质是==OnClickListenerObject mOnClickListenerProxy = Proxy.newProxyInstance(MainActivity.class.getClassLoader(), // 1加载器new Class[]{View.OnClickListener.class}, // 2要监听的接口,监听什么接口,就返回什么接口new InvocationHandler() { // 3监听接口方法里面的回调/**** void onClick(View v);** onClick ---> Method* View v ---> Object[] args** @param proxy* @param method* @param args* @return* @throws*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 加入了自己逻辑Log.d("hook", "拦截到了 OnClickListener的方法了");Button button = new Button(MainActivity.this);button.setText("我是hook的新button");// 让系统程序片段 --- 正常继续的执行下去return method.invoke(mOnClickListenerObj, button);}});

代码里有注释,InvocationHandler里就是我们要做的事情,这里是实例化新的按钮。
而Hook的关键必须不能阻断系统执行其他流程,所以这里比如返回return method.invoke(mOnClickListenerObj, button); 按道理应该会Toast出“我是hook的新button”,最后最后别忘里最核心的一步

        //将系统的onclictener替换成我写的动态代理onClicktenerField.set(listenerInfoObj, onClickListenerPoxy);

这样替换掉就能生效里,接下来看看效果。

以下是完整代码

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button button = (Button) findViewById(R.id.button);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(MainActivity.this, "" + ((Button) v).getText(), Toast.LENGTH_SHORT).show();}});// 在不修改以上代码的情况下,通过Hook把 ((Button) v).getText() 内容给修改try {hook(button); // button就是View对象} catch (Exception e) {e.printStackTrace();Toast.makeText(this, "Hook失败" + e.toString(), Toast.LENGTH_SHORT).show();}}private void hook(View view) throws Exception {Class mViewClass = Class.forName("android.view.View");Method getListenerInfoMethod = mViewClass.getDeclaredMethod("getListenerInfo");getListenerInfoMethod.setAccessible(true); // 授权// 执行方法Object  mListenerInfo = getListenerInfoMethod.invoke(view);// 替 换  public OnClickListener mOnClickListener; 替换我们自己的Class mListenerInfoClass = Class.forName("android.view.View$ListenerInfo");Field mOnClickListenerField = mListenerInfoClass.getField("mOnClickListener");final Object mOnClickListenerObj = mOnClickListenerField.get(mListenerInfo); // 需要@1对象// 1.监听 onClick,当用户点击按钮的时候-->onClick, 我们自己要先拦截这个事件// 动态代理// mOnClickListener 本质是==OnClickListenerObject mOnClickListenerProxy = Proxy.newProxyInstance(MainActivity.class.getClassLoader(), // 1加载器new Class[]{View.OnClickListener.class}, // 2要监听的接口,监听什么接口,就返回什么接口new InvocationHandler() { // 3监听接口方法里面的回调/**** void onClick(View v);** onClick ---> Method* View v ---> Object[] args** @param proxy* @param method* @param args* @return* @throws*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 加入了自己逻辑Log.d("hook", "拦截到了 OnClickListener的方法了");Button button = new Button(MainActivity.this);button.setText("同学们大家好....");// 让系统程序片段 --- 正常继续的执行下去return method.invoke(mOnClickListenerObj, button);}});// 狸猫换太子 把系统的 mOnClickListener  换成 我们自己写的 动态代理mOnClickListenerField.set(mListenerInfo, mOnClickListenerProxy); // 替换的 我们自己的动态代理}
}

接下来的我分享Android Hook式插件化教程(二)Hook系统源码 敬请关注

Android Hook式插件化教程(一)Hook从入门到精通相关推荐

  1. Android 插件化原理学习 —— Hook 机制之动态代理

    前言 为了实现 App 的快速迭代更新,基于 H5 Hybrid 的解决方案有很多,由于 webview 本身的性能问题,也随之出现了很多基于 JS 引擎实现的原生渲染的方案,例如 React Nat ...

  2. 【Android 插件化】Hook 插件化框架 ( 使用 Hook 方式替换插件 Activity 的 mResources 成员变量 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  3. 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  4. 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  5. Android 插件化原理解析——Hook机制之AMSPMS

    在前面的文章中我们介绍了DroidPlugin的Hook机制,也就是代理方式和Binder Hook:插件框架通过AOP实现了插件使用和开发的透明性.在讲述DroidPlugin如何实现四大组件的插件 ...

  6. Android插件化开发指南——Hook技术(二)

    文章目录 1. 前言 2. 分析 3. 加载外部资源文件代码 4. References 1. 前言 在上篇Android插件化开发指南--Hook技术(一)[长文]中提到最终的效果其实在插件中的Ma ...

  7. 【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  8. 【Android 插件化】“ 插桩式 “ 插件化框架 ( 代理 Activity 组件开发 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  9. 【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

最新文章

  1. 36晋级12第四场:评委弃权 无人晋级
  2. html事件绑定的方法,javascript实现简单的on事件绑定
  3. angr学习笔记(5)(栈符号化)
  4. 随手记录自动化常用的一些事情
  5. Linux 中将用户添加到组的指令
  6. 接收端收到数据包以后的处理过程
  7. IntelliJ IDEA中无法加载jar包导致出现“cannot resolve symbol...”问题的解决
  8. 睡觉时钱被转走、开房信息被叫卖、数字货币被篡改,你的安全感,还在吗?...
  9. mysql 服务账号_MySql 账号管理
  10. 关于Java的多线程Runnable的个人理解(基础,不讲概念)
  11. static 和 visibility hidden 的区别
  12. Flash:LoadVars数据提交与表单处理
  13. node.js核心模块
  14. easyui下拉选项多怎么解决_30岁以后皮肤松弛皱纹越来越多怎么办?这组瑜伽帮你解决...
  15. 手机用计算机怎么求最高分,计算机中求最高分怎么算【excel 如何去掉一个最高分和一个最低分求平均数】...
  16. skynet.pack序列化学习
  17. js实现显示系统时间的表盘
  18. 百度排名靠前工具软件
  19. 这一年我都做了什么......
  20. 安卓在活动左上角添加返回键

热门文章

  1. 安全模式下音频文件需要服务器是否有病毒,Win7安全模式下居然可以做这些事!...
  2. 在UDP应用层面测试实现丢包的检测
  3. Linux OOM机制详解
  4. 短信删了怎样找回信息
  5. MIFARE智能卡技术简介
  6. No qualifying bean of type ‘java.lang.String‘ available expected
  7. 算法越学越扎心,有没啥破解之法?
  8. Kafka学习之路(一)Kafka的简介
  9. DBeaver Ultimate 开始教程
  10. npm是干什么的?为什么要使用npm?(适合不太了解 npm 的新人阅读)