第三十八節(jié)—AOP之Aspects庫(kù)(二)

本文為L(zhǎng)_Ares個(gè)人寫作欲逃,以任何形式轉(zhuǎn)載請(qǐng)表明原文出處。

上一節(jié),繼續(xù)探索录肯。本節(jié)將從AspectsContainer蚤假、AspectIdentifier來(lái)入手栏饮,探索Aspects庫(kù)到底是如何完成了hook。

圖1.0.0

一磷仰、先記錄幾個(gè)問題

  1. 首先袍嬉,已知Aspects庫(kù)可以完成在被hook的方法的前、后添加代碼灶平,也可以替換被hook的方法的原有代碼伺通。
  2. 其次,在上一節(jié)的例子中逢享,當(dāng)調(diào)用Aspects庫(kù)的公開API中的兩個(gè)方法時(shí)罐监,被hook的方法的_cmd也就是方法的SEL名稱發(fā)生了改變,出現(xiàn)了aspects__前綴瞒爬,變成了aspects__被hook的方法的SEL弓柱,這是怎么回事?(可見下圖1.1.0)侧但。
  3. 再次矢空,用來(lái)增加或者替換被hook的方法的block參數(shù)塊中的函數(shù),是如何完成了method_swizzling的禀横。
圖1.1.0

二屁药、初始化容器AspectsContainer

上一節(jié)探索到 :

AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);

這一行代碼的上面,探索完了Aspects庫(kù)對(duì)被hook的類和被hook的方法的合法性校驗(yàn)柏锄。

這一節(jié)從這一行代碼開始酿箭,先探索aspect_getContainerForObjectAspectsContainer复亏。

這里主要是對(duì)AspectsContainer做操作。

  • 獲取被hook的對(duì)象的AspectsContainer容器缭嫡。
  • 主要思想就是通過關(guān)聯(lián)對(duì)象方式缔御,將被hook的方法selector的名字重命名加上前綴變成aspects__selector,然后以它為鍵械巡,在self的關(guān)聯(lián)表中查詢對(duì)應(yīng)的容器刹淌。
  • AspectsContainer只有3個(gè)屬性,都是數(shù)組讥耗,分別存儲(chǔ)beforeAspects有勾、insteadAspectsafterAspects古程。

1. aspect_getContainerForObject

釋義 : 該方法是獲取AspectsContainer容器蔼卡。容器對(duì)象存儲(chǔ)的內(nèi)容是所有被hook的對(duì)象/類。方法的返回值是一個(gè)AspectsContainer對(duì)象挣磨。

方法的功能和注釋在下圖2.1.0中雇逞。

圖2.1.0

2. aspect_aliasForSelector

釋義 : 上圖2.1.0中,對(duì)關(guān)聯(lián)表的鍵aliasSelector的生成方式茁裙。

static SEL aspect_aliasForSelector(SEL selector) {
    NSCParameterAssert(selector);
    
    /**
     1. 方法中的宏 :
        static NSString *const AspectsMessagePrefix = @"aspects_";
     2. 返回的值 :
        關(guān)聯(lián)表的鍵的命名方式 = "aspects_" + "被hook的方法的SEL"
     */
    return NSSelectorFromString([AspectsMessagePrefix stringByAppendingFormat:@"_%@", NSStringFromSelector(selector)]);
}

三塘砸、初始化AspectIdentifier

這里就進(jìn)入到aspect_add()函數(shù)對(duì)被hook的類被hook的selector的信息保存。它們的原始信息都存儲(chǔ)在了AspectIdentifier對(duì)象中晤锥。

//直接調(diào)用的AspectIdentifier的初始化方法掉蔬,構(gòu)造一個(gè)對(duì)象
identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];

1. AspectIdentifier的屬性

SEL selector : 被hook的方法。
id block : hook后要執(zhí)行的操作女轿。
NSMethodSignature *blockSignature : block的簽名信息。
id object : 被hook的類壕翩。
AspectOptions options : block的執(zhí)行位置蛉迹。

2. AspectIdentifier初始化方法

圖3.2.0

