从零开始使用gradle配置即可执行的Hook库详解

目录
  • 背景
    • 本文须知
    • 当前技术背景
    • 底层选择
    • 目标流程图
  • Transform
  • ASM
  • 封装开始
    • 目标
    • 实现
      • gradle 定义extension
      • Transform阶段收集信息:
      • 自定义的classvisitor
      • 自定义method visitor
      • 自定义hook操作
  • 总结

背景

有一天,老板突然找到小B说,隐私合规需要我们获取权限前,需要明确授权来意,这个你来跟一下吧!小B此时就可愁了,因为项目权限那么多,每个自己手动加上授权来意提示的话,可能会漏掉很多,工作量也大,这可咋办呀!老B看到小B这么愁眉苦脸,连忙说:“可以用ASM进行插桩呀!hook想要的方法”,小B听了,兴奋的去百度了一下,但是发现asm学习成本又高,短期又不可能搞完,这可咋办呀!明明我只想搞hook一个方法交差来着!!老B:”没事,所以本文就来了!”

本文须知

这里只是提供一个设计思路,不会涉及到太多细节,需要读者了解相关的知识,如果不清楚只想使用的话,也是有的 github.com/TestPlanB/S… 欢迎点星星或者pr噢!

当前技术背景

目前可以利用字节码进行hook的框架有很多,比如ASM,AspectJ,javassit等等,都是可以在编译时插入相关的字节码,进行方法的插桩,从而达到一个hook的目的,但是这些工具好归好,但是都有一个小问题,就是需要上手,部分hook框架上手门槛高,也有自己独特的用法,短时间内可能很难使得开发人员上手。所以对hook库进行一个二次封装,也是很多公司在做的一个事情。方法有很多种,作者基于自己的理解,认为配置式的hook才是最简单的,毕竟,Android就有gradle进行各种的项目工程配置,那么我们为什么就不能通过gradle进行配置的Hook呢?基于上面的猜想,就有了本文!友情提示:阅读本文最好对asm跟transform机制有所了解

底层选择

为了更加通用和高效,本次采用asm作为底层,进行二次封装,毕竟android官方的link还有比较出色的aspectj都是基于asm进行底层修改的,那我们这次也同样使用,好了就开干!

目标流程图

Transform

为了让不太了解的ASM的也能够阅读本文,所以也会介绍部分ASM相关的信息,详细了解还需要大家去官网阅读噢!这里先介绍Transform机制。 Transform是android 进行编译时,在class 文件生成 dex文件时,给我们开发者预留的一个小口,可以理解在这个阶段,我们可以修改已生成的class等文件,编织入自己额外的字节码,从而达到无需修改项目本身的源代码就可以行为修改的机制!如果大家有留意的话,这个机制就是gradle 在build阶段中,会存在一个transformClassesWithXXForXX的task,举例子:

transformClassesWithSpiderPluginForDebug,就是在这里进行的transform修改。 当然,一个项目会存在多个transform,如图所示

就像流水线一样,我们的transform处理完就会交给下一个transform,共同修改生成的字节码的行为。大家可以先简单理解为这是一个任务,提供了接口给外部修改生成字节码的机会,具体我们可以google相关的资料,也可以看下最后例子项目的处理

ASM

ASM是一个字节码修改框架,他就在我们上文提到的Transform里面做了文章。关于ASM的介绍我们简单来几下,有个大概的认知就好,就像我们访问一个方法/属性一样,jvm肯定是先加载类,然后在执行方法或者属性的方法,ASM的运行机制就如图一样

封装开始

目标

我们的目标是建立一个基于gradle配置即可运行的hook库,先从使用角度考虑,如果我想hook一个类是LogUtils,中的test方法的话,需要哪些参数呢?快动一下你聪明的小脑袋,emmm,比如类的名称需要吧!方法名称!还有捏!只靠这两个明显还不够,因为我们还存在着各种重载不是嘛,那怎么表示一个特定方法呢!没错,还有函数签名对吧!毕竟编译器底层就是靠着函数签名去识别某个方法的呀,还有嘛?找到这个方法后,我们是在方法前/方法本身/方法后 进行自定义修改呢?所以就还需要一个类似于模式一样的东西吧!这里就称为hook模式好了,还有嘛?找到这个方法,我们还需要自己自定义的操作吧!就定义为hook操作吧。 总结起来,我们需要hook模式,类的名称,方法名称,函数签名,hook操作就可以完成一次hook某个方法的需求了对吧,就比如以下代码所示

