当前位置: 萬仟网 > IT编程>开发语言>Java > 从零开始使用gradle配置即可执行的Hook库详解

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

2022年09月18日 Java 我要评论
背景有一天,老板突然找到小b说,隐私合规需要我们获取权限前,需要明确授权来意,这个你来跟一下吧!小b此时就可愁了,因为项目权限那么多,每个自己手动加上授权来意提示的话,可能会漏掉很多,工作量也大,这可

背景

有一天,老板突然找到小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库的资料请关注萬仟网其它相关文章!

(0)
打赏 微信扫一扫 微信扫一扫

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。

发表评论

验证码:
Copyright © 2017-2022  萬仟网 保留所有权利. 琼ICP备2022007597号