很常規(guī)的構(gòu)造函數(shù),唯二的特點(diǎn)是對(duì)參數(shù)中block塊的簽名的獲取和校驗(yàn)放妈。

3. aspect_blockMethodSignature

該方法是獲取block的簽名信息北救。

參數(shù)

  1. block : 要獲取簽名信息的block。
  2. error : 獲取簽名信息出現(xiàn)的錯(cuò)誤信息芜抒。

實(shí)現(xiàn)

圖3.3.0

上圖3.3.0中扭倾,AspectBlockRef結(jié)構(gòu)體的結(jié)構(gòu) :

圖3.3.1

實(shí)現(xiàn)的思路非常的簡(jiǎn)單,在block的章節(jié)中介紹過挽绩,可以進(jìn)我的主頁(yè)看。
實(shí)現(xiàn)思路大體 :

  1. 通過位移block的指針驾中,從block的首地址唉堪,位移到block結(jié)構(gòu)體的desc3上面模聋。
  2. desc3中的signature元素存儲(chǔ)了block塊type encoding字符。
  3. 利用NSMethodSignature的方法唠亚,將type encoding字符轉(zhuǎn)成NSMethodSignature對(duì)象链方。這個(gè)對(duì)象也就是所謂的block塊的簽名

4. aspect_isCompatibleBlockSignature

該方法是對(duì)上面3.aspect_blockMethodSignature獲得的block塊的簽名的兼容性驗(yàn)證灶搜。

參數(shù)

  1. blockSignature : 要驗(yàn)證兼容性的block的簽名信息祟蚀。
  2. object : 被hook的類。
  3. selector : 被hook的方法割卖。
  4. error : 錯(cuò)誤信息前酿。

實(shí)現(xiàn)

圖3.4.0
  1. Aspects庫(kù)在公開API的注釋中說(shuō)不允許hook靜態(tài)方法。因?yàn)檫@里調(diào)用的是instanceMethodSignatureForSelector鹏溯。
  2. block塊type encoding格式在上一節(jié)說(shuō)過 : 返回值的type encoding + block的type encoding : @? + 參數(shù)的type encoding罢维。
    • 第一個(gè)位置 : 返回值的type encoding
    • 第二個(gè)位置 : block的type encoding丙挽,也就是@?肺孵。
  3. 判斷一下block的參數(shù)數(shù)量是否大于1。
    • 等于1說(shuō)明 : block的簽名中颜阐,參數(shù)只有block自己平窘。
    • 大于1說(shuō)明 : block不是一個(gè)空的block,也就是說(shuō)不是^(void){}這種無(wú)參數(shù)block凳怨。而是有除block自身外瑰艘,其他的參數(shù)。比如上一節(jié)的例子中的 : id<AspectInfo> aspectInfo(存在的話一定在block的參數(shù)的第2個(gè)位置上)猿棉、name磅叛、agesex萨赁。
  4. block的參數(shù)數(shù)量絕對(duì)不可以多于被hook的方法的參數(shù)數(shù)量弊琴。

四、AspectIdentifier對(duì)象加入AspectsContainer容器

能進(jìn)入到這里杖爽,表明被hook的類被hook的方法以及block參數(shù)中用來(lái)插入或者替換的函數(shù)都是符合Aspects庫(kù)的規(guī)定的敲董。

[aspectContainer addAspect:identifier withOptions:options];

參數(shù)

  1. aspect : 要加入容器的AspectsIdentifier對(duì)象。
  2. options : block的執(zhí)行位置信息慰安。

實(shí)現(xiàn)

- (void)addAspect:(AspectIdentifier *)aspect withOptions:(AspectOptions)options {
    
    // 1. 斷言區(qū)
    NSParameterAssert(aspect);
    
    // 2. 利用positionFilter過濾器腋寨,獲取AspectOptions參數(shù)中的,想要進(jìn)行hook的位置化焕,存入position中
    NSUInteger position = options&AspectPositionFilter;
    
    // 3. 利用position判斷被hook的類和方法屬于AspectsContainer容器中哪個(gè)數(shù)組萄窜,存入相應(yīng)的數(shù)組。
    switch (position) {
        case AspectPositionBefore:  self.beforeAspects  = [(self.beforeAspects ?:@[]) arrayByAddingObject:aspect]; break;
        case AspectPositionInstead: self.insteadAspects = [(self.insteadAspects?:@[]) arrayByAddingObject:aspect]; break;
        case AspectPositionAfter:   self.afterAspects   = [(self.afterAspects  ?:@[]) arrayByAddingObject:aspect]; break;
    }
    
}

