本文由嵌入式企鹅圈原创团队成员、阿里资深工程师Hao分享。

上篇文章《Android无线开发的几种常用技术》我们介绍了几种android移动应用开发中的常用技术,其中的热补丁正在被越来越多的开发团队所使用,它涉及到dalvik虚拟机和android的一些核心技术,现在就来介绍下它的一些原理。

本篇先介绍dexposed方案:https://github.com/alibaba/dexposed,它是手机淘宝团队使用的热补丁方案,后来开源到github上,取的名字dexposed表明了自己是基于大名鼎鼎的xposed hook方案,有饮水思源、回馈开源项目的意思。与xposed不同的是,dexposed是自己hook自己的应用,因此不需要root权限。

它的关键点是:在native层中先找到要修复的Java函数对应的Method对象,修改它变为native方法,把它的nativeFunc指向hookedMethodCallback。这样对这个java函数的调用就转为调用hookedMethodCallback这个native函数了,然后再用这个native函数回调java层自己实现的统一接口来处理。这个统一接口是XC_MethodReplacement类,它主要有beforeHookedMethod、afterHookedMethod和replaceHookedMethod等几个方法,前两个在执行原java函数前后做一些事,replaceHookedMethod则是替换原java方法。下面来详细分析下这个hook的过程。

一、DexposedBridge.findAndHookMethod

findAndHookMethod是hook原java方法的入口,它传入的参数是类Class和方法名,最后一个可变参数parameterTypesAndCallback,是用户实现的用于替换原方法的XC_MethodReplacement的实例。

先调用XposedHelpers.findMethodExact找到要hook的java方法,再用hookMethod进行真正的hook。

1.findMethodExact根据类名和方法名,用反射找到Method,并把它的属性改为可访问。

2.hookMethod先把hook成功后的callback、要hook的方法的参数和返回值类型保存到AdditionalHookInfo中,把它作为参数传给hookMethodNative。hookMethodNative是一个native方法,它的第3个参数slot表示该Method在class的方法表中所处的位置,在native的实现中会用到这个slot。

hookMethodNative的实现环境分dalvik和art,因为dexposed对art的支持不完善,同时art本身的原理和机制也是一个难点,所以本篇只介绍dalvik下的实现,art的有关内容以后有机会再作介绍。

二、hookMethodNative

每一个java的类在虚拟机的实现中都对应着一个C++的ClassObject。dvmDecodeIndirectRef是libdvm中的方法,它可以从java对象的间接引用获得ClassObject对象,再根据slot,用dvmSlotToMethod找到Method对象。这里的ClassObject和Method都是虚拟机内部用来表示class和Method的数据结构。

然后把原来的Method结构先备份到XposedHookInfo中,

XposedHookInfo的结构如下:

可见,它用originalMethod保存原来java方法的Method,用reflectedMethod保存原java方法在native的引用,注意这跟originalMethod中保存的Method对象不同。originalMethod中保存的Method可以理解为执行字节码的地址,而reflectedMethod中保存的是用来描述原java方法的一个ClassObject对象。它们两个在第五部分重新调用原java方法时会用到。

additionalInfo用来保存附加信息AdditionalHookInfo。接着使用SET_METHOD_FLAG宏把该方法设为native,让nativeFunc指向hookedMethodCallback,这样对该java方法的调用就会转为对hookedMethodCallback这个native方法的调用了。Insns指向这个方法的字节码,在这里把它改为指向hookInfo,实际上也就是originalMethod的字节码的地址。

三、hookedMethodCallback

hookedMethodCallback会回调java层的方法handleHookedMethod,最终会调用到前面说过的,在findAndHookMethod中传入的XC_MethodReplacement里的before、after方法。

这里首先把在hookInfo中保存的信息作为传给java层的handleHookedMethod的参数,然后用dvmCallMethod这个dalvik的函数调用xposedHandleHookedMethod这个java的方法。xposedHandleHookedMethod在初始化时已经被设置好了。

