APK修改神器:插桩工具 DexInjector

本文介绍了一个针对Dex进行插桩的工具,讲解了一下直接修改Dalvik字节码和Dex文件时遇到的问题和解决方法

作者:字节跳动终端技术—— 李言

背景

线下场景中,我们经常需要在APK中插入一些检测代码,来实现一些记录方法调用耗时,或者增加一些打印日志的功能。目前的常规做法都是在编译期修改class字节码达到,例如byteX提供了方便的修改class框架。

但是,编译期修改灵活性不足,对于已经编译好的apk则无能为力,无法插桩或修改。导致很多业务方都要配置独立的jenkins打包后,才能触发进步一步的测试。一次自动化测试任务有将近一半的时间都消耗在打包过程中。

为了解决这个痛点,我们开发了一套直接针对APK(dex)插桩的工具,DexInjector。主要用来做一些日志、性能方面的数据采集和注入一些第三方工具,避免业务方二次打包,节省测试时间。

该方案已经用在日志旁路、网络数据抓取、第三方库注入,用户信息注入、日常调试等。

工具目前可以实现:

  • 方法前插桩

  • 方法后插桩

  • 初始化插桩

技术方案调研

调研了一下市面上现有的字节码修改方案。

smali

可以通过smali 和baksmali 工具将dex文件转换成可方便阅读的smali语法文件,但是smali的工具对smali字节码的解析是通过语法解析,如果要插入一个新的代码进去对寄存器等操作没有办法实现结构化操作。

redex

redex 支持通过配置在方法前进行插桩,可以通过实现pass来完成自己的插桩功能。但是功能实现有限,使用起来比较复杂,而且在执行之后插入了一些fb自定义的代码,但Redex 提供了一套强大的字节码修改能力,后续的版本会基于redex的字节码修改能力进行完善。

dexter

dexter 工具是google开发的一个类似dexdump的工具,但其内部实现了对dex文件结构和字节码指令的一套完整的操作api,轻量简洁,对字节码的操作可以达到ASM的体验。

综合,选用dexter对dex进行操作。

方案设计

需求

根据性能防劣化和流量统计的需求,都是在一个方法的方法体内部前后插入对其他方法的调用。以网络流量统计为例,需要在 okhttp3.RealCall.getResponseWithInterceptorChain 的方法内部开头插入一个方法来获取request请求的详细数据。

Response getResponseWithInterceptorChain() throws IOException {

    com.netflow.inject.hookRealCall(this);//插入的方法

    List<Interceptor> interceptors = new ArrayList<>();

    interceptors.addAll(client.interceptors());

          //.....省略部分代码

    return chain.proceed(originalRequest);

  }

Dex 插桩

基本流程

1. Dex文件分析

先要分析Dex文件格式,将其序列化成各种数据结构,Dex文件的结构可以参照官方文档(见参考文献)

2. 字节码解析

在code 段将二进制的字节码解析成可处理的数据结构

3. 字节码构造

按照字节码规范构造字节码指令,并插入到现有字节码的序列中即可完成字节码的插入。

4. 字节码序列化

将修改后的Dex结构重新计算Index,然后将各个数据Section序列化为Dex的文件格式。

功能需求

插桩支持两种能力,在一个方法的方法体前面和后面插入一个静态方法调用。

1. 方法体前面插桩

如果被插入的方法为实例方法,则方法的第一参数为 this,随后的参数和被插入的方法一致 ,如果方法是静态方法则插入的方法定义需要和被插入的方法参数类型和个数一致,举例:

public class Tracer{

        //被插入的方法,为实例方法

        private void MethodA(int a,int b){

        }


        //被插入的方法,为静态方法

        private static void MethodB(int a,int b){

        }

}


public class Hooker{

        //插入的方法

        private static void TestHookA(Tracer this_,int a,int b){

        }


        private static void TestHookB(int a,int b){

        }

}


插入后/

public class Tracer{

        private void MethodA(int a,int b){

                Hooker.TestHookA(this,a,b
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值