五、類的準(zhǔn)備工作和方法的Hook

能進(jìn)入這里查刻,也是表明被hook的類被hook的selector以及用來(lái)插入或置換的block塊函數(shù)都是符合Aspects庫(kù)的規(guī)定的键兜。

從這里開始,就是真正的對(duì)被hook的類穗泵、被hook的selector普气、block塊內(nèi)的函數(shù)進(jìn)行操作了。

aspect_prepareClassAndHookSelector(self, selector, error);

1. 方法的整體邏輯

先來(lái)看這個(gè)函數(shù)的整體實(shí)現(xiàn)邏輯佃延,然后挑出其中的封裝邏輯再詳細(xì)探索现诀。

參數(shù)

self : 被hook的對(duì)象。
selector : 被hook的方法履肃。
error : 可能發(fā)生的錯(cuò)誤信息仔沿。

實(shí)現(xiàn)

圖5.1.0

2. aspect_hookClass()

該方法是對(duì)被hook的對(duì)象的類做hook后的處理。

圖5.2.0

我給這個(gè)方法分成了3個(gè)區(qū)域榆浓,方便理解于未,最主要的是看圖5.2.0中的注釋,下面依次說(shuō)明3個(gè)區(qū)域的功能陡鹃。

2.1 方法準(zhǔn)備區(qū)

代碼非常的簡(jiǎn)單烘浦,邏輯也簡(jiǎn)單 :

  1. 斷言判斷參數(shù)的合法性。
  2. statedClass :
    • 如果self是類對(duì)象 : statedClass就是類對(duì)象本身萍鲸。
    • 如果self是實(shí)例對(duì)象 : statedClass就是實(shí)例對(duì)象所屬的類闷叉。
  3. baseClass :
    • 如果self是類對(duì)象 : baseClass就是元類。
    • 如果self是實(shí)例對(duì)象 : baseClass就是實(shí)例對(duì)象所屬的類脊阴。
  4. className :
    • 如果self是類對(duì)象 : className就是元類名稱字符串握侧。
    • 如果self是實(shí)例對(duì)象 : className就是實(shí)例對(duì)象所屬的類的名稱字符串。

2.2 特殊情況區(qū)

  1. if ([className hasSuffix:AspectsSubclassSuffix]) :

    如果selfisa指向的類已經(jīng)有_Aspects_后綴嘿期。例如上節(jié)案例中的ViewController品擎,原本它的isa指向的類元類ViewController,如果它的isa指向的類變成了ViewController_Aspects_备徐,則表明它被hook過萄传。

    可以直接返回baseClass

  2. else if (class_isMetaClass(baseClass)) :

    進(jìn)入這里蜜猾,則代表在調(diào)用Aspects的公開API時(shí)秀菱,調(diào)用的是+方法,也就是類方法蹭睡,說(shuō)明要hook的是整個(gè)類對(duì)象衍菱,而不是類的某個(gè)實(shí)例對(duì)象。

    返回aspect_swizzleClassInPlace((Class)self)肩豁。

  3. else if (statedClass != baseClass) :

    進(jìn)入這里脊串,表示上一步?jīng)]有發(fā)生辫呻,也就是說(shuō),實(shí)例對(duì)象才會(huì)進(jìn)入到這個(gè)判斷洪规。

    一般情況下印屁,實(shí)例對(duì)象的類實(shí)例對(duì)象的isa指向的類是同一個(gè)類。
    如果發(fā)生不是同一個(gè)類的情況斩例,則證明,該對(duì)象有可能出現(xiàn)了特殊情況从橘,比如進(jìn)行著鍵值觀察(KVO)念赶。

    返回aspect_swizzleClassInPlace(baseClass)