GetStaticMethodId是dvm中用来获取静态方法地址的函数,可见在初始化时,已经把java的静态方法handleHookedMethod的地址赋给了xposedHandleHookedMethod了。这里需要注意两点,一是这个时候已经不能像没hook之前那样,通过jni从native调java的函数,或者从java调native的函数,因为原来java方法的上下文已经被改变了(已经被保存在hookInfo中),所以后面只能通过libdvm中的方法,手动修改函数的指向。二是dvmCallMethod的第5和第6个参数originalReflected和original就是第二部分中保存的方法的引用和方法的字节码地址(original被直接转成了int型),后面第五部分中这两项还会被重新传回native层用来找到原java函数的入口。

四、handleHookedMethod

前面说到从native中调回java的方法handleHookedMethod,handleHookedMethod会根据需要,选择是否还调用原来的java方法,或者只调用XC_MethodReplacement里自己实现的before、after方法。

其中beforeHookedMethod方法默认会调用replaceHookedMethod,我们只要实现它即可替代对原方法的调用。

如果param.returnEarly为false才会调invokeOriginalMethodNative执行原来的方法。

默认的beforeHookedMethod中会调setParam,把param.returnEarly的值设为为true,这样就不会再调用原来的java方法了。

最后还要把返回值返回。

五、invokeOriginalMethodNative

如果在java层需要重新调用原java函数,那么在第二部分中把原java函数的信息备份到hookInfo中就能起到作用了。Java层的invokeOriginalMethod方法会调一个native的方法invokeOriginalMethodNative来实现这个过程。

这个native函数同样在初始化时就被设置好了:

要调用的invokeOriginalMethodNative在虚拟机中Method是dexposedInvokeOriginalMethod,这里传入了第二部分中备份的原java方法的对象引用reflectedMethod和字节码地址int型的original。

dvmSetNativeFunc的第2个参数是DalvikBridgeFunc类型的指针,这个函数会把dexposedInvokeOriginalMethod的nativeFunc指向xxx_invokeOriginalMethodNative。再次注意此时不能像平常的jni调用那样,java层的invokeOriginalMethodNative经过jni注册后能直接调到com_taobao_android_dexposed_DexposedBridge_invokeOriginalMethodNative了。

dvmInvokeMethod跟dvmCallMethod一样,都是dalvik中用来直接调Method的函数,这样就完成了对原java方法的调用。

最后一句话概括这种hook方法,就是通过把原java方法的类型改为native来把对java函数的调用转到native层,在native层用dvm的各种函数来操作Method的指针和对象来控制函数流程。

