文章目录 一、" 字节码插桩 " 技术简介 二、AspectJ 插桩工具 三、ASM 插桩工具 一、" 字节码插桩 " 技术简介 ---- 性能优化 , 插件化 , 热修复 , 等技术都需要用到 " 字节码插桩 ; 通过 AOP 面向切面编程 , 完成上述操作 ; " 字节码插桩 " 技术应用 : 代码生成 : 编译时生成代码 , 提高开发效率 , 减少手工工作量 , 降低出错概率 ; 代码修改 : 为某些三方库添加崩溃 try catch 异常捕获机制 ; 代码监控 : 编译时插桩 , 监控应用各种性能 , 如页面打开时间 , 页面停留时间 ; 友盟应该用了该技术 ; 代码分析 : 使用编译时字节码插桩技术 , 自定义代码检查 字节码文件打包到 .dex 文件中 ; 二、AspectJ 插桩工具 ---- AspectJ 插桩工具 : 使用简单 : 使用 AspectJ 插桩工具修改字节码文件 , 不需要了解 .class , 生成的字节码肯定大于之前的字节码文件 , 对原来的性能也有一定影响 , 修改后的字节码文件 性能低于 修改前的字节码文件 ; 三、ASM 插桩工具 ASM 插桩工具 : 操作灵活 : 可以在字节码
如何使用ASM给android的某个函数做插桩? io implementation 'org.apache.directory.studio:org.apache.commons.io:2.4' //引入ASM相关api,这是我们插桩的关键 ,要靠他实现方法插桩 implementation "org.ow2.asm:asm:$asmVersion" implementation "org.ow2.asm:asm-util: { // 判断方法name是onCreate if (name.startsWith("onCreate")) { //插桩函数的实现 好了,通过ASM的一顿操作,已经将代码插入到了MainActivity的onCreate函数中,我们如何验证?
前言本文代码案例基于Api13。 ,使代码更易维护和扩展。 提到AOP,大家最常见的就是日志的记录,AOP可以在不修改原有业务代码的情况下,通过代码插桩的方式,为应用程序添加日志记录功能;当然了,除了日志记录功能,相信大家肯定见过很多的三方的统计,比如友盟,神策等等 AOP,说的简单直白一点,就是,它可以实现对原有的对象方法,在执行前后,进行插桩,同样,在鸿蒙的开发中,我们也可以直接对其方法进行替换操作,如何实现,在Api 11之后使用Aspect对象即可。 相关总结正确的运用AOP,可以提升代码的模块化、复用性、可维护性和灵活性,同时降低了耦合度,使系统更易于扩展和维护。
方案:通过Aspect.addBefore对自定义方法进行执行前插桩记录方法调用前时间、Aspect.addAfter对自定义方法进行执行后插桩记录执行后时间,以统计方法耗时及调用次数。 核心代码:function addTimePrinter(targetClass: Object, methodName: string, isStatic: boolean) { let count 方案:在EntryAbility的onCreate方法中对UIAbilityContext类的startAbility方法进行插桩,以获取Want参数的bundleName属性。 类对象,然后在onCreate方法中完成插桩操作。 例:对EntryAbility的onCreate方法进行插桩,当本应用被其他应用调用触发onCreate回调,以获取Want参数的bundleName属性。
0、Clang插桩原理 Clang在优化过程中,可以自己定义Pass来优化代码 1、编译插件的工具准备 1.1 新建文件夹llvm,下载LLVM(预计大小 648.2 M) $ git clone https xode使用clang的替代版本 3.5 将Enable Index-While-Building Functionality值改为NO,否则会报错 3.6 执行可以获取到log输出方法执行情况 4、插桩的其他思路 5、插桩的其他思路:SanitizerCoverage OC 的方法调用在底层都是objc_msgSend函数。 所以,如果能够Hook它,获取每个调用objc_msgSend的方法名,也能够达到插桩效果。
1.什么是编译插桩? 顾名思义,所谓的编译插桩就是在代码编译期间修改已有的代码或者生成新代码。 ? Java-字节码-dex 如图,这是Java代码的编译流程。 从图中可以看出,编译插桩可以从两个方面着手 Java 文件。类似 APT、AndroidAnnotation 这些代码生成的场景,它们生成的都是 Java 文件,是在编译的最开始介入。 可以操作“.class”的 Java 字节码,也可以操作“.dex”的 Dalvik 字节码,这取决于我们使用的插桩方法。 缺点是:如果使用者对字节码不熟悉的话不好操作 3.掌握插桩应该具备的基础知识 (1)熟练掌握字节码相关技术。 如AspectJ,ASM 4.插桩实践 字节码插桩--你也可以轻松掌握,Android字节码插桩——详细讲解 附带Demo 其实这两篇文章我只是大致理解了,并没有运行。
* 在各个代码块进行基本相同的代码调用,侵入性高,如果后期进行更换SDK,有可能会进行大量改动 * 手动进行埋点可能导致认为疏忽造成的埋点丢失 * 只能根据埋点进行用户行为回溯,有些细节和流程无法衔接上 方案4:字节码插桩 字节码函数插桩目前有以下两种框架 ASM 思路:应用程序打包成APK之前会先编译成.class文件,然后打包成dex,最后组成apk。 ASM框架进行字节码函数插桩 ============== 经过上述方案的对比,最终采用ASM进行字节码插桩。主要是对代码的侵入低,可定制化配置(过滤采集页面,过滤时长,配置页面映射等)。 下图箭头指向处就是进行函数插桩的位置。 相关视频推荐: 【Android组件化设计】字节码插桩优化框架初始化速度 本文转自 https://juejin.cn/post/6844904194445426702,如有侵权,请联系删除。
Clang代码覆盖率检测(插桩技术) Clang的全称是C Language Family Frontend for LLVM,即基于LLVM的C系列语言的前端编译器。 本篇文章,我们主要介绍Clang内置的一个简单的代码覆盖率检测功能,对于iOS开发来说,此功能更多用于Objective-C的方法插桩,为二进制重排提供支持,优化应用启动速度。 ,每个代码块开始调用时,都会首先调用此插桩函数。 ,支持的级别参数有三种: 1. edge:默认的级别,细粒度最高的级别,函数,Block和代码块都会被插桩。 2. bb:基础的块级代码会被插桩。 3. func:仅仅函数块会被插桩。
今天推荐一个群友开源的插桩框架 —— Mamba ,想学习 Gradle Plugin 和 ASM 的朋友们可以关注一波。 ,则对方法进行插桩,插桩效果如下: System.out.println("========start========="); TimeUtil.setsStartTime("newFunc", System.nanoTime 4、Mamba Mamba 的实现类似于 Matrix,但插桩的内容不是 methodId,而是当前的类、方法名和方法参数,插桩效果如下: public void test() { 性能 大家可能会比较关心插桩后的性能问题,我这里列一下测试用例和结果: 1、方法插桩,多次测试耗时为 0 毫秒 2、方法参数插桩,多次测试,耗时大约在 2 毫秒 注意 方法的参数收集目前只支持最多 插桩时还需要为 Mamba 实现类配置 exclude,避免插桩导致方法循环调用 总结 总的来说,各个方案实现都差不多,略微的差异在于业务的不同实现。
字节码注入与控制流 1 注入方式 JaCoCo是一个被广泛使用的JAVA覆盖率统计工具,它利用ASM库,通过注入字节码的方式来修改和生成java字节码,从而记录程序的执行数据,但它不会改变原有代码的行为 2 Probe探针组成 Jacoco是通过一个Probe探针的方式来注入的,探针是字节指令集插入到java方法中,程序执行后可以被记录,它不会改变原有代码的行为。 probearray 将一个引用类型本地变量推送至栈顶 xPUSH probeid 将一个常量值推送至栈顶 ICONST_1 将int型推送至栈顶 BASTORE 从操作数栈存存储到数组 根据代码逻辑的不同
由于通常您关心的是应用程序中的热点路径,因此您正在为位于代码性能关键部分的内容进行插桩化。在热点路径中注入插桩化代码可能很容易导致整体基准测试减慢2倍。 上述所有内容增加了实验之间的时间,消耗了更多的开发时间,这就是为什么工程师如今很少手动插桩化他们的代码的原因。然而,自动化代码插桩化仍然被编译器广泛使用。 编译器能够自动对整个程序进行插桩化,并收集有关执行的有趣统计信息。自动插桩化最广泛的用例是代码覆盖分析和基于性能指导的优化比如PGO。 在谈到插桩化时,重要的是要提到二进制插桩化技术。 二进制插桩化的思想类似,但它是在已构建的可执行文件上完成的,而不是在源代码级别上。有两种类型的二进制插桩化:静态(在构建之前完成)和动态(在程序执行时根据需要插入插桩化代码)。 与代码插桩化类似,二进制插桩化只允许对用户级代码进行插桩化,而且可能非常慢
OpenTelemetry 指标插桩 OpenTelemetry 提供了几种类型的插桩,用于在应用程序中捕获和记录指标数据。 这些插桩具有不同的用途,适合各种用例: 时间序列: 时间序列插桩跟踪指标值随时间的演变。它们对于监控应用程序行为的趋势和模式非常有用。时间序列指标的示例包括CPU 使用率、内存消耗和请求延迟。 累加: 累加插桩通过将新的数据点添加到现有总数来累积指标值。它们非常适合测量累积量,例如处理的请求总数或传输的数据总量。 同步: 同步插桩在应用程序代码中被调用时立即记录指标数据。 异步: 异步插桩将指标数据的记录推迟到单独的线程或进程,允许主应用程序逻辑在不等待指标收集完成的情况下继续执行。这种方法最大程度地减少了性能影响,但可能会导致指标报告略有延迟。 使用哪种插桩类型? 选择正确的插桩类型取决于应用程序的具体要求和特性。
license授权证书校验模块,实行一台机器一个授权证书,初步方案是增加拦截器针对全局请求进行拦截校验,评估后认为校验方式单一,应该增加重要工具类,业务service实现中每个方法的进行校验,因为涉及代码量较大硬编码工作困难 ,故选择通过自定义maven插件在编译期间进行动态代码插桩操作 项目配置 新建maven项目设置打包方式 <packaging>maven-plugin</packaging> 增加依赖项 <! catch (Exception e) { exceptioncount++; e.printStackTrace(); } } ASM插桩 新建ClassVisitor重写visitMethod方法来过滤访问需要插桩的方法,需要排除自带的init方法 public class MethodCoverageClassVisitor extends endsWith(".class")) { //class筛选 boolean flag = false; //自定义的class文件筛选条件代码
文章目录 一、GOT 表拦截与插桩拦截 二、插桩拦截简介 三、插桩拦截涉及的 ARM 和 x86 中的跳转指令 一、GOT 表拦截与插桩拦截 ---- 函数拦截有 2 种方式 : 使用 GOT 表进行函数拦截 : 修改 GOT 表实现函数拦截 ; 插桩拦截 : 该方法就是 在实际被调用的函数中添加跳转代码实现函数拦截 ; 在 【Android 逆向】函数拦截原理 ( 通过修改 GOT 全局偏移表拦截函数 | 通过在实际被调用的函数中添加跳转代码实现函数拦截 ) 博客中分析到 , " 使用 GOT 表进行函数拦截 " 方法不能保证 100% 成功 , 插桩拦截 可以实现 100% 拦截成功率 ; " 插桩拦截 " 由于需要 修改代码 , 可能会 被反调试工具发现 , 如果是游戏 , 账号估计就凉了 ; 反调试工具 不可能监控所有的函数 , 一个应用的函数个数都是以万进行计算的 ; 二、插桩拦截简介 ---- 三、插桩拦截涉及的 ARM 和 x86 中的跳转指令 ---- 插桩拦截 时 , 在 实际函数 入口处写入的 跳转代码 就是 汇编中的 跳转指令 ; 跳转指令 可以理解为 " 指令 " 或 " 机器码
插件化简介 ( 组件化与插件化 ) 【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 ) 【Android 插件化】插件化原理 ( 类加载器 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 运行应用 | 代码整理 ) ---- 文章目录 Android 插件化系列文章目录 一、编译 " 插件 " 模块 二、首次编译运行 " 宿主 " 模块 三、第二次运行 " 宿主 " 模块 四、" 宿主 " 模块代码 五、" 插件 " 模块代码 六、" 依赖库 " 模块代码 1、插件 Activity 接口 2、插件 Activity 基类 3、代理 Activity ( 桩 ) 4、插件管理器
鸿蒙 提供运行时AOP的能力,系统提供接口(见后文介绍),可以分别可以对类方法做执行前插桩、执行后插桩、替换方法实现。使用场景/方法举例:在应用启动的地方调用上述接口对类方法进行埋点或者替换实现。 接口介绍:Aspect类用于封装提供切面能力(Aspect Oriented Programming,简写AOP)的接口,这些接口可以用来对类方法进行前后插桩或者替换实现。 targetClass: Object, methodName: string, isStatic: boolean, instead: Function): void; }这三个接口可以分别可以对方法做执行前插桩 、执行后插桩、替换方法实现。 接口使用实例:1、addBefore–执行前插桩static addBefore(targetClass: Object, methodName: string, isStatic: boolean,
文章目录 一、ARM 架构下的插桩拦截 二、完整代码示例 一、ARM 架构下的插桩拦截 ---- ARM 架构下的跳转指令 : 下面的二进制数都是十六进制数 ; 32 位指令 ; 04 F0 1F 00 00 00 00 , B target ; B 指令是无条件跳转指令 , 04 F0 1F E5 是对应的机器码 ; 在 【Android 逆向】函数拦截 ( 修改内存页属性 | x86 架构插桩拦截 pStub; 最后 , 将 arm 跳转指令二进制机器码拷贝到函数开始位置 ; /* 将机器码复制到函数开始位置 */ memcpy(pFunc, code, sizeof(code)); 二、完整代码示例 ---- 下面是 插桩函数拦截 的代码 , 兼容 x86 与 arm 架构 ; 注意 : 写完之后推荐刷新 CPU 高速缓存 , 调用 cache_flush 系统调用函数 ; /* * unsigned pFunc 地址 - 5 * 跳转指令 跳转的是 偏移量 , 不是绝对地址值 */ *(unsigned*)(code + 1) = pStub - pFunc - 5; /* 将跳转代码拷贝到
的异常,此时 TinyInst 将从执行流的位置按 basic-block(基础块) 解析代码指令,在基础块头部添加插桩代码、修正末尾的跳转指令偏移,再将整块指令代码写入工作内存空间中,随后跟随跳转指令 内存空间内申请空间,作为二进制重写的工作内存空间,其申请的大小为 原始代码段大小 * 插桩指令放大系数4 + 全局跳转表大小,其中全局跳转表项为固定值 0x2000 个,随后通过 InitGlobalJumptable 全局跳转表」进行分析,接下来将先分析插桩操作。 7.二进制重写 参考资料 TinyInst 采用的是二进制重写的方案进行插桩,紧接着上文的代码逻辑继续跟进;TinyInst 还原模块入口点的断点后继续执行目标程序,执行流抵达目标模块后抛出 0xC0000005 : 首先使用 InstrumentBasicBlock() 在基础块的头部写入插桩代码 mov 指令,这里的地址是 TinyInst 在目标模块的工作内存空间初始化的覆盖率 bitmap, 并且 TinyInst
文章目录 前言 一、函数拦截需要的几个参数 二、插桩前先保存实际函数入口 6 字节数据 三、在插桩的函数入口写入跳转指令 | 构造拼接桩函数 前言 【Android 逆向】函数拦截实例 ( 函数拦截流程 6 字节数据 ---- 插桩前先 保存函数的入口 6 字节数据 , 因为之后插桩 , 会使用跳转代码 0xE9,0,0,0,0 覆盖函数入口内存 , 被破坏的实际函数 最终还是要执行 , 需要拷贝一下 , 供之后实际函数调用使用 ; unsigned char code[64] = { 0 }; /* 插桩前先保存函数的入口 6 字节数据 , 因为之后插桩 , * 会使用跳转代码 0xE9,0,0,0,0 ---- 这里执行了 2 次插桩操作 : 第一次是实际函数跳转 : 函数插桩 , pApi 是实际函数 , pUser 是插桩后跳转到的拦截函数 ; 该情况是在 clock_gettime 函数的入口处插入跳转代码 架构插桩拦截 ) 【Android 逆向】函数拦截 ( ARM 架构下的插桩拦截 | 完整代码示例 ) 博客中有详细的说明 , 先修改内存页属性 , 然后直接修改内存 , 写入跳转汇编指令对应的二进制机器码数据
该类通过实现 ClassFileTransformer 接口,在类加载阶段对字节码进行实时插桩,注入覆盖率统计逻辑。 重转换触发的类对象,类加载时为 null protectionDomain 类的保护域 classfileBuffer 原始类文件字节数组(不可修改) 2.2 方法特性说明 返回值规则 返回修改后的字节数组:应用插桩逻辑 (快速返回 null) 确保线程安全(多个类可能并行加载) 合理使用类过滤机制(通过 className 快速筛选目标类) 六、应用场景分析 场景 触发条件 典型应用 新类加载 首次加载类文件 启动时插桩 动态类重定义 使用热部署工具 在线调试时代码替换 类重转换 调用 retransformClasses 覆盖率数据动态重置 通过深入理解 CoverageTransformer 的工作机制,开发者可以更好地进行定制化插桩 ,实现精准的代码覆盖率统计和动态分析。