在這個(gè)區(qū)域恰力,處理了公開API中叉谜,類方法的調(diào)用者。以及正在被KVO觀察的對(duì)象的isa指向的類踩萎。并且停局,它們調(diào)用的方法都是aspect_swizzleClassInPlace,只不過傳參不同香府。

2.2.1 aspect_swizzleClassInPlace

static Class aspect_swizzleClassInPlace(Class klass) {
    
    //斷言區(qū)
    NSCParameterAssert(klass);
    
    //獲取傳入類的名稱字符串
    NSString *className = NSStringFromClass(klass);

    //單例創(chuàng)建的一個(gè)集合董栽,存儲(chǔ)已經(jīng)發(fā)生swizzled的類
    //函數(shù)的參數(shù)是一個(gè)block,那么參數(shù)block的執(zhí)行企孩,就要看函數(shù)的實(shí)現(xiàn)中锭碳,block在哪里被執(zhí)行
    //所以block里面的代碼,需要看函數(shù)的實(shí)現(xiàn)勿璃,才能知道什么時(shí)候被執(zhí)行
    _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) {
        
        //如果當(dāng)前被hook的類擒抛,不在已發(fā)生swizzled的類集合中
        if (![swizzledClasses containsObject:className]) {
            
            //swizzled被hook的類的forwardInvocation方法
            aspect_swizzleForwardInvocation(klass);
            
            //添加這個(gè)類到已發(fā)生swizzled的類的集合
            [swizzledClasses addObject:className];
        }
    });
    return klass;
}

有兩點(diǎn) :

  1. _aspect_modifySwizzledClasses
  2. aspect_swizzleForwardInvocation

先看_aspect_modifySwizzledClasses

2.2.2 _aspect_modifySwizzledClasses

static void _aspect_modifySwizzledClasses(void (^block)(NSMutableSet *swizzledClasses)) {
    
    //定義靜態(tài)可變集合补疑,存儲(chǔ)已經(jīng)發(fā)生過混合的類
    static NSMutableSet *swizzledClasses;
    
    //下面很明顯是單例模式創(chuàng)建可變集合
    static dispatch_once_t pred;
    dispatch_once(&pred, ^{
        swizzledClasses = [NSMutableSet new];
    });
    
    //這里就是block被執(zhí)行的地方歧沪,用自旋鎖保證線程安全
    @synchronized(swizzledClasses) {
        block(swizzledClasses);
    }
}

明顯是一個(gè)以帶參數(shù)的block做參數(shù)的函數(shù)。

先初始化一個(gè)靜態(tài)的可變集合莲组,用來(lái)存儲(chǔ)已經(jīng)發(fā)生過swizzled的類诊胞,利用單例初始化,然后把集合當(dāng)參數(shù)胁编,傳入?yún)?shù)block厢钧,并且調(diào)用block

所以嬉橙,最后的重點(diǎn)還是block內(nèi)都對(duì)傳入的類做了什么早直。

再看aspect_swizzleForwardInvocation

2.2.3 aspect_swizzleForwardInvocation

static void aspect_swizzleForwardInvocation(Class klass) {
    
    //斷言區(qū)
    NSCParameterAssert(klass);
    
    // If there is no method, replace will act like class_addMethod.
    /**
     1. class_replaceMethod : 可以看一下蘋果的官方文檔,如果方法不存在市框,這個(gè)方法會(huì)像class_addMethod一樣去添加這個(gè)方法到klass里面霞扬。
     2. 替換klass(被hook的)的forwardInvocation方法的IMP實(shí)現(xiàn),并且把原有的IMP返回。
     3. 之所以要操作klass的forwardInvocation方法喻圃,是因?yàn)榉椒ǖ淖詈蟛檎也襟E是forwardInvocation:消息轉(zhuǎn)發(fā)
     */
    IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@");
    
    //如果被hook的對(duì)象的isa指向的中間類(也就是添加了_Aspects_后綴的中間類)萤彩,已經(jīng)實(shí)現(xiàn)了forwardInvocation方法
    if (originalImplementation) {
        //將原有就存在的forwardInvocation方法的IMP添加給__aspects_forwardInvocation:這個(gè)方法
        class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@");
    }
    
    //打印日志
    AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass));
}

