iOS Hook block without libffi

基礎(chǔ)知識

關(guān)于block原理的文章已經(jīng)有很多,這里就沒必要再復(fù)述一遍。只列出一些和主題密切相關(guān)的知識點(diǎn)。

block是什么?

block就是block
block是一個NSBlock對象。
block是一個指針,指向下面這個結(jié)構(gòu)體。

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
    };

調(diào)用block的時候發(fā)生了什么

block內(nèi)的代碼會被編譯成一個C函數(shù)。block的invoke指針,指向這個函數(shù),調(diào)用block等于調(diào)用這個函數(shù)。


怎么hook一個block

hook一個block需要兩步。

1 讓block調(diào)用的時候不去調(diào)用原本的函數(shù),而是調(diào)用自己的函數(shù)。
2 自己的函數(shù)再去調(diào)用block原本的函數(shù)。

第一步很簡單,只需要把block的invoke指針指向自己的函數(shù)就可以了。
第二步是這篇文章的重點(diǎn),接下來逐一講述。

block和普通對象一樣,支持消息轉(zhuǎn)發(fā)

如果我們把block的invoke指針指向_objc_msgForward。調(diào)用block,會觸發(fā)消息轉(zhuǎn)發(fā)。我們會在forwardInvocation:中拿到一個參數(shù)已經(jīng)被放好了的NSInvocation對象。走到這里,形式已經(jīng)一片大好。

當(dāng)NSInvocation對象的target為block時,調(diào)用其invoke方法,會發(fā)生什么?

使用hopper觀察[NSInvocation invoke]的實(shí)現(xiàn)。得到如下結(jié)果:

未命名.png

大概解釋一下這個方法的流程,invoke最終通過invoking完成調(diào)用。在調(diào)用前,其第一個參數(shù) $r15 會被賦值為objc_msgSend或objc_msgSend_stret。接著,遍歷target的superClass,查看是否繼承自NSBlock。如果繼承自NSBlock,就把target的首地址+0x10賦值給 $r15。而block的首地址+16就是block的invoke指針。由此不難看出,NSInvocation本身是便完美支持target作為block使用。

有了NSInvocation的支持,就可以安心的通過invoke去調(diào)用block。

還需要一個假block

最后,整體的流程大概是這樣:

在準(zhǔn)備hook一個block時,我們需要先copy這個block,并讓block持有copy結(jié)果。這里copy的主要的目的是復(fù)制外部變量。

設(shè)置block的invoke指針為_objc_msgForward,調(diào)用block觸發(fā)消息轉(zhuǎn)發(fā)。

在forwardInvocation:中執(zhí)行完自己的邏輯后,將invocation的target設(shè)置為剛剛copy的block,執(zhí)行invoke。完成Hook。

PS: 在FishBind中,hookBlock被作為其中的一個功能點(diǎn)。有興趣的朋友可以去看相關(guān)的具體實(shí)現(xiàn)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容