阅读 55

hook工具之Aspects源码分析

Aspects源码解析打算分两篇文章讲解
一个是解析,第二个就是作者为什么这么做。
本篇为源码流程分析

源码连接Aspects

Aspects源码解析(一)
Aspects源码解析之十万个为什么(二)

流程:

在替换Afoo方法

  1. 生成A的子类B,类名为 A__Aspects_

  2. 替换B的forwardInvocation:的实现为__ASPECTS_ARE_BEING_CALLED__

  3. 替换B的 -(Class) class+ (Class) class的实现方法,替换返回为A class

  4. 为B添加方法名为:foo_aspects_,实现方法为foo的方法实现、并把源方法foo的实现替换成_objc_msgForward

  5. 当hook后,调用foo方法时候,跳转到

    __ASPECTS_ARE_BEING_CALLED__
    复制代码

    去处理

    1. Foo -> _objc_msgForward -> __ASPECTS_ARE_BEING_CALLED__

主要成员:

AspectsContainer(存储所有切片 object/class)

通过关联对象绑定到self ,key为aspects__foo

NSArray<AspectIdentifier *> *beforeAspects

NSArray<AspectIdentifier *> *insteadAspects

NSArray<AspectIdentifier *> *afterAspects
复制代码

AspectIdentifier

存储block签名,判断是参数个数是否超过原方法

根据option添加到AspectContainner中

执行 [blockInvocation invokeWithTarget:``**self**``.block];

AspectTracker

(标记已经hook过的类) 存在单例swizzledClassesDictkeyclass

AspectInfo(回调组装存储信息)

包含原始方法的签名和实例,回调使用存储参数

流程图&引用关系

image.png

细节实现(值得学习的地方):

  1. 通过不同block的flags来找到block的签名

    signature
    复制代码
    1. 了解block结构体组成,细节操作
  2. disallowedSelectorList为什么是单例

    1. 黑名单列表针对的是全局,如果是单例不必每次都执行都创建,性能更高
  3. 枚举位运算

    1. 用于过滤
  4. 为什么替换

    forwardInvocation
    复制代码
    1. 消息发送
    + (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    - (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    - (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
    复制代码
    1. 直接调用和更适合调用
  5. hooked检查:

    1. 父类/子类是否已经被替换,是否已经替换过(循环调用)
    2. block参数长度
  6. 白名单为什么使用单例。为什么设置白名单

    1. 避免错误和不合逻辑
    2. 因为整体重复调用,重复生成,浪费性能
  7. 使用类似kvo的结构的好处 class方法的hook

    1. 避免干扰原有class实现
  8. 为什么返回是id

    1. 屏蔽细节。给你想的东西

备注:

官方签名Type Encodings文档

文章分类
iOS
文章标签