也就是說(shuō),這個(gè)方法把傳入類的forwardInvocation:方法的IMP替換了斧拍。

對(duì)于參數(shù)klass :

  1. 如果klass是類本身雀扶,也就是通過+方法進(jìn)入到這里,那么整個(gè)類的forwardInvocation方法的IMP實(shí)現(xiàn)肆汹,都將被替換成__ASPECTS_ARE_BEING_CALLED__愚墓。(關(guān)于其他未被hook的方法如果沒有實(shí)現(xiàn),該怎么辦昂勉,后面在該函數(shù)的解釋里會(huì)說(shuō)明)

  2. 如果klass是另一種情況浪册,即被hook的對(duì)象isa指向的類不是一般情況下的自己的父類,而是出現(xiàn)類似KVO鍵值觀察的情況岗照。那么要替換的就是NSKVONotifying_父類名類的forwardInvocation方法的IMP實(shí)現(xiàn)村象。

問題 :

其實(shí)這里會(huì)出現(xiàn)一種情況 :

  1. 當(dāng)被hook的對(duì)象是一個(gè)KVO鍵值觀察對(duì)象或者就是普通的實(shí)例對(duì)象,如果被hook的對(duì)象所屬的類中有未被實(shí)現(xiàn)的方法攒至,并且厚者,沒實(shí)現(xiàn)的方法還不是被hook的方法

  2. 那么首先就會(huì)進(jìn)入動(dòng)態(tài)決議嗓袱,如果動(dòng)態(tài)決議依舊沒有實(shí)現(xiàn)籍救,則會(huì)進(jìn)入_objec_msgForward,進(jìn)行消息轉(zhuǎn)發(fā)渠抹。

  3. 如果我們不實(shí)現(xiàn)消息轉(zhuǎn)發(fā)流程中的快速轉(zhuǎn)發(fā)蝙昙,則最后會(huì)調(diào)用一次NSKVONotifying_父類名類的forwardInvocation方法。

  4. 但是這個(gè)方法的IMP被交換了梧却,那是不是沒實(shí)現(xiàn)的方法也會(huì)進(jìn)入__ASPECTS_ARE_BEING_CALLED__這個(gè)IMP實(shí)現(xiàn)呢奇颠?會(huì)怎么處理呢?在下面__ASPECTS_ARE_BEING_CALLED__方法解析的時(shí)候會(huì)有介紹放航。

2.3 默認(rèn)情況區(qū)

這里就是aspect_hookClass()函數(shù)的默認(rèn)實(shí)現(xiàn)烈拒,也就是針對(duì) :

  1. 從未經(jīng)過aspect_hook()處理的實(shí)例對(duì)象。
  2. 不是類對(duì)象广鳍。
  3. 不是KVO觀察對(duì)象荆几。

這三種情況除外的,普通的赊时,被hook的實(shí)例對(duì)象的吨铸,類的處理。

