热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Hook技术初探

最近在研究插件化技术,插件化的中经常会使用到Hook技术,查阅了很多资料这里总结下讲的比较好的,希望对大家有所帮助。Hook技术Hoo

最近在研究插件化技术,插件化的中经常会使用到 Hook 技术,查阅了很多资料这里总结下讲的比较好的,希望对大家有所帮助。

Hook 技术

Hook 是钩子的意思,在 Android 操作系统中系统维护着自己的一套事件分发机制。应用程序,包括应用触发事件和后台逻辑处理,也是根据事件流程一步步地向下执行。

而钩子的意思,就是在事件传送到终点前截获监控事件的传输,像个钩子钩上事件一样,并且能够在钩上事件时,处理一些自己特定的事件。较为形象的流程如下图所示。

在这里插入图片描述

Hook 的这个本领,使它能够将自身的代码「融入」被勾住(Hook)的程序的进程中,成为目标进程的一个部分。

在 Android 系统中使用了沙箱机制,普通用户程序的进程空间都是独立的,程序的运行彼此间都不受干扰。根据 Hook 对象与 Hook 后处理的事件方式不同, Hook 还分为不同的种类,如消息 Hook 、API Hook 等。

从 Android 的开发来说,Android 系统本身就提供给了我们两种开发模式,基于 Android SDK 的 Java 语言开发,基于 AndroidNDK 的 Native C/C++ 语言开发。所以,我们在讨论 Hook 的时候就必须在两个层面上来讨论。

Java Hook

通过对 Android 平台的虚拟机注入与 Java 反射的方式,来改变 Android 虚拟机调用函数的方式(ClassLoader),从而达到 Java 函数重定向的目的,这里我们将此类操作称为 Java API Hook。

下面通过 Hook View 的 OnClickListener 来说明 Hook 的使用方法。

btn_test.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(v.getContext(), "Button Click", Toast.LENGTH_SHORT).show();}
});

首先进入 View 的 setOnClickListener 方法,我们看到 OnClickListener 对象被保存在了一个叫做 ListenerInfo 的内部类里,其中 mListenerInfo 是 View 的成员变量。

public void setOnClickListener(@Nullable OnClickListener l) {if (!isClickable()) {setClickable(true);}getListenerInfo().mOnClickListener = l;
}ListenerInfo getListenerInfo() {if (mListenerInfo != null) {return mListenerInfo;}mListenerInfo = new ListenerInfo();return mListenerInfo;
}

ListeneInfo 里面保存了 View 的各种监听事件,比如 OnClickListener、OnLongClickListener、OnKeyListener 等等。

static class ListenerInfo {//...public OnClickListener mOnClickListener;protected OnLongClickListener mOnLongClickListener;private OnKeyListener mOnKeyListener;private OnTouchListener mOnTouchListener;private OnHoverListener mOnHoverListener;private OnGenericMotionListener mOnGenericMotionListener;private OnDragListener mOnDragListener;//...
}

我们的目标是 Hook OnClickListener,所以就要在给 View 设置监听事件后,替换 OnClickListener 对象,注入自定义的操作。

