写点什么

Android C/C++ 层 hook 和 java 层 hook 原理以及比较

  • 2022 年 4 月 18 日
  • 本文字数:3513 字

    阅读完需:约 12 分钟

作者:Denny Qiao(乔喜铭),云智慧/架构师。


云智慧集团成立于 2009 年,是全栈智能业务运维解决方案服务商。经过多年自主研发,公司形成了从 IT 运维、电力运维到 IoT 运维的产业布局,覆盖 ITOM、ITOA、ITSM、DevOps 以及 IoT 几大领域,为金融、政府、运营商、能源、交通、制造等上百家行业的客户,提供了数字化运维体系建设及全生命周期运维管理解决方案。云智慧秉承 Make Digital Online 的使命,致力于通过先进的产品技术,为企业数字化转型和提升 IT 运营效率持续赋能。

android java 层 hook 机制

android dalvic 虚拟机和 JVM 的区别

  1. Dalvik 虚拟机并不是按照 Java 虚拟机的规范来实现的,与 jvm 并不兼容

  2. Java 虚拟机运行的是 Java 字节码,而 Dalvik 虚拟机运行的则是其专有的文件格式 DEX(Dalvik Executable)

  3. Davic 读取的是 dex 文件,jvm 读取的.class 和 jar 文件

  4. Dalvik 基于寄存器,而 JVM 基于栈

  5. 每一个 Android 应用都运行在一个 Dalvik 虚拟机实例里,而每一个虚拟机实例都是一个独立的进程空间。虚拟机的线程机制,内存分配和管理,Mutex 等等都是依赖底层操作系统而实现的。所有 Android 应用的线程都对应一个 Linux 线程,虚拟机因而可以更多的依赖操作系统的线程调度和管理机制

  6. 有一个特殊的虚拟机进程 Zygote,他是虚拟机实例的孵化器。每当系统要求执行一个 Android 应用程序,Zygote 就会 FORK 出一个子进程来执行该应用程序。它在系统启动的时候就会产生,它会完成虚拟机的初始化、库的加载、预置类库和初始化的操作。如果系统需要一个新的虚拟机实例,它会迅速复制自身,以最快的速度提供给系统

android 的启动流程

android 的编译结构图

android hook 原理

Javac 流程

Java 类文件是 8 位字节的二进制流


Android dalvik 虚拟机相比 jvm 有一个 dex 模块


目的是: 优化 class,减小体积,加快加载运行速度,我们 hook 的关键就是修改 class 文件,在原有 class 文件中增加,修改方法或者变量,以便加入我们的 hook 代码到 class 中,自动埋点。在 android 中 hook 的入口点是 dex 模块。

修改 class 的关键技术: asm 框架

  • ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。

  • ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。

  • Java class 被存储在严格格式定义的.class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。

  • ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

Android hook 的实现方案

  1. 直接修改 android SDK 中的 dex 模块 dx.jar,用 asm 修改 dx.jar 中加载 class 的入口 API,在函数中加入我们 hook 机制代码,对每一个加载的 class 进行代码注入。最后以安装包的形式提供用户。


优点:一劳永逸,适用于所有的 android 开发工具,适合 eclipse,android studio,各种脚本编译等,开发工期短。在初期,我们采用这种方法,很快完成了产品的开发,推向市场


缺点: 安装过程中需要替换用户 android sdk 中的 dx.jar 文件,属于侵入式安装,有一些用户不太接受。Android sdk 不断的升级,我们也需要不断推出新的 sdk,升级维护比较麻烦。


  1. 插件机制:需要实现不同的开发环境的插件:eclipse 插件,gradle 插件,各种自动化编译脚本的插件等。


基本原理: 在各个编译工具调用 dx 完成 dex 的过程中,通过编译环境提供的接口,调用我们 class 注入代码。


优点: 用户使用比较方便,不用修改用户 android SDk 环境,升级维护方便。比如 gradle 插件,版本放在 jcenter 仓库,直接配置就可以了。

实现方案的特点

针对各个开发环境,实现插件,在编译过程中对 class 文件进行 hook。这是一种静态 hook,不影响系统运行效率,而且对 android 的系统兼容性较好。


但是有一个缺点,不能 hook android sdk,只能 hook sdk 之上的代码,那么随着不同模块代码的升级和改变,我们的 hook 代码就不得不随之改变,而且需要不断适配新出现的第三发功能模块。不断地推出新的 sdk 版本支持这种变化。需要升级,维护。代码体积以及内存,CPU 等性能逐渐降低。

Android c/c++ hook

android 的 ndk 简介


NDK 是 Google 为 Android 进行本地开发而放出的一个本地开发工具,包括 Android 的 Na#ve API、公共库以及编译工具。


注意:NDK 需要 Android 1.5 版本以上的支持,NDK 与 SDK 是并列关系,DNK 是 SDK 的有效补充。



一个 android 工程包括 2 部分:java 部分和 ndk 扩展

So 库文件结构

  • ELF 文件格式提供了两种视图,分别是链接视图和执行视图

  • 链接视图是以节(secXon)为单位,执行视图是以段(segment)为单位。链接视图就是在链接时用到的视图,而执行视图则是在执行时用到的视图。上图左侧的视角是从链接来看的,右侧的视角是执行来看的。




我们比较关注的是执行视图中,段中.rel.plt 项:重定位的地方在.got.plt 段内(注意也是.got 内,具体区分而已)。 主要是针对外部函数符号,一般是函数。首次被调用时候重定位。首次调用时会重定位函数地址,把最终函数地址放到.got 内,以后读取该.got 就直接得到最终函数地址。

so hook 关注点

  • 导入表(GOT 表 hook),SO 引用外部函数的时候,在编译时会将外部函数的地址以 Stub 的形式存放在.GOT 表中,加载时 linker 再进行重定位,即将真实的外部函数写到此 stub 中。

  • HOOK 的思路就是:替换 GOT 表中的外部函数地址。可以理解为 hook 导入函数。

So hook 基本流程:

  1. 通过读取 FILE *fd = fopen("/proc/self/maps","r") 内存映射表,找到 so 库在进程内存中的基地址。

  2. 通过基地址,读取并解析 SO 的结构,找到外部函数对应在 GOT 表中的存放地址。

  3. 替换 GOT 表中的外部函数地址

NDK hook 的基本流程:

  • 主要原理:通过解析映射到内存中的 elf 的结构,解析出 got,然后进行 hook 重定位替换。其中必须要基于执行视图(ExecuXon View)进行符号解析;

  • ELF 文件格式是基于链接视图(Linking View),链接视图是基于节(SecXon)对 ELF 进行解析的。然而动态链接库在加载的过程中,linker 只关注 ELF 中的段(Segment)信息。

NDK hook 实现关键方法:

1、 从给定的 so 中获取基址,获取 so 句柄 ElfHandle:ElfHandle* handle = openElfBySoname(soname);


2、从 segment 视图获取 elf 信息(即加载到内存的 so):getElfInfoBySegmentView(info, handle);


3、根据符号名寻找函数地址 Sym:findSymByName(info, symbol, &sym, &symidx);


4、遍历链表,进行一次替换 relplt 表函数地址操作,其中需要使用 mprotect 修改访问内存,然后调用系统指令 清除缓存:replaceFunc(addr, replace_func, old_func)


5、遍历链表,进行一次替换 reldyn 表函数地址操作,其中需要使用 mprotect 修改访问内存,然后调用系统指令 清除缓存:replaceFunc(addr, replace_func, old_func))


6、释放资源,关闭 elf 句柄 :closeElfBySoname(handle);

c/c++层与 java 层 hook 的对比

目前的 android hook 方式具有以下缺点:


  • 实现复杂:需要支持各种开发环境,eclipse android studio,各种自动化编译工具,每种都比较复杂,开发和维护成本都比较高。需要支持各种用户使用到的第三方库。

  • 集成升级和维护:用户集成比较复杂,升级比较困难,需要不断的适配新出现的各种第三方库,因为我们是对用户代码进行 hook,而不是 SDK。

下一代的 android agent 实现构想

以 android naXve sdk 的思路实现,动态 hook app。


  • 优点: 针对 android sdk 进行 hook,acXvity 事件,网络,线程,崩溃,anr 等直接在 android sdk 的基础上进行 hook,而不是针对用户 app 的实现代码进行 hook,这样就可以大大减少对第三方库新增,升级等问题的适配。减少对系统资源的占用。

  • 集成方式: 透视宝 android sdk 的提供方式 so 库和 jar 包,以普通的 so 和 jar 的方式集成,不再需要各种集成插件的支持,支持网络动态升级和维护。

  • Hook 方式: 动态 hook,在 app 启动过程中进行 hook,可以各个功能点动态控制。

  • 性能: sdk 的体积会大大减少,对 CPU 的占用会降低

  • 兼容性: 现在的兼容性是对各个 android 系统版本之间的兼容性,以后只需要对新出现的 android 手机系统进行适配。

  • 缺点: 技术难度增加,需要进行大量兼容性测试!

写在最后

近年来,在 AIOps 领域快速发展的背景下,IT 工具、平台能力、解决方案、AI 场景及可用数据集的迫切需求在各行业迸发。基于此,云智慧在 2021 年 8 月发布了 AIOps 社区, 旨在树起一面开源旗帜,为各行业客户、用户、研究者和开发者们构建活跃的用户及开发者社区,共同贡献及解决行业难题、促进该领域技术发展。


社区先后 开源 了数据可视化编排平台-FlyFish、运维管理平台 OMP 、云服务管理平台-摩尔平台、 Hours 算法等产品。


可视化编排平台-FlyFish:


项目介绍:https://www.cloudwise.ai/flyFish.html


Github 地址: https://github.com/CloudWise-OpenSource/FlyFish


Gitee 地址: https://gitee.com/CloudWise/fly-fish


行业案例:https://www.bilibili.com/video/BV1z44y1n77Y/


部分大屏案例:



用户头像

全栈智能业务运维服务商 2021.03.10 加入

我们秉承Make Digital Online的使命,致力于通过先进的产品技术,为企业数字化转型和提升IT运营效率持续赋能。 https://www.cloudwise.com/

评论

发布
暂无评论
Android C/C++层hook和java层hook原理以及比较_Java_云智慧AIOps社区_InfoQ写作平台