Android C/C++层hook和java层hook原理以及比较
作者:Denny Qiao(乔喜铭),云智慧/架构师。
云智慧集团成立于2009年,是全栈智能业务运维解决方案服务商。经过多年自主研发,公司形成了从IT运维、电力运维到IoT运维的产业布局,覆盖ITOM、ITOA、ITSM、DevOps以及IoT几大领域,为金融、政府、运营商、能源、交通、制造等上百家行业的客户,提供了数字化运维体系建设及全生命周期运维管理解决方案。云智慧秉承Make Digital Online的使命,致力于通过先进的产品技术,为企业数字化转型和提升IT运营效率持续赋能。
android java层hook机制
android dalvic虚拟机和JVM的区别
- Dalvik虚拟机并不是按照Java虚拟机的规范来实现的,与jvm并不兼容
- Java虚拟机运行的是Java字节码,而Dalvik虚拟机运行的则是其专有的文件格式DEX(Dalvik Executable)
- Davic读取的是dex文件,jvm读取的.class和jar文件
- Dalvik基于寄存器,而JVM基于栈
- 每一个Android应用都运行在一个Dalvik虚拟机实例里,而每一个虚拟机实例都是一个独立的进程空间。虚拟机的线程机制,内存分配和管理,Mutex等等都是依赖底层操作系统而实现的。所有Android应用的线程都对应一个Linux线程,虚拟机因而可以更多的依赖操作系统的线程调度和管理机制
- 有一个特殊的虚拟机进程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的实现方案
- 直接修改android SDK中的dex模块dx.jar,用asm修改dx.jar中加载class的入口API,在函数中加入我们hook机制代码,对每一个加载的class进行代码注入。最后以安装包的形式提供用户。
优点:一劳永逸,适用于所有的android 开发工具,适合eclipse,android studio,各种脚本编译等,开发工期短。在初期,我们采用这种方法,很快完成了产品的开发,推向市场。
缺点: 安装过程中需要替换用户android sdk中的dx.jar文件,属于侵入式安装,有一些用户不太接受。Android sdk不断的升级,我们也需要不断推出新的sdk,升级维护比较麻烦。
- 插件机制:需要实现不同的开发环境的插件: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基本流程:
- 通过读取 FILE *fd = fopen("/proc/self/maps","r") 内存映射表,找到so库在进程内存中的基地址。
- 通过基地址,读取并解析 SO 的结构,找到外部函数对应在GOT 表中的存放地址。
- 替换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/
部分大屏案例: