所在的位置: swift >> swift优势 >> SwiftHook新思路虚函

SwiftHook新思路虚函

摘要:业界对Swift的Hook大多都需要依靠OC的消息转发特性来实现,本文从修改Swift的虚函数表的角度,介绍了一种新的Hook思路。并以此为主线,重点介绍Swift的详细结构以及应用。

1.前言

由于历史包袱的原因,目前主流的大型APP基本都是以Objective-C为主要开发语言。

但是敏锐的同学应该能发现,从Swift的ABI稳定以后,各个大厂开始陆续加大对Swift的投入。

虽然在短期内Swift还难以取代Objective-C,但是其与Objective-C并驾齐驱的趋势是越来越明显,从招聘的角度就即可管中窥豹。

在过去一年的招聘过程中我们总结发现,有相当数量的候选人只掌握Swift开发,对Objective-C开发并不熟悉,而且这部分候选人大多数比较年轻。

另外,以RealityKit等新框架为例,其只支持Swift不支持Objective-C。上述种种现象意味着随着时间的推移,如果项目不能很好的支持Swift开发,那么招聘成本以及应用创新等一系列问题将会凸显出来。

因此,58同城在年Q4的时候在集团内发起了跨部门协同项目,从各个层面打造Objective-C与Swift的混编生态环境——项目代号”混天“。

一旦混编生态构建完善,那么很多问题将迎刃而解。

2.原理简述

本文的技术方案仅针对通过虚函数表调用的函数进行Hook,不涉及直接地址调用和objc_msgSend的调用的情况。

另外需要注意的是,**SwiftCompiler**设置为Optimizeforspeed(Release默认)则TypeContext的VTable的函数地址会清空。

设置为Optimizeforsize则Swfit可能会转变为直接地址调用。

以上两种配置都会造成方案失效。因此本文重点在介绍技术细节而非方案推广。

如果Swift通过虚函数表跳表的方式来实现方法调用,那么可以借助修改虚函数表来实现方法替换。即将特定虚函数表的函数地址修改为要替换的函数地址。但是由于虚函数表不包含地址与符号的映射,我们不能像Objective-C那样根据函数的名字获取到对应的函数地址,因此修改Swift的虚函数是依靠函数索引来实现的。

简单理解就是将虚函数表理解为数组,假设有一个FuncTable[],我们修改函数地址只能通过索引值来实现,就像FuncTable[index]=replaceIMP。但是这也涉及到一个问题,在版本迭代过程中我们不能保证代码是一层不变的,因此这个版本的第index个函数可能是函数A,下个版本可能第index个函数就变成了函数B。显然这对函数的替换会产生重大影响。

为此,我们通过Swift的OverrideTable来解决索引变更的问题。在Swift的OverrideTable中,每个节点都记录了当前这个函数重写了哪个类的哪个函数,以及重写后函数的函数指针。

因此只要我们能获取到OverrideTable也就意味着能获取被重写的函数指针IMP0以及重写后的函数指针IMP1。只要在FuncTable[]中找到IMP0并替换成IMP1即可完成方法替换。

接下来将详细介绍Swift的**函数调用、TypeContext、Metadata、VTable、OverrideTable**等细节,以及他们彼此之间有何种关联。为了方便阅读和理解,本文所有代码及运行结果,都是基于arm64架构

3.Swift的函数调用

首先我们需要了解Swift的函数如何调用的。与Objective-C不同,Swift的函数调用存在三种方式,分别是:基于Objective-C的消息机制、基于虚函数表的访问、以及直接地址调用。

3.1Objective-C的消息机制

首先我们需要了解在什么情况下Swift的函数调用是借助Objective-C的消息机制。如果方法通过

objcdynamic修饰,那么在编译后将通过objc_msgSend的来调用函数。假设有如下代码

classMyTestClass:NSObject{

objcdynamicfunchelloWorld(){print("callhelloWorld()inMyTestClass")}}letmyTest=MyTestClass.init()myTest.helloWorld()

编译后其对应的汇编为

0xb+:bl0xb;typemetadataaccessorforSwiftDemo.MyTestClassat


转载请注明:http://www.aierlanlan.com/grrz/606.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了