Fishhook 原理浅析

一、初识 fishhook

Fishhook 是 facebook 的开源库。官方描述,它的作用是:

... enables dynamically rebinding symbols in Mach-O binaries running on iOS in the simulator and on device

支持对 iOS 模拟器和设备上运行的 Mach-O 二进制文件,动态地重绑定其中的符号

更浅显的解读是,如果你的程序调用了动态库中的函数 A,可以通过 fishhook,在运行时将“调用函数 A”改为“调用函数 B”。

Fishhook 的源码只有 250 行,因此可以方便地接入到业务中。

二、C 函数调用也能篡改?

我们能理解 OC 中 method swizzling 的原理。因为 OC 有一套运行时机制,因此给 OC 对象 ​obj​ 发送消息 ​call​ 时,要经过 runtime 的匹配,才能在运行时得出 ​call​ 方法的地址,让指令跳转到 ​call​ 方法内部。因此,只要在 runtime 匹配的过程中做手脚,就可以将 ​call​ 方法篡改为其他方法。

但是 C 语言中,调用某个函数,指令跳转到某个地址,这是在编译链接时就决定的。看起来我们并不能在运行时改变这个地址。

但实际上,如果调用的函数是动态库中的,那么,这个二进制程序,在启动前也并不知道动态库中这个函数的地址。这个地址也是运行时计算得到的。
因此这就给 fishhook 提供了机会。

三、正常函数的调用过程

在了解 fishhook 做了什么之前,我们先一步一步看看,调用一个动态库里的 C 函数,会经过哪些步骤。

3.1 MachO 背景知识

在开始之前,我们先回顾一下 MachO 的背景知识。

进程是特殊文件在内存中加载得到的结果

一个 iOS app 的进程,就是 MachO 文件在内存中加载得到的结果。

比如这是一个 arm64 架构下的 MachO 文件。Mach 加载器会读取 MachO 文件中的加载命令(Load Commands),这些命令决定了如何布局这个进程的内存空间。

(这里需要注意:内存地址和 MachO 文件的 offset 是两个概念,对于 TEXT 和 DATA 段中的东西,arm64 架构下,可以认为 0x100000000 + offset + ASLR = 内存地址)

3.2 ​printf()​ 调用的过程

我们以 ​printf()​ 这个函数举例。

以下分析均在 arm64 架构下进行。

调用 ​printf()​ 的汇编是什么样

我们知道,当发生一个函数调用时,编译器和静态链接器,会把这个函数调用,成“跳转到某个地址继续执行程序”。

那么我们首先看看调用 ​printf()​ 的汇编是什么样的。

首先,我们将 Xcode 设置为,断点时查看汇编。

然后在调用 ​printf()​ 时打一个断点。运行到这个断点时,我们看到了这样一行汇编代码:

0x10044e694 <+72>: bl 0x10044ea78 ; symbol stub for: printf

意思是跳转到 ​0x10044ea78​ 地址继续执行,并且这个地址,代表了 printf 函数。

​0x10044ea78​ 地址,实际是什么呢?

由于 ASLR 技术,我们需要减去 image 的起始地址,才能得到 rebase 之前的地址,用于和 MachO 文件对比。通过 ​image list​ 命令得到各个 image 的起始地址后,我们发现,​0x10044ea78​ 落在起始地址为 ​0x0000000100448000​ 的主二进制中,二者相减,偏移量为 ​0x6a78​

​0x6ac8​ 位于 TEXT 段 __stubs 节。说明,调用 ​printf()​ 首先跳转到了代码段的某个地址上。

去找符号表

使用 ​otool -v XSQFishhookDemo -s __TEXT __stubs​ 查看,得到​0x6a78​ 地址开始的指令为:

0000000100006a78        nop
0000000100006a7c        ldr        x16, #0x15dc
0000000100006a80        br        x16

​nop​ 为空命令

​ldr​ 这一行的意思是,将当前 pc 寄存器中的值,加上 ​0x15dc​,再存到 x16 寄存器中

​br​ 这一行的意思是,跳转到 x16 寄存器的值指向的地址。

x16 寄存器中的值会是什么?根据 ldr 这一行算出,是 ​0000000100006a7c​ + ​0x15dc​ = 0x8058

去查看 0x8058 这个地址,它位于 DATA 段 __la_symbol_ptr 节。

__la_symbol_ptr 节是一系列指针,这些指针指向的,是某一个指令的地址。

这里 ​0x8058​ 这个地址中存的是 ​0x100006b08​

这个 0x100006b08 是 MachO 还未 or 刚刚载入内存中时,内存中 0x100008058 地址存的值。但是内存中的值是可以被改变的。事实上,只有第一次调用 printf 的时候,这一步会跳转到 0x100006b08,之后,这个内存会被写入新的地址,即动态库中 printf() 函数的地址。

