为什么使用汇编可以 Hook objc_msgSend(下)- 实现与分析

本文详细分析了使用汇编 Hook objc_msgSend 的过程,包括 Hook 思路、fishhook 的实现原理以及内联汇编实现 Hook 的方法。fishhook 通过修改 Mach-O 的 __DATA segment 来完成 C 方法的 Hook,而内联汇编则用于实现 Hook 前后的上下文管理和方法调用。文章还介绍了 Inline Hook 的定义和 fishhook 如何在运行时进行函数重定向。
摘要由CSDN通过智能技术生成

背景

在上一篇「为什么使用汇编可以 Hook objc_msgSend(上)- 汇编基础」中,我们介绍了 ARM64 中的汇编基础,并且知道了在汇编当中,调用一个方法的前后在栈空间是怎样表现的,以及内存中的几个特殊寄存器是如何操作的。

今天这篇文章,我们来详细详细分析一下使用汇编来 Hook objc_msgSend 的全部流程。

Hook 思路梳理

对于 objc_msgSend 这个我们要 Hook 的方法,我们首先要搞清楚,这是一个什么样的方法?我需要用什么方案才能 Hook 到它的入口。首先我们来整理以下我们拥有的 Hook 方案:

  • 基于 Objective-C Runtime 的 Method Swizzling:也就是我们经常使用的 class_replaceMethod 方法;

  • 基于 fishhook 的 Hook:由于在 Mach-O 当中,有 Bind 和 Lazy Bind 的两个概念,所以 Facebook 通过修改 __la_symbol__nl_symbol 两个表的指针,在二次调用的时候,直接通过 __la_symbol_ptr 找到函数地址直接调用,从而不用多次繁琐的进行函数寻址;

  • 基于 Dobby 的 Inline HookDobby 是通过插入 __zDATA 段和 __zTEXT 段到 Mach-O 中__zDATA 用来记录 Hook 信息(Hook 数量、每个 Hook 方法的地址)、每个 Hook 方法的信息(函数地址、跳转指令地址、写 Hook 函数的接口地址)、每个 Hook 的接口(指针)。__zText 用来记录每个 Hook 函数的跳转指令。Dobby 通过 mmap 把整个 Mach-O 文件映射到用户的内存空间,写入完成保存本地。所以 Dobby 并不是在原 Mach-O 上进行操作,而是重新生成并替换。 关于 Dobby 中的奇技淫巧还有很多,如果有可能后续会出一个分析文章(插旗子)。

当然,成熟的 Hook 方案还有很多,并且这些经常出现在逆向工程中,我这里只是列举了最常用的三个。

什么是 Inline Hook

首先给个定义:Inline Hook 就是在运行的流程中插入跳转指令来抢夺运行流程的一个方法Inline Hook 示意图

上图展示了 Inline Hook 大致的思路:

  1. 将原函数的前  N 个字节搬运到 Hook 函数的前 N 个字节;

  2. 然后将原函数的前 N 个字节填充跳转到 Hook 函数的跳转指令;

  3. 在 Hook 函数末尾几个字节填充跳转回原函数 +N 的跳转指令;

以上的 N 有多大,取决于你的跳转指令写得有多大(占用了多少指令)。

相较与 Inline Hook, fishhook 使用的是很 Trick 的方式,通过劫持 stub 从而达到替换的目的

在罗巍的「iOS 应用逆向与安全之道」中将 fishhook 归类成 Inline Hook。从广义的定义上来说,只要完成重定向到我们自己的方法,并在远方法前后可定制处理就可以算作 Inline Hook。但是我和页面仔讨论的结果是,这里的 Inline 应该要理解为 Inline Modification,这种技术通常如上图所示,覆盖方法开头指令中的前几个字节,完成 Hook 方法的重定向工作。

fishhook 完成跳转

汇编实现的 objc_msgSend 为什么可以当作 C 方法?

通过查看 objc_msgSend ,我们知道 Runtime 的 Method Swizzling 并不适用,因为它并不是 Objective-C 方法,调用时并不会有我们经常说的“消息转发”;通过查看 Runtime 源码,我们发现 objc_msgSend 是使用纯汇编实现函数,通过汇编文件我们可以看到以下定义:

ENTRY  _objc_msgSend

这里的 ENTRY 是什么意思呢?在文件中继续搜索 ENTRY 我们找到了这么一个宏:

.macro  ENTRY /* name */
	.text
	.align 5
	.globl    $0
$0:
.endmacro

这里定义了一个汇编宏,表示在 text 段定义一个 global 的 _objc_msgSend$0 其实就是这个宏传入的参数,也就是一个方法入口。我们可以手动将这个宏来展开:

.text
.align 5
.globl _objc_msgSend

; ...

这里我们发现,在第三行的位置通过 C 的 name mangling 命名规则,将符号 _objc_msgSend 映射为 C 的全局方法符号。也就是说,这段汇编可以通过头文件声明,便已完成了 C 的函数定义。我们在后续处理的时候可以将其视为 C 方法。

当然我们也可以使用 MachOView 来验证这个符号名。

验证 _objc_msgSend 为全局 C 符号

这里如果不太明白如何使用纯汇编实现 C 方法,可以看高级页面仔的这篇文章「在Xcode工程中嵌入汇编代码」

fishhook 实现的基础

既然我们将 objc_msgSend 已经视作 C 方法,那么我就可以使用 fishhook 来完成 Inline Hook 的第一步:跳到 Hook 方法。fishhook 是如何做的呢?它是在什么阶段完成这个动作的?来看下图:

fishhook 调用时机

我们知道,Apple 自身的共享缓存库其实不会编译进我们自己的 Mach-O 中的,而是在 App 启动后的动态链接才会去做重绑定操作。这里我们要如何去验证呢?首先我们写一个 fishhook 的 demo:

#import "ViewCon
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值