android 方法字节码插桩hook插件分享,全埋点,性能监控,线程,IO创建监控,一站式搞定,高可定制化,使用非常简单!

项目地址:https://github.com/miqt/android-plugin/blob/master/plugin/hook-method-plugin/readme.md

这是一个 android 方法字节码插桩hook的插件,在方法进入和方法退出时,将当前运行的所有参数回调到固定的接口中,利用这一点,可以进行方法切片式开发,通过配置Hook点,达到Hook监控的目的。

利用这个插件,可以实现:

  1. Android 全埋点,页面浏览,点击,等无痕埋点,按需配置Hook点即可,配置方法见下文
  2. Android 方法耗时,性能统计
  3. 各种拦截器,例如,拦截某方法,在某方法执行前,先判断权限或进行准备工作
  4. 在引用的第三方jar包中追加代码,增加创建线程,文件IO相关的监控等

效果展示

例如日常开发中,我们免不了可能要进行一些点击事件埋点的任务,如果我们利用这个插件,可以直接配置一次统计所有的点击事件埋点,下面来看一下具体做法:

  1. 先定义Hook点,这个hook点的目标是监控所有的点击事件。在 app module 中的 build.gradle 添加以下代码,详细字段含义会在下文说明。
apply plugin: 'com.miqt.plugin.hookmethod'
hook_method {
    buildLog = true
    injectJar = false //是否hook引用的第三方jar包
    enable = true
    hookTargets {
        hook_onclick { // <-- 定义Hook点
            interfaces = "android/view/View\$OnClickListener"//<-- hook条件1,继承了这个接口
            methodName = "onClick"                          //<-- hook条件2,方法名
            descriptor = "(Landroid/view/View;)V"           //<-- hook条件3,方法参数和返回值类型
            hookTiming = "Enter"                            //<-- hook条件4,指定是在方法进入时进行Hook
        }
    }
}

hook_method 是对插件的整体配置,hookTargets 是Hook点位的配置,编译时,会按照定义的筛选条件来进行筛选,四个条件同时满足,才会进行插桩。

注意,这里由于是字节码操作,因此这里的descriptor定义都是字段描述符,和字节码描述符,不知道的可以百度了解下,非常简单。如果你不想了解也没事,我后面有介绍有一种方法可以直接查看某个方法的字节码信息。利用@HookInfo注解

进行过以上配置后,我们随便写一个按钮,然后添加个点击事件。

public class MainActivity extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "onClick", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

点击同步按钮,编译一下项目,这时候,插件就会自动生成该点击事件的hook转发handler处理类:

类似这样的:HookHandler

这时候我们打开Apk反编译查看,就可以看到插桩代码了:

public class MainActivity extends Activity {
    /* access modifiers changed from: protected */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            /* class com.asm.code.MainActivity.AnonymousClass1 */

            public void onClick(View view) {
                HookHandler.hook_onclickEnter(this, "com/asm/code/MainActivity$1", "onClick", "[android.view.View]", "void", new Object[]{view});//<--插桩的代码
                Toast.makeText(MainActivity.this, "onClick", 0).show();
            }
        });
    }
}

到了这里,我们只需要在hook_onclickEnter这个方法中编写全局点击事件监控处理逻辑就可以了,所有的这一切都是插件自动完成的哦,我们只需要配置Hook规则就可以了,是不是非常简单。

示例配置传送门:https://github.com/miqt/android-plugin/blob/master/app/build.gradle

以此类推,该插件还可以通过自定义Hook规则,Hook项目中的任意方法的代码,达到切面编程的目的。

使用配置方法

  1. 添加maven仓库

    maven { url 'https://raw.githubusercontent.com/miqt/maven/master' }
    maven { url 'https://gitee.com/miqt/maven/raw/master' }
    

    还拉取不到库?

    maven { url 'https://raw.fastgit.org/miqt/maven/master' }
    

    或者,去我的仓库git地址下载下来,本地依赖。

    最开始我使用 jcenter() ,结果后来这个库官方弃用了,不过现在 0.3.5版本 仍然可以从这个库上拉取下来,之后版本我都使用gitee和github作为仓库存储地址了。

  2. 添加插件依赖

    项目根目录:build.gradle 添加以下代码

    dependencies {
        classpath 'com.miqt:hook-method-plugin:0.4.0'
    }
    

    对应 module 中启用插件,可以是application也可以是library

    apply plugin: 'com.miqt.plugin.hookmethod'
    hook_method {
        buildLog = true
        injectJar = false //是否hook引用的第三方jar包
        enable = true
        hookTargets {
            hook_onclick { // <-- 自定义Hook点
                interfaces = "android/view/View\$OnClickListener"//<-- hook条件1,继承了这个接口
                methodName = "onClick"                          //<-- hook条件2,方法名
                descriptor = "(Landroid/view/View;)V"           //<-- hook条件3,方法参数和返回值类型
                hookTiming = "Enter"                            //<-- hook条件4,指定是在方法进入时进行Hook
            }
        }
    }
    
  3. 对应 module 中添加类库依赖

    dependencies {
        implementation 'com.miqt:hook-method-lib:0.4.0'
    }
    

    项目 clean 然后运行,logcat 过滤 MethodHookHandler 就可以看到打印结果啦!

插件配置参数说明

hook_method {} 中的参数

这个里面是定义插件相关的配置。

参数 是否必填 类型 含义 默认值
enable bool 是否启用 true
runVariant 字符串 插件执行环境,有的时候我们只想在debug环境进行插桩,release环境不需要,这时候可以设置为:DEBUG,表示仅DEBUG执行时,运行插件,这个参数有四个枚举值:
DEBUG: 仅Debug运行时插桩
RELEASE: 仅RELEASE运行时插桩
ALWAYS: 所有运行情况 (默认)
NEVER: 从不插桩,等同于enable = false
ALWAYS
injectJar bool 是否对引用的所有第三方依赖jar,aar进行按照规则插桩hook false
buildLog bool 是否在.\app\build\plugin 文件夹输出插件编译日志,编译有错误时,请打开 true
handler 字符串 插桩Hook转发的Handler接收类,也就是插桩处理类,例如传xxx.xxx.A,则编译后会自动生成A.java这个类,并自动生成方法,类似这种: com.miqt.hookplugin.HookHandler
handlerDir 字符串 插桩Hook转发的Handler接收类在那个路径生成,默认是.\src\main\java 一般不需要自定义 .\src\main\java

hookTargets {} 中的参数

结构:

hook_method {
    hookTargets {
        name1 {
            条件1 = value1
            条件2 = value2
            条件3 = value3
        }
        name2 {
            条件1 = value1
            条件2 = value2
            条件3 = value3
        }
    }
}

这里是定义hood点位筛选条件的,逻辑是,必须满足参数 1,2,3设置的条件,才会插桩

注意:如果设置:hook_All{ } 这种无条件参数的,意思就是所有的方法都插桩。

参数 是否必填 类型 含义 默认值
name 字符串 上文的name1,name2位置,意思是hook点命名,这个命名最终会是这个点位的Hook接收方法名,可以自己随便定义 null
access int 限定条件之:方法访问修饰符,比如private,public,static,final,使用`Opcodes.ACC_PRIVATE Opcodes.ACC_STATIC`这种形式来定义 -1
interfaces 字符串 限定条件之:方法所在类必须实现了什么接口,例如OnClickListener接口就这么写:interfaces = "android/view/View\$OnClickListener"这里不用点,用/表示这是字节码标识符。给方法或类加@HookInfo注解,插件会自动在编译控制台和编译Log日志文件中展示出对应的字节码标识符。 null
superName 字符串 限定条件之:方法所在类必须是superName的子类 null
className 字符串 限定条件之:方法所在类名称,这里是全称,例如com/xxx/A,支持正则限定 null
methodName 字符串 限定条件之:方法名,支持正则限定 null
descriptor 字符串 限定条件之:方法字段描述符,不知道咋写的用@HookInfo标记看下输出就知道了。 null
annotation 字符串 限定条件之:方法或所在类必须包含这个注解 null
signature 字符串 限定条件之:方法或所在类必须包含这个泛型 null
exceptions 字符串 限定条件之:方法必须符合抛出某异常的形式 null
hookTiming 字符串 限定条件之:在方法进入时插桩还是退出时
Enter:方法进入时
Return:方法退出时
`Enter
Return`:进入和退出时(默认) Enter|Return

内置注解

@HookInfo: 输出对应字节码限制条件信息
@HookMethod: 标记hook方法,不需要写hookTargets,可以理解为内置的
@HookMethodInherited: 标记hook方法,有类继承性,不需要写hookTargets,可以理解为内置的
@IgnoreMethodHook: 标记忽略方法,优先级最高

读都读到这里了,帮我点个star吧,你的肯定是我持续维护的动力!

如果有使用问题欢迎提交Issues ,或者加我微信一起交流,微信号在我的主页。

致谢

这个插件是借鉴了很多大佬的代码,并结合自己的想法进行了一些调整,在此感谢他们付出的努力。

https://github.com/novoda/bintray-release
https://github.com/JeasonWong/CostTime
https://github.com/MegatronKing/StringFog

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • title: Android AOP之字节码插桩author: 陶超description: 实现数据收集SDK时...
    陶菜菜阅读 38,054评论 40 182
  • 篇头语 应师傅指导,最近研究了一下从Gradle编译入手,实现字节码插桩,进而实现一些功能,其实网上相关文章也不算...
    小熊兜里有糖阅读 3,335评论 5 18
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • 在这之前你需要了解的DSL语法原理与常用API介绍、gradle打包流程、gradle生命周期和hook技术、tr...
    不仅仅是代码阅读 783评论 0 1
  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 6,018评论 0 4