iOS Hook block without libffi

基础知识

关于block原理的文章已经有很多,这里就没必要再复述一遍。只列出一些和主题密切相关的知识点。

block是什么?

block就是block
block是一个NSBlock对象。
block是一个指针,指向下面这个结构体。

struct Block_layout {
        void *isa;
        volatile int32_t flags; // contains ref count
        int32_t reserved; 
        void (*invoke)(void *, ...);
        struct Block_descriptor_1 *descriptor;
        // imported variables
    };

调用block的时候发生了什么

block内的代码会被编译成一个C函数。block的invoke指针,指向这个函数,调用block等于调用这个函数。


怎么hook一个block

hook一个block需要两步。

1 让block调用的时候不去调用原本的函数,而是调用自己的函数。
2 自己的函数再去调用block原本的函数。

第一步很简单,只需要把block的invoke指针指向自己的函数就可以了。
第二步是这篇文章的重点,接下来逐一讲述。

block和普通对象一样,支持消息转发

如果我们把block的invoke指针指向_objc_msgForward。调用block,会触发消息转发。我们会在forwardInvocation:中拿到一个参数已经被放好了的NSInvocation对象。走到这里,形式已经一片大好。

当NSInvocation对象的target为block时,调用其invoke方法,会发生什么?

使用hopper观察[NSInvocation invoke]的实现。得到如下结果:

未命名.png

大概解释一下这个方法的流程,invoke最终通过invoking完成调用。在调用前,其第一个参数 $r15 会被赋值为objc_msgSend或objc_msgSend_stret。接着,遍历target的superClass,查看是否继承自NSBlock。如果继承自NSBlock,就把target的首地址+0x10赋值给 $r15。而block的首地址+16就是block的invoke指针。由此不难看出,NSInvocation本身是便完美支持target作为block使用。

有了NSInvocation的支持,就可以安心的通过invoke去调用block。

还需要一个假block

最后,整体的流程大概是这样:

在准备hook一个block时,我们需要先copy这个block,并让block持有copy结果。这里copy的主要的目的是复制外部变量。

设置block的invoke指针为_objc_msgForward,调用block触发消息转发。

在forwardInvocation:中执行完自己的逻辑后,将invocation的target设置为刚刚copy的block,执行invoke。完成Hook。

PS: 在FishBind中,hookBlock被作为其中的一个功能点。有兴趣的朋友可以去看相关的具体实现。

你可能感兴趣的:(iOS Hook block without libffi)