// Default case. Create dynamic subclass.
    //默認(rèn)的情況下祖秒,上面的幾個(gè)if條件都不滿足诞吱,那么就要自己動(dòng)手創(chuàng)建動(dòng)態(tài)的子類
    //給isa所指的類的名字前面加上Aspects庫(kù)的后綴
    const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String;
    
    //看清楚這里舟奠,這是objc_getClass而不是object_getClass
    //兩者有明確的區(qū)別,objc_getClass(subclassName)和[self class]有點(diǎn)像房维,返回的都是類本身
    //只不過objec_getClass的參數(shù)是const char*類型沼瘫,傳入類的名字就可以拿到一個(gè)類
    //而object_getClass則是獲取參數(shù)的isa指向的類
    Class subclass = objc_getClass(subclassName);

    //如果這個(gè)類還不存在
    if (subclass == nil) {
        //創(chuàng)建subclass的元類和類,并設(shè)置baseClass為subclass的父類
        subclass = objc_allocateClassPair(baseClass, subclassName, 0);
        //這個(gè)subclass不能被創(chuàng)建
        if (subclass == nil) {
            //報(bào)錯(cuò)
            NSString *errrorDesc = [NSString stringWithFormat:@"objc_allocateClassPair failed to allocate class %s.", subclassName];
            AspectError(AspectErrorFailedToAllocateClassPair, errrorDesc);
            //返回nil
            return nil;
        }
        //設(shè)置這個(gè)新類的forwardInvocation方法的IMP
        aspect_swizzleForwardInvocation(subclass);
        //設(shè)置新類的-(void)class方法的IMP咙俩,讓新類的-(void)class方法返回的是被hook的對(duì)象的類
        aspect_hookedGetClass(subclass, statedClass);
        //設(shè)置新類的元類的-(void)class方法(也就是新類的+(void)class方法)的IMP耿戚,也是返回被hook的對(duì)象的類
        aspect_hookedGetClass(object_getClass(subclass), statedClass);
        //把新類注冊(cè)到runtime中,這樣這個(gè)新類才算realized的暴浦。
        objc_registerClassPair(subclass);
    }

    //設(shè)置self(被hook的對(duì)象)的isa指向?yàn)閟ubclass類(新類)
    object_setClass(self, subclass);
    //返回這個(gè)新類
    return subclass;

還是重點(diǎn)看一下注釋情況溅话,然后從中可以找到一個(gè)沒有解析過,并且封裝起來(lái)的方法aspect_hookedGetClass歌焦。看實(shí)現(xiàn)砚哆。

static void aspect_hookedGetClass(Class class, Class statedClass) {
    //class是新類和新類的元類独撇,有Aspects庫(kù)的后綴。
    //statedClass是被hook的對(duì)象的類
    
    //斷言區(qū)
    NSCParameterAssert(class);
    NSCParameterAssert(statedClass);
    
    //拿到class類的-(void)class方法
    Method method = class_getInstanceMethod(class, @selector(class));
    
    //設(shè)置一個(gè)新的IMP躁锁,IMP的實(shí)現(xiàn)是返回一個(gè)被hook的對(duì)象的類
    IMP newIMP = imp_implementationWithBlock(^(id self) {
        return statedClass;
    });
    
    //替換掉class類的-(void)class方法的IMP為newIMP纷铣,返回statedClass類
    class_replaceMethod(class, @selector(class), newIMP, method_getTypeEncoding(method));
}

這個(gè)方法的目的 :

  1. 把新生成的中間類中間類的元類class方法,全都返回被hook的對(duì)象的類战转。
  2. 這樣做了以后搜立,哪怕在下面的object_setClass中,將被hook的對(duì)象isa指向變成了中間類槐秧,也不會(huì)影響被hook的對(duì)象調(diào)用-(void)class方法返回的是其原來(lái)的類啄踊。

3. 被hook的方法的處理

上面的2.aspect_hookClass()完成了對(duì)被hook的對(duì)象的類的處理,這里則開始對(duì)被hook的方法進(jìn)行處理刁标。

這里截取的是上圖5.1.0中颠通,Class klass = aspect_hookClass(self,error)之后的代碼,也就是對(duì)被hook的方法的處理膀懈。

圖5.3.0

看圖中畫了紅框的部分顿锰,從上到下,依此說(shuō)一下設(shè)計(jì)實(shí)現(xiàn)的思路启搂。

1. 首先硼控,Aspects庫(kù)的作者是利用class_getInstanceMethod來(lái)獲取klass中的SELselector的方法的。

Method targetMethod = class_getInstanceMethod(klass, selector);

使用class_getInstanceMethod是因?yàn)?code>klass已經(jīng)被處理過胳赌,被處理的klass無(wú)非就3種情況牢撼,在上面的aspect_hookClass中已經(jīng)介紹過,被hook的對(duì)象可能是 :

  1. 是普通的實(shí)例對(duì)象 :
    klass就是中間類匈织。
    中間類名稱是組成是 : 被hook的對(duì)象的類的名字 + _Aspects_后綴浪默。

  2. 是類對(duì)象 :
    klass的類不發(fā)生改變牡直,依然是被hook的類對(duì)象

  3. 是被KVO鍵值觀察的實(shí)例對(duì)象 :
    klassKVO的中間類纳决。
    KVO的中間類名稱組成是 : NSKVONotifying_ + 實(shí)例對(duì)象的類名

