美文网首页iOS程序猿iOS学习笔记iOS Developer
静态拦截iOS对象方法调用的简易实现

静态拦截iOS对象方法调用的简易实现

作者: 欧阳大哥2013 | 来源:发表于2020-01-06 08:34 被阅读0次

最近出现了几篇关于二进制重排启动优化的文章。所有方案中都需要事先统计所有的函数调用情况,并根据函数调用的频次来进行代码的重排。

这些函数调用中,OC对象的方法调用最多。统计OC对象的方法调用可以在运行时通过第三方库比如fishhook来Hook所有objc_msgSend调用来实现,也可以在编译后链接前通过静态插桩的方式来实现Hook拦截。

对于静态插桩的实现一般有如下两个方案:

  1. 借助于LLVM语法树分析来实现代码插桩。

  2. 将源码编译为静态库,并通过修改静态库中.o目标文件的代码段来实现代码插桩。

上述的两个方法实现起来比较复杂,要么就要了解LLVM,要么就要熟悉目标文件中间字节码以及符号表相关的底层知识。

本文所介绍的是第三种静态Hook方案,也是依赖于静态库这个前提来实现对objc_msgSend函数进行Hook,从而实现在编译前链接后的OC对象方法调用插桩。

这个方案实现的原理很简单。因为静态库其实只是一个编译阶段的中间产物,静态库目标文件中的所有引用的外部符号会保存到一张字符串表中,所有函数调用都只是记录了函数名称在字符串表的索引位置,在链接时会才会根据符号名称来替换为真实的函数调用指令。因此我们可以将所有静态库字符串表中的objc_msgSend统一替换为另外一个长度相同的字符串:hook_msgSend(名字任意只要长度一致并唯一)即可。然后在主工程源代码中实现一个名字为hook_msgSend的函数即可。这个函数必须要和objc_msgSend的函数签名保持一致,这样在链接时所有静态库中的objc_msgSend调用都会统一转化为hook_msgSend调用。

下面的是具体的实现步骤:

1. 在主工程中编写hook_msgSend的实现。
hook_msgSend的函数签名要和objc_msgSend保持一致,并且要在主工程代码中实现,而且必须要用汇编代码实现。具体实现的逻辑和目前很多文章中介绍的对objc_msgSend函数的Hook实现保持一致即可。

很多对objc_msgSend进行Hook的实现其实是不完整的,因此如果想完全掌握函数调用ABI规则的话请参考:《深入iOS系统底层之函数调用

2. 将所有其他代码都统一编译为一个或多个静态库。
将源代码按功能编译为一个或多个静态库,并且主工程链接这些静态库。这种程序代码的组织方式已经很成熟了,最常用的方法是我们可以借助代码依赖集成工具cocoapods来实现,这里就不再赘述了。

3. 在主工程的Build Phases 中添加Run Script脚本。
我们需要保证这个脚本一定要运行在链接所有静态库之前执行。因此可以放到Compile Sources 下面。

4. 实现静态库符号替换的Run Script脚本。
这是最为关键的一步,我们可以实现一个符号替换的程序,然后在Run Script脚本中 执行这个符号替换程序。符号替换程序的输入参数就是主工程中所链接的所有静态库的路径。至于这个符号替换程序如何编写则没有限制,你可以用ruby编写也可以用python也可以用C语言编写。 无论用何种方法实现,你都需要首先了解一下静态库.a的文件结构。你可以从:《深入iOS系统底层之静态库》一文中掌握到一个静态库文件的组成结构。了解了静态库文件的组成结构后,你的符号替换程序要做的事情就可以按如下步骤实现:
一)、 打开静态库.a文件。
二)、找.a文件中定义的字符串表部分。字符串表的描述如下:

struct stringtab
{
    int size;     //字符串表的尺寸
    char strings[0];   //字符串表的内容,每个字符串以\0分隔。
};

字符串表中的strings的内容就是一个个以\0分隔的字符串,这些字符串的内容其实就是这个目标文件所引用的所有外部和内部的符号名称。

三)、将字符串表中的objc_msgSend字符串替换为hook_msgSend字符串。
四)、保存并关闭静态库.a文件。

5. 编译、链接并运行你的主工程程序。


采用本文中所介绍的静态Hook方法的好处是我们不必Hook所有的OC方法调用,而是可以有选择的进行特定对象和类的方法调用拦截。因此这种技术不仅可以应用代码重排统计上,还可以应用在其他的监控和统计应用中。因为这种机制可以避免程序在运行时进行objc_msgSend替换而产生的函数调用风暴问题。另外的一个点就是这个方法不局限于对objc_msgSend进行Hook,还可以对任意的其他函数进行Hook处理。因此这种技术也可以应用在其他方面。

相关文章

  • 静态拦截iOS对象方法调用的简易实现

    最近出现了几篇关于二进制重排启动优化的文章。所有方案中都需要事先统计所有的函数调用情况,并根据函数调用的频次来进行...

  • 浅谈java代理

    一、静态代理 代理对象和目标对象需要实现相同的接口,可以做到通过调用代理对象的方法来实现调用目标对象方法的目的,实...

  • 再论静态方法和类方法

    实例对象可以调用实例方法、类方法、静态方法 类对象只能调用类方法、静态方法

  • Swift 中的方法声明

    实例方法 类方法 静态方法 实例方法由实例对象进行调用;类方法和静态方法由类对象进行调用

  • ios bridge原理

    // 消息请求拦截 先来说说JS调用iOS:在HTML加载完毕时注入 JS对象jsObj,当JS调用相应方法时候由...

  • python 实例方法、类方法、静态方法的区别

    实现方式上区别 类,静态方法是装饰器实现 调用上区别 实例方法只能由实例对象调用 定义上区别 类方法实现类逻辑 静...

  • 建造者模式

    实现 总结 使用静态内部类和静态方法可以直接通过类方法调用 封装对象构建细节 适用于构建对象时有特殊逻辑操作

  • Java学习 Day9

    1.static(静态):修饰成员变量,成员方法,静态块。 静态变量和静态方法的调用:类名和对象名调用。调用语法:...

  • JS实例属性/方法、静态属性/方法、私有属性/方法、原型属性/方

    实例属性/方法 & 静态属性/方法 实例属性/实例方法通过对象调用 静态属性/静态方法用类名直接调用 私有属性/方...

  • Java基础知识的小总结(2)

    静态方法 静态方法其实就是类方法,与类有关的,普通的方法在类被实例化后,被对象来调用,静态方法无法调用非静态方法,...

网友评论

    本文标题:静态拦截iOS对象方法调用的简易实现

    本文链接:https://www.haomeiwen.com/subject/ftqtactx.html