public class HookView {public static void hookOnClickListener(View view) {try {// 通过反射获取到 getListenerInfo() 方法&#64;SuppressLint("DiscouragedPrivateApi")Method getListenerInfo &#61; View.class.getDeclaredMethod("getListenerInfo");// 设置访问权限getListenerInfo.setAccessible(true);// 调用 view 的 getListenerInfo() 获取到 ListenerInfoObject listenerInfo &#61; getListenerInfo.invoke(view);// 通过反射获取到 ListenerInfo 的 Class 对象&#64;SuppressLint("PrivateApi")Class<?> listenerInfoClass &#61; Class.forName("android.view.View$ListenerInfo");// 获取到 mOnClickListener 成员变量Field mOnClickListener &#61; listenerInfoClass.getDeclaredField("mOnClickListener");// 设置访问权限mOnClickListener.setAccessible(true);// 获取 mOnClickListener 属性的值View.OnClickListener originOnClickListener &#61; (View.OnClickListener) mOnClickListener.get(listenerInfo);// 创建 OnClickListener 代理对象HookedOnClickListener hookedOnClickListener &#61; new HookedOnClickListener(originOnClickListener);// 为 mOnClickListener 属性重新赋值mOnClickListener.set(listenerInfo, hookedOnClickListener);} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | ClassNotFoundException | NoSuchFieldException e) {e.printStackTrace();}}static class HookedOnClickListener implements View.OnClickListener {private final View.OnClickListener origin;HookedOnClickListener(View.OnClickListener origin) {this.origin &#61; origin;}&#64;Overridepublic void onClick(View v) {Log.e("HookedOnClickListener", "onClick");if (origin !&#61; null) {origin.onClick(v);}}}
}

到这里&#xff0c;我们成功 Hook 了 OnClickListener&#xff0c;在点击之前和点击之后可以执行某些操作&#xff0c;达到了我们的目的。下面是调用的部分&#xff0c;在给 Button 设置 OnClickListener 后&#xff0c;执行 Hook 操作。

btn_test.setOnClickListener(new View.OnClickListener() {&#64;Overridepublic void onClick(View v) {Toast.makeText(v.getContext(), "Button Click", Toast.LENGTH_SHORT).show();}
});HookView.hookOnClickListener(btn_test);

Native Hook

Android Native Hook 主要分为两种&#xff1a;PLT Hook、Inline Hook。

我对 Native 开发不熟&#xff0c;这里仅仅做下了解。以下章节摘自Android Native Hook技术路线概述。


PLT Hook

先来介绍一下 Android PLT Hook 的基本原理。Linux 在执行动态链接的 ELF 的时候&#xff0c;为了优化性能使用了一个叫延时绑定的策略。

延时绑定的策略是为了解决原本静态编译时要把各种系统 API 的具体实现代码都编译进当前 ELF 文件里导致文件巨大臃肿的问题。所以当在动态链接的 ELF 程序里调用共享库的函数时&#xff0c;第一次调用时先去查找 PLT 表中相应的项目&#xff0c;而 PLT 表中再跳跃到 GOT 表中希望得到该函数的实际地址&#xff0c;但这时 GOT 表中指向的是 PLT 中那条跳跃指令下面的代码&#xff0c;最终会执行 _dl_runtime_resolve() 并执行目标函数。

第二次调用时也是 PLT 跳转到 GOT 表&#xff0c;但是 GOT 中对应项目已经在第一次 _dl_runtime_resolve() 中被修改为函数实际地址&#xff0c;因此第二次及以后的调用直接就去执行目标函数&#xff0c;不用再去执行 _dl_runtime_resolve() 了。

因此&#xff0c;PLT Hook 通过直接修改 GOT 表&#xff0c;使得在调用该共享库的函数时跳转到的是用户自定义的 Hook 功能代码。

了解 PLT Hook 的原理后&#xff0c;可以进一步分析出这种技术的特点&#xff1a;

  • 由于修改的是 GOT 表中的数据&#xff0c;因此修改后&#xff0c;所有对该函数进行调用的地方就都会被 Hook 到。这个效果的影响范围是该 PLT 和 GOT 所处的整个 so 库。因此&#xff0c;当目标 so 库中多行被执行代码都调用了该 PLT 项所对应的函数&#xff0c;那它们都会去执行 Hook 功能。
  • PLT 与 GOT 表中仅仅包含本 ELF 需要调用的共享库函数项目&#xff0c;因此不在 PLT 表中的函数无法 Hook 到。

那么这些特点会导致什么呢&#xff1f;

  • 可以大量 Hook 那些系统 API&#xff0c;但是难以精准 Hook 住某次函数调用。

这比较适用于开发者对于自家 App 性能监控的需求。比如 Hook 住 malloc 使其输出参数&#xff0c;这样就能大量统计评估该 App 对于内存的需求。

但是对于一些对 Hook 对象有一定精准度要求的需求来说很不利&#xff0c;比如说是安全测试或者逆向分析的工作需求&#xff0c;这些工作中往往需要对于目标 so 中的某些关键点有准确的观察。

  • 对于一些 so 内部自定义的函数无法 Hook 到&#xff0c;因为这些函数不在 PLT 表和 GOT 表里。

这个缺点对于不少软件分析者来说可能是无法忍受的。因为许多关键或核心的代码逻辑往往都是自定义的。例如 NDK 中实现的一些加密工作&#xff0c;即使使用了共享库中的加密函数&#xff0c;但秘钥的保存管理等依然需要进一步分析&#xff0c;而这些工作对于自定义函数甚至是某行汇编代码的监控能力要求是远远超出 PLT Hook 所能提供的范围。