這3種klass都有著一個(gè)絕對(duì)的共同點(diǎn) : 全部繼承于被hook的對(duì)象的類碰逸。

所以,class_getInstanceMethod一定可以在klass的繼承鏈上阔加,找到selector的方法饵史。然后得到targetMethod

2. 獲得targetMethodIMP

沒什么可說(shuō)的胜榔,就是獲得被hook的方法原始IMP胳喷,直接用objcAPI,獲得MethodIMP夭织。

IMP targetMethodIMP = method_getImplementation(targetMethod);

3. 判斷被hook的方法原始IMP如果不是直接調(diào)用_objc_msgForward

if (!aspect_isMsgForwardIMP(targetMethodIMP))

aspect_isMsgForwardIMP的實(shí)現(xiàn) :

static BOOL aspect_isMsgForwardIMP(IMP impl) {
    
    //這個(gè)是arm64架構(gòu)吭露,也就是iOS系統(tǒng),手機(jī)真機(jī)的情況下尊惰,會(huì)直接調(diào)用_objc_msgForward
    return impl == _objc_msgForward
    
    //不要看這里了讲竿,這是非arm64架構(gòu)下的,消息轉(zhuǎn)發(fā)是調(diào)用的_objc_msgForward_stret
#if !defined(__arm64__)
    || impl == (IMP)_objc_msgForward_stret
#endif
    ;
}

一般正常的情況下弄屡,我們是不會(huì)直接給一個(gè)方法的實(shí)現(xiàn)寫成_objc_msgForward的题禀。所以大多數(shù)的情況,這里的BOOL值都是NO膀捷。一般都會(huì)進(jìn)入if判斷中的代碼迈嘹。

4. 給中間類添加一個(gè)方法

// Make a method alias for the existing method implementation, it not already copied.
        //獲取被hook的方法的typeEncoding
        const char *typeEncoding = method_getTypeEncoding(targetMethod);
        
        //獲取被hook的方法的別名SEL
        SEL aliasSelector = aspect_aliasForSelector(selector);
        
        //如果被hook的類不響應(yīng)這個(gè)aliasSelector
        if (![klass instancesRespondToSelector:aliasSelector]) {
            
            //把這個(gè)aliasSelector關(guān)聯(lián)上targetMethod的實(shí)現(xiàn),然后添加到klass上
            __unused BOOL addedAlias = class_addMethod(klass, aliasSelector, method_getImplementation(targetMethod), typeEncoding);
            
            NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass);
        }

也很好理解全庸,只是給klass添加了一個(gè)新的方法秀仲,方法的SEL名稱格式是 :

aspects_ + 被hook的方法的SEL。很熟悉糕篇,在給容器添加關(guān)聯(lián)對(duì)象的時(shí)候出現(xiàn)過啄育,這個(gè)SEL名稱是容器在關(guān)聯(lián)表中的鍵。

5. 替換被hook的方法的IMP

//我們利用forwardInvocation方法hook進(jìn)去
        //用_objc_msgForward(消息轉(zhuǎn)發(fā))替換被hook的方法的IMP
        class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);

aspect_getMsgForwardIMP的實(shí)現(xiàn)拌消,我只截取arm64架構(gòu)下的實(shí)現(xiàn) :

static IMP aspect_getMsgForwardIMP(NSObject *self, SEL selector) {
    
    //只看這里挑豌,這里是arm64架構(gòu)
    IMP msgForwardIMP = _objc_msgForward;

    //不用看了
#if !defined(__arm64__)

    ... ...
#endif
    return msgForwardIMP;
}

所以,現(xiàn)在墩崩,被hook的方法SEL還是原來(lái)的SEL氓英,但是IMP已經(jīng)換成了objc_msgForward了。