这就是 Lazy bind。

Lazy bind

Fishhook 的 README 中介绍:

​__la_symbol_ptr​ is an array of pointers to imported functions that is generally filled by a routine called ​dyld_stub_binder​ during the first call to that symbol

也就是说,这个 __la_symbol_ptr 节中的内容,在载入内存后,是一个函数指针的数组。

我们就来看看这个 0x8058 中存储的 0x100006b08,是一个什么样的函数指针。

0x100006b08 位于 __TEXT 段 __stub_helper 节,它的汇编指令是:

0000000100006b08        ldr        w16, 0x100006b10
0000000100006b0c        b        0x100006a84

接下来就会跳转到 0x6a84 这个地址。

这个地址是 __stub_helper 节的开头。

执行几个指令后,就回去调用 dyld_stub_binder 函数

dyld_stub_binder 是 dyld 执行 bind 的函数。这个函数执行完后,__la_symbol_ptr 节中的内容,将不再是指向 __stub_helper 节的地址,而是 printf 函数真正的地址。

那么下次调用 printf 函数的时候,就可以直接通过 __la_symbol_ptr,找到真正的 printf 函数地址了。

四、Fishhook 做了什么

这时,我们想,如果把 __la_symbol_ptr 中存的地址改了,那是不是就能把 printf 函数指向其它一段指令了呢?

Fishhook 也是这样想的。

所以 Fishhook 的原理,也是篡改内存中 __la_symbol_ptr 中的内容。把原本应该存 printf() 地址的那块内存,内容改为 my_printf() 地址。

那么问题来了,我们能用函数指针的方式,知道 my_printf() 的地址,但是我们怎么知道,__la_symbol_ptr 中的第几个元素,表示的是 printf() 的地址呢?

如果我们能知道 __la_symbol_ptr 中的第 n 个元素,表示的函数的名字,那我们可以遍历 __la_symbol_ptr 数组,找到表示 printf() 的那个。

于是现在问题变成了,我们怎么知道 __la_symbol_ptr 中的每个元素表示的函数的名字呢?

这里就有一个规律:

我们再用 printf() 来走一遍这个流程,验证一番。

4.1 找到对应函数名

__la_symbol_ptr --> Indirect Symbols

MachO 文件有个规律,__la_symbol_ptr 节中的第 i 个数据,在 Indirect Symbols 中有对应的体现,且 index 变成了 reserved0 + i。

printf() 对应的数据,在 __la_symbol_ptr 节中是第 10 个元素,__la_symbol_ptr 中的 reserved1 为 12,那么我们找到 Indirect Symbols 中的第 22 个元素:

这里的值是 0x50

Indirect Symbols --> Symbol Table

我们拿着 0x50,去 Symbol Table 中找到 Symbol Table 中 index = 0x50 的数据。

得到 0x233

Symbol Table --> String Table

Symbol Table 中偏移量为 0x233 的字符串,就是 _printf

这样,主 binary 就知道,__la_symbol_ptr 节中的第 10 个元素,代表 printf,

4.2 篡改 __la_symbol_ptr 中的数据

这样,fishhook 只要遍历一次 __la_symbol_ptr,当发现第 i 个元素对应的名字,就是我们要 hook 的名字时,就可以将 __la_symbol_ptr 中的第 i 个元素的值篡改掉,完成 hook 的过程。

参考资料

Fishhook 源码:https://github.com/facebook/fishhook

iOS逆向(6)-从fishhook看runtime,hook系统C函数 https://www.jianshu.com/p/b6a72aa6c146

Dyld之二: 动态链接过程:https://blog.cnbluebox.com/blog/2017/10/12/dyld2/

App Startup Time: Past, Present, and Future

Optimizing App Startup Time

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • Hook 1. 概述:Hook,中文译为“挂钩”或“钩子”。在iOS逆向中是指改变程序运行流程的一种技术。通过ho...
    Nice_cheo阅读 1,278评论 0 0
  • 一、fishhook能做什么事情? c函数的地址是在编译的时候就已经确定了,位于程序的TEXT段,为只读区域: 如...
    fanglaoda阅读 1,482评论 0 7
  • 在上篇文章不知MachO怎敢说自己懂DYLD中已经详细介绍了MachO,并且由MachO引出了dyld,再由dyl...
    一缕清风扬万里阅读 8,636评论 37 46
  • fishhook fishhook是Facebook提供的用于hook系统c函数的库。它能动态重新绑定运行在iOS...
    lattr阅读 1,073评论 0 1
  • 总是试图一直一直的去验证一个人的真心❤️! 可是能经得起验证的❤️又能有几颗! 人无千日好,理应铭记于心!
    Mango_Jing阅读 204评论 0 0