Android热补丁技术—dexposed原理简析(阿里Hao)的更多相关文章

  1. Android热补丁技术—dexposed原理简析(手机淘宝采用方案)

    上篇文章<Android无线开发的几种常用技术>我们介绍了几种android移动应用开发中的常用技术,其中的热补丁正在被越来越多的开发团队所使用,它涉及到dalvik虚拟机和android ...

  2. 阿里资深工程师分享支付宝热补丁技术—— AndFix原理

    本文由嵌入式企鹅圈原创团队成员.阿里资深工程师Hao分享. 上次我们介绍了用dexposed方案实施热补丁的原理,它本质上就是hook要修改的函数,这样一来在正式版本发布时就不能直接拿热补丁的代码集成 ...

  3. 支付宝热补丁技术— AndFix原理[阿里Hao]

    本文由嵌入式企鹅圈原创团队成员.阿里资深project师Hao分享. 上次我们介绍了用dexposed方案实施热补丁的原理.它本质上就是hook要改动的函数.这样一来在正式版本号公布时就不能直接拿热补 ...

  4. 微信开源的Android热补丁框架 Tinker

    前不久,微信开源了其Android热补丁框架Tinker,它的特别之处在于放在github.com/Tencent下面,是该账号下第一个正式的开源项目,可以看到腾讯对它的重视和认可. 早在6月份微信客 ...

  5. Android热修复技术原理详解(最新最全版本)

    本文框架 什么是热修复? 热修复框架分类 技术原理及特点 Tinker框架解析 各框架对比图 总结   通过阅读本文,你会对热修复技术有更深的认知,本文会列出各类框架的优缺点以及技术原理,文章末尾简单 ...

  6. Android热更新技术——Tinker、nuwa、AndFix、Dexposed

    一.热修复技术作用 线上app BUG紧急修复,不重新发版,不重新安装,在线远程修复问题 二.局限性与适用场景 补丁只能针对单一客户端版本,随着版本差异变大补丁体积也会增大: 补丁不能支持所有的修改, ...

  7. Android热修复技术原理详解

    阿里Dexposed -- native解决方案 原理: 直接在native层进行方法的结构体信息对换,从而实现完美的方法新旧替换,从而实现热修复功能   他的思想完全来源于Xposed框架,完美诠释 ...

  8. 深入探索Android热修复技术原理读书笔记 —— 热修复技术介绍

    1.1 什么是热修复 对于广大的移动开发者而言,发版更新是最为寻常不过的事了.然而,如果你 发现刚发出去的包有紧急的BUG需要修复,那你就必须需要经过下面这样的流程: 这就是传统的更新流程,步骤十分繁 ...

  9. Android 热修复技术(1)---原理

    热修复技术分为几部分: 原理介绍 Android HotFix源码分析 自定义框架 1.Android分包MultiDex原理 首先Dex是什么东西? Dex就是Window里面的exe文件 也就是可 ...

随机推荐

  1. 【344】Jupyter relevant problems

    参考:Jupyter Notebook Tutorial: The Definitive Guide 参考:ipython notebook 如何修改一开始打开的文件夹路径? Ref: Install ...

  2. MYsql系统函数和联合查询

    函数是SQL里的关键字,用于对字段里的数据进行操作.函数是一个命令,通常与字段名称或者是表达式联合使用,处理输入的数据并产生结果 常用函数 控制函数 字符串函数 数学函数 日期时间函数 汇总函数 CA ...

  3. 实现Action的三种方式

    实现Action的三种方式: 1.普通类  一般采用此种方法 2.实现Action接口 3.继承ActionSupport类

  4. conductor APIs

    任务和工作流元数据 端点 描述 输入 GET /metadata/taskdefs 获取所有任务定义 N / A GET /metadata/taskdefs/{taskType} 检索任务定义 任务 ...

  5. np.identity()

    二.np.identity()这个函数和之前的区别在于,这个只能创建方阵,也就是N=M 函数的原型:np.identity(n,dtype=None) 参数:n,int型表示的是输出的矩阵的行数和列数 ...

  6. 【校招面试 之 C/C++】第13题 C++ 指针和引用的区别

    1.指针和引用的定义和性质区别: (1)指针:指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元:而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已.如: int ...

  7. 【原创】java删除未匹配的文件夹FileFileFilter,FileUtils,删除目录名字不是某个名字的所有文件夹及其子文件夹

    闲着无聊,写了个小程序. 需求: 举例: 比如我的E盘有一个test的目录,test的结构如下: 要求除了包含hello的目录不删除以外,其他的所有文件夹都要删除. 代码如下: package com ...

  8. redis中multi和pipeline区别以及效率(推荐使用pipeline)

    手册得知 pipeline 只是把多个redis指令一起发出去,redis并没有保证这些指定的执行是原子的:multi相当于一个redis的transaction的,保证整个操作的原子性,避免由于中途 ...

  9. pycharm中的常用快捷键

    查找 Ctrl + F  替换 Ctrl + R 注释 Ctrl + /  去掉注释 Ctrl + / Function   Shortcut Use this shortcut to... Clos ...

  10. BZOJ1699: [Usaco2007 Jan]Balanced Lineup排队 - 线段树

    description 查询区间最大和最小 题解 线段树 愉悦身心啊 代码 #include<cstring> #include<cstdio> #include<alg ...