  • 在回调原函数方面&#xff0c;PLT Hook 在 hook 目标函数时&#xff0c;如果需要回调原来的函数&#xff0c;那就在 Hook 后的功能函数中直接调用目标函数即可。

可能有点绕&#xff0c;详细解释一下&#xff1a;假设对目标函数 malloc() 的调用在 1.so 中&#xff0c;用户用 PLT Hook 技术开发的 HookMalloc() 功能函数在 2.so 中。&#xff08;因为通常情况下目标函数与用户的自定义 Hook 功能函数不在一个 ELF 文件里&#xff09;当 1.so 中调用 malloc() 时会去 1.so 的 PLT 表中查询&#xff0c;结果是执行流程进入了 2.so 中的 HookMalloc() 中。如果这时候 HookMalloc 中希望调用原目标函数 malloc()&#xff0c;那就直接调用 malloc() 就好了。因为这里的 malloc 会去 2.so 中的 PLT 表中查询&#xff0c;不受 1.so 中那个被修改过的 PLT 表的影响。

本技术路线的典型代表是爱奇艺开源的 xHook 工具库。xhook 是一个针对 Android 平台 ELF&#xff08;可执行文件和动态库&#xff09;的 PLT&#xff08;Procedure Linkage Table&#xff09;hook 库。从维护频率和项目标志设计来看这是一款产品级的开源工具。

Inline Hook

Inline Hook 即内部跳转 Hook&#xff0c;通过替换函数开始处的指令为跳转指令&#xff0c;使得原函数跳转到自己的函数&#xff0c;通常还会保留原函数的调用接口。与 GOT 表 Hook 相比&#xff0c;Inline Hook 具有更广泛的适用性&#xff0c;几乎可以 Hook 任何函数&#xff0c;不过其实现更为复杂&#xff0c;考虑的情况更多&#xff0c;并且无法对一些太短的函数 Hook。

本技术路线的基本原理是在代码段中插入跳转指令&#xff0c;从而把程序执行流程引向用户需要的功能代码中去&#xff0c;以此达到 Hook 的效果&#xff0c;如下图所示&#xff1a;

在这里插入图片描述

从上图中可以看出主要有如下几个步骤&#xff1a;

  • 在想要 Hook 的目标代码处备份下面的几条指令&#xff0c;然后插入跳转指令&#xff0c;把程序流程转移到一个 stub 段上去。
  • 在 stub 代码段上先把所有寄存器的状态保存好&#xff0c;并调用用户自定义的 Hook 功能函数&#xff0c;然后把所有寄存器的状态恢复并跳转到备份代码处。
  • 在备份代码处把当初备份的那几条指令都执行一下&#xff0c;然后跳转到当初备份代码位置的下面接着执行程序。

由此可以看出使用 Inline Hook 有如下的 Hook 效果特点&#xff1a;

  • 完全不受函数是否在 PLT 表中的限制&#xff0c;直接在目标 so 中的任意代码位置都可进行 Hook。这个 Hook 精准度是汇编指令级的。这对于逆向分析人员和安全测试人员来说是个非常好的特性&#xff01;

  • 可以介入任意函数的操作。由于汇编指令级的 Hook 精度&#xff0c;以及不受 PLT 表的限制&#xff0c;Inline Hook 技术可以去函数执行中的任意代码行间进行 Hook 功能操作&#xff0c;从而读取或修改任意寄存器&#xff0c;使得函数的操作流程完全可以被控制。

  • 对 Hook 功能函数的限制较小。由于在第二步调用 Hook 功能函数前已经把所有之前的寄存器状态都进行保存了&#xff0c;因此此时的 Hook 功能函数几乎就是个独立的函数&#xff0c;它无需受限于原本目标函数的参数形式&#xff0c;完全都由自己说了算。并且执行完后也完全是一个正常的函数退出形式释放栈空间。

  • 对于 PLT Hook 的强制批量 Hook 的特性&#xff0c;Native Hook 要灵活许多。当想要进行批量 Hook 一些系统 API 时也可以直接去找内存里对应的如 libc.so 这些库&#xff0c;对它们中的 API 进行 Hook&#xff0c;这样的话&#xff0c;所有对这个 API 的调用也就都被批量 Hook 了。


技术对比

根据以上的分析&#xff0c;我们发现这两种技术在原理和适用场景上的差别是相当大的。因此有必要进行一下对比&#xff0c;给那些有 Native Hook 需求的童鞋一些参考。

NamePLT HookNative Hook
精准度 函数级汇编级
范围 出现在PLT表中的动态链接函数 目标so内全部可执行代码
灵活性 只能批量 单次或批量都可以
技术难度 涉及内存地址计算和修改等 涉及寄存器计算、手写汇编、指令修复等

我的 GitHub

github.com/jeanboydev

技术交流群

欢迎加入技术交流群&#xff0c;来一起交流学习。

QQ 技术交流群

微信技术交流群

我的公众号

欢迎关注我的公众号&#xff0c;分享各种技术干货&#xff0c;各种学习资料&#xff0c;职业发展和行业动态。

Android 波斯湾

参考资料


  • 理解 Android Hook 技术以及简单实战
  • Android Native Hook技术路线概述

推荐阅读
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
author-avatar
小短腿Tel
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有