也就是說(shuō)鹦筹,現(xiàn)在如果再調(diào)用被hook的方法铝阐,就相當(dāng)于直接調(diào)用objc_msgForward,進(jìn)入消息轉(zhuǎn)發(fā)铐拐。

而上面我們剛說(shuō)過徘键,klass類的forwardInvocation全部都被替換成了__ASPECTS_ARE_BEING_CALLED__练对,也就是說(shuō) :

當(dāng)完成aspect_prepareClassAndHookSelector后,再調(diào)用被hook的方法吹害,相當(dāng)于直接調(diào)用到了__ASPECTS_ARE_BEING_CALLED__螟凭。

但是這里還有一個(gè)問題存在,如果我實(shí)現(xiàn)了forwardingTargetForSelector怎么辦?

這個(gè)問題放到最后一起解決它呀。

注釋

如何執(zhí)行被替換的block和如何執(zhí)行原有方法螺男,將會(huì)放入下一節(jié),AOP之Aspects庫(kù)(三)進(jìn)行探索纵穿。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末下隧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子谓媒,更是在濱河造成了極大的恐慌淆院,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件句惯,死亡現(xiàn)場(chǎng)離奇詭異迫筑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)宗弯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)搂妻,“玉大人蒙保,你說(shuō)我怎么就攤上這事∮鳎” “怎么了邓厕?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)扁瓢。 經(jīng)常有香客問我详恼,道長(zhǎng),這世上最難降的妖魔是什么引几? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任昧互,我火速辦了婚禮,結(jié)果婚禮上伟桅,老公的妹妹穿的比我還像新娘敞掘。我一直安慰自己,他們只是感情好楣铁,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布玖雁。 她就那樣靜靜地躺著,像睡著了一般盖腕。 火紅的嫁衣襯著肌膚如雪赫冬。 梳的紋絲不亂的頭發(fā)上浓镜,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音劲厌,去河邊找鬼膛薛。 笑死,一個(gè)胖子當(dāng)著我的面吹牛脊僚,可吹牛的內(nèi)容都是我干的相叁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼辽幌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼增淹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起乌企,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤虑润,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后加酵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拳喻,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年猪腕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冗澈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陋葡,死狀恐怖亚亲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腐缤,我是刑警寧澤捌归,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站岭粤,受9級(jí)特大地震影響惜索,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜剃浇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一巾兆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧偿渡,春花似錦臼寄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至适揉,卻和暖如春留攒,著一層夾襖步出監(jiān)牢的瞬間煤惩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工炼邀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留魄揉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓拭宁,卻偏偏與公主長(zhǎng)得像洛退,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子杰标,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 本文為L(zhǎng)_Ares個(gè)人寫作兵怯,以任何形式轉(zhuǎn)載請(qǐng)表明原文出處。 一腔剂、切面編程AOP AOP媒区,其實(shí)這種思想在之前的Met...
    L_Ares閱讀 845評(píng)論 0 1
  • 前言 最近在搞重構(gòu)相關(guān)的事情,遇到了不少這樣的場(chǎng)景: 進(jìn)入一個(gè)界面掸犬,在viewWillAppear:的時(shí)候做相應(yīng)判...
    上山砍柴閱讀 223評(píng)論 0 1
  • 作者:郭小弟 我們一定要給自己提出這樣的任務(wù):第一袜漩,學(xué)習(xí),第二是學(xué)習(xí)湾碎,第三還是學(xué)習(xí)宙攻。 —— 列寧 今天看了一個(gè)As...
    iOS亮子閱讀 827評(píng)論 0 6
  • 前言 眾所周知,Aspects框架運(yùn)用了AOP(面向切面編程)的思想介褥,這里解釋下AOP的思想:AOP是針對(duì)業(yè)務(wù)處理...
    花了個(gè)缺閱讀 16,871評(píng)論 2 45
  • 我們一定要給自己提出這樣的任務(wù):第一粘优,學(xué)習(xí),第二是學(xué)習(xí)呻顽,第三還是學(xué)習(xí)。 —— 列寧 前面幾章大概的說(shuō)了一下iOS的...
    郭小弟閱讀 15,329評(píng)論 1 34