Android Hook式插件化教程(一)Hook从入门到精通
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从入门到精通相关推荐
- Android 插件化原理学习 —— Hook 机制之动态代理
前言 为了实现 App 的快速迭代更新,基于 H5 Hybrid 的解决方案有很多,由于 webview 本身的性能问题,也随之出现了很多基于 JS 引擎实现的原生渲染的方案,例如 React Nat ...
- 【Android 插件化】Hook 插件化框架 ( 使用 Hook 方式替换插件 Activity 的 mResources 成员变量 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- Android 插件化原理解析——Hook机制之AMSPMS
在前面的文章中我们介绍了DroidPlugin的Hook机制,也就是代理方式和Binder Hook:插件框架通过AOP实现了插件使用和开发的透明性.在讲述DroidPlugin如何实现四大组件的插件 ...
- Android插件化开发指南——Hook技术(二)
文章目录 1. 前言 2. 分析 3. 加载外部资源文件代码 4. References 1. 前言 在上篇Android插件化开发指南--Hook技术(一)[长文]中提到最终的效果其实在插件中的Ma ...
- 【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】“ 插桩式 “ 插件化框架 ( 代理 Activity 组件开发 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
最新文章
- 36晋级12第四场:评委弃权 无人晋级
- html事件绑定的方法,javascript实现简单的on事件绑定
- angr学习笔记(5)(栈符号化)
- 随手记录自动化常用的一些事情
- Linux 中将用户添加到组的指令
- 接收端收到数据包以后的处理过程
- IntelliJ IDEA中无法加载jar包导致出现“cannot resolve symbol...”问题的解决
- 睡觉时钱被转走、开房信息被叫卖、数字货币被篡改,你的安全感,还在吗?...
- mysql 服务账号_MySql 账号管理
- 关于Java的多线程Runnable的个人理解(基础,不讲概念)
- static 和 visibility hidden 的区别
- Flash:LoadVars数据提交与表单处理
- node.js核心模块
- easyui下拉选项多怎么解决_30岁以后皮肤松弛皱纹越来越多怎么办?这组瑜伽帮你解决...
- 手机用计算机怎么求最高分,计算机中求最高分怎么算【excel 如何去掉一个最高分和一个最低分求平均数】...
- skynet.pack序列化学习
- js实现显示系统时间的表盘
- 百度排名靠前工具软件
- 这一年我都做了什么......
- 安卓在活动左上角添加返回键
热门文章
- 安全模式下音频文件需要服务器是否有病毒,Win7安全模式下居然可以做这些事!...
- 在UDP应用层面测试实现丢包的检测
- Linux OOM机制详解
- 短信删了怎样找回信息
- MIFARE智能卡技术简介
- No qualifying bean of type ‘java.lang.String‘ available expected
- 算法越学越扎心,有没啥破解之法?
- Kafka学习之路(一)Kafka的简介
- DBeaver Ultimate 开始教程
- npm是干什么的?为什么要使用npm?(适合不太了解 npm 的新人阅读)