比如hook LogUtils类的test方法,签名是()V,
替换为调用LogTest类的一个静态方法test
hookMethod hookMode.Default(hook模式), 
"com/example/spider/LogUtils"(类的名称), 
"test"(方法名称), "()V"(函数签名), { MethodVisitor mv ->
    mv.visitMethodInsn(Opcodes.INVOKESTATIC, 
    "com/example/spider/LogTest", "test", "()V", false)
}(hook操作)

实现

为了使只要通过上面的代码就能实现hook操作,我们需要定义: asm相关的:自定义的classvisitor,methodvisitor gradle:extension参数,比如上面的“hookMethod”,用来标记我们需要哪部分进行hook操作 transform:标准transform写法。

gradle 定义extension

我们按照上面思路,是不是需要定义一个类,包含hook模式,类的名称,方法名称,函数签名,hook操作,才能将参数传给transform,从而执行自己的ASM操作。 所以就需要定义extension参数: 我们可以在定义plugin的时候,在apply阶段通过project.extensions.create,创建一个自己的配置格式参数,比如Hook.class里面就有我们的参数

project.extensions.create("hook"(标识名称), Hook.class)

使用的话就可以在任意gradle文件使用

hook{
  参数1 对象值
  参数2 对象值
}

这样的话,我们只需要在Transform阶段收集到配置信息传给ASM即可!。

Transform阶段收集信息:

gradle声明的信息我们都可以通过project.xx(标识名称)获取

比如

hook.methodHooker = project.hook,就拿到了一个属于Hook类的hook对象
后续通过hook.hook模式就可以拿到属性是hook模式的参数了

自定义的classvisitor

我们transform阶段会遍历所有的类,但是我们只需要对特定的类进行修改对不对,所以在这里,我们需要针对只需对gradle配置的类,比如例子中的LogUtils进行处理即可,而不需要动刀其他的类! transform进行时,调用classvisitor就会调用其visit方法,我们在这里识别出我们需要hook的类即可对不对,加入我们需要hook的东西都在 hook.hookMethodList里面,我们只需要遍历一遍,找到需要的类,然后打上一个标记

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
    super.visit(version, access, name, signature, superName, interfaces);
   for(遍历hookMethodList里面){
    if 如果配置的类 == name{
        标记就为true
    }
    }
}

调用visit方法后,就代表了这个类被访问过了,就会调用其visitmethod方法,如果标记有效,我们就采用自定义的method visitor进行方法的修改,否则就还是原本的method visitor

@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
    MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);
    if (标记不为true) {
        return mv;
    }
    进行我们自定义的method visitor操作
    }
    return mv;
}

自定义method visitor

如果class是我们需要hook的class,就会走到了自定义的method visitor,这里是ASM的定义

@Override
public void visitCode() {
    if hook模式是方法前{
     hook 行为执行
     }
    super.visitCode();
}
@Override
public void visitMethodInsn(int opcode, String owner, String methodName, String descriptor, boolean isInterface) {
if hook模式是方法本身{
     hook 行为执行
     }
    super.visitMethodInsn(opcode, owner, methodName, descriptor, isInterface);
}
@Override
public void visitInsn(int opcode) {
    if (opcode == Opcodes.ATHROW || (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
     if hook模式是方法后{
     hook 行为执行
     }
    }
    super.visitInsn(opcode);
}

自定义hook操作

在配置阶段,一个hook操作就可以抽象为Closure,如果用groovy语法就是Closure,如果是Kotlin就是一个函数,代表要进行的操作。 在Transform阶段我们就可以织入自定义的closure,等满足条件就触发。幸运的是,ASM本身就提供了一个为AndroidStudio,准备的插件,叫“ASM Bytecode viewer”,通过这个插件,我们可以直接生成想要的插入代码所对应的ASM编码,如图:

通过closure所传递的methodvisitor,我们就可以执行配置的hook操作了。值得注意一点是,Spider不重新定义hook规则,而是在ASM基础上,封装比较容易编译错误的点,比如Transform编写,visitor类的编写等等,便于实现我们自己的hook规格,而脱离框架本身,这点是需要运用Spider的开发者需要注意的点!

总结

因为ASM体系有很多细节,文章是没办法列举出所有细节,所以只能表露一个设计思路,具体的用法大家可以移步github.com/TestPlanB/S… 上面也是Spider的设计思路,具体用法也可以看Readme噢!

以上就是从零开始使用gradle配置即可执行的Hook库详解的详细内容,更多关于gradle配置可执行Hook库的资料请关注我们其它相关文章!

时间: 2022-09-15

Android三方依赖冲突Gradle中exclude的使用

目录 一.场景 二.如何查看项目中的三方依赖? 三.使用exclude解决依赖冲突的问题 四.总结 一.场景 Android项目的开发过程中,我们项目中的gradle的dependencies闭包中会引入很多三方依赖库,引入的库越多,越容易产生库之间的依赖冲突. 列举冲突的场景: 1.同一个依赖库引入多个版本: 2.重复引入了同一个依赖库: 编译报错信息一般为:Program type already present 二.如何查看项目中的三方依赖? 方案一: Gradle task工具查看 按照

Android开发之Gradle 进阶Tasks深入了解

目录 前言 定义Task register与create的区别 查找Task 配置Task 将参数传递给Task构造函数 Task添加依赖 Task排序 Task添加说明 跳过Task 使用onlyIf 使用 StopExecutionException 禁用与启用Task Task超时 Task支持增量编译 Task的输入输出 自定义task类型 声明输入输出的好处 推断task依赖关系 输入和输出验证 并行task 增量编译原理解析 一些高端操作 将@OutputDirectory链接到@I

Android性能优化之plt hook与native线程监控详解

目录 背景 native 线程创建 PLT PLT Hook xhook bhook plt hook总结 背景 我们在android超级优化-线程监控与线程统一可以知道,我们能够通过asm插桩的方式,进行了线程的监控与线程的统一,通过一系列的黑科技,我们能够将项目中的线程控制在一个非常可观的水平,但是这个只局限在java层线程的控制,如果我们项目中存在着native库,或者存在着很多其他so库,那么native层的线程我们就没办法通过ASM或者其他字节码手段去监控了,但是并不是就没有办法,还有

android的got表HOOK实现代码

概述 对于android的so文件的hook根据ELF文件特性分为:Got表hook.Sym表hook和inline hook等. 全局符号表(GOT表)hook,它是通过解析SO文件,将待hook函数在got表的地址替换为自己函数的入口地址,这样目标进程每次调用待hook函数时,实际上是执行了我们自己的函数. Androd so注入和函数Hook(基于got表)的步骤: 1.ptrace附加目标pid进程; 2.在目标pid进程中,查找内存空间(用于存放被注入的so文件的路径和so中被调用的函

Android Studio调试Gradle插件详情

前言: 使用ASM在编译期修改字节码来完成各种功能(统计方法耗时.全埋点统计...),就需要开发Gradle插件.开发插件中遇到问题排查使用日志输出效率太低了,能断点调试的话就更好了.其实已经有很多文章写了调试插件的方法,但是自己照着他们的步骤下来还是遇到一些问题,把这些问题记录下来方便大家少走弯路. Android Studio版本是3.5.2,Gradle版本是3.5.1 1.点击[Run]->[Edit Configurations...]: 2.然后点击左上角的[+],选择[Remote

Android下hook点击事件的示例

Hook是一种思想,也就是将原来的事件,替换到我们自己的事件,方便我们做一些切入处理.目的是不修改原来的代码,同时也避免遗漏的N多类里面处理. 最近需要在现有的app中设置统计埋点.去业务代码里埋的话似乎耦合度太高.所以决定使用hook的方法对事件进行埋点处理. 这里先记一下对点击事件hook的基本流程. 1.先建一个代理类实现View.OnClickListener,用来做点击后的后续处理. import android.view.View; /** * 实现点击监听 */ public cl

Android fragment实现按钮点击事件的示例讲解

fragment无法直接进行点击事件,需要放到oncreatActivity中 代码如下: @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_first, null); return view; } 点击事件代码: @Override

Android 自定义按钮点击事件和长按事件对比

 Android 自定义按钮点击事件和长按事件对比 一个按钮同时实现点击和长按事件,有时候会有冲突,我们针对这一现象来自定义按钮来区分点击和长按事件 1.xml中 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="mat

Android 实现为点击事件添加震动效果

Android 点击Button 实现震动效果教程 Overview 在Android 的点击效果中,遇到震动效果的还是很多的. 接下来就让我们看一下如何实现震动效果. 所需要的权限 如果我们在开发中需要使用到我们的震动,那么我们就需要申请一下权限: <uses-permission android:name="android.permission.VIBRATE"/> 这样我们的权限就申请好了. 我们震动效果的帮助类 创建一个名为VibrateHelp的点击震动的帮助类.

iOS下Safari点击事件失效的解决方法

前言 本文主要给大家介绍了关于在iOS下Safari浏览器点击事件失效的相关解决方案,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 问题描述 当使用委托给一个元素添加click事件时,如果事件是委托到 document 或 body 上,并且委托的元素是默认不可点击的(如 div, span 等),此时 click 事件会失效. 可以使用下面的代码在 iOS 中进行测试. <!DOCTYPE html> <html> <head> <meta

用Kotlin实现Android点击事件的方法

近期,Google宣布Kotlin成为了Android一级开发语言.于是就刚刚简单的研究了一下,查资料的时候发现现成的资料还是很少的,于是决定自己记录一下,方便以后查看,也供其他人一个参考. 在android中,点击事件大致分为三种写法: 1. 匿名内部类. 2. Activity实现全局OnClickListener接口. 3. 指定xml的onClick属性. 今天用Kotlin实现这三种方式实现点击事件 匿名内部类:这种方式最简单 override fun onCreate(savedIn

Android使用RecyclerView实现自定义列表、点击事件以及下拉刷新

Android使用RecyclerView 1. 什么是RecyclerView RecyclerView 是 Android-support-v7-21 版本中新增的一个 Widgets,官方对于它的介绍则是:RecyclerView 是 ListView 的升级版本,更加先进和灵活. 简单来说就是:RecyclerView是一种新的视图组,目标是为任何基于适配器的视图提供相似的渲染方式.它被作为ListView和GridView控件的继承者,在最新的support-V7版本中提供支持. 2.

Android编程出现Button点击事件无效的解决方法示例

本文实例讲述了Android编程出现Button点击事件无效的解决方法.分享给大家供大家参考,具体如下: 遇到这样一个问题,给一个界面上方的按钮添加了点击事件,但死活没反应,而放在界面下方的3个按钮,都有相应点击事件,百度了一下无非有两种可能: 1.button没有初始化或者button初始化多次,导致混乱. 2.button点击事件写错,无法监听. 但我确定的是这些都是没有错的,后来找到的原因是下方的scroll布局覆盖了上方的button的布局,使用了fill_parent,所以获取不到点击

Android中点击事件的四种写法详解

Android中点击事件的四种写法 使用内部类实现点击事件 使用匿名内部类实现点击事件 让MainActivity实现View.OnClickListener接口 通过布局文件中控件的属性 第一种方法:使用内部类 基本步骤如下: 1. 新建一个MyOnClickListener类并实现View.OnClickListener接口 2. 重写View.OnClickListener接口中的OnClick(View view)方法 3. 给Button绑定一个监听器,并监听一个点击事件 示例代码如下

Android 中ListView的Item点击事件失效的快速解决方法

在平常的开发过程中,我们的ListView可能不只是简单的显示下文本或者按钮,更多的是显示复杂的布局,这样的话,我们就得自己写布局和自定义adapter了,一般是继承于BaseAdapter,示例代码见下方.写ListView的点击事件时OnItemClickListener,onItemClick方法没有执行,导致ListView中Item条目点击事件失效,而Item中的View点击事件可以在getView方法中进行处理.导致整个Item点击失效的原因多半是由于在[你自己定义的Item中存在诸