load方法能否被hook钩挂?(上)

3 篇文章 1 订阅
1 篇文章 0 订阅

hook钩挂方式分为MethodSwizzle、fishhook、CydiaSubstrate等等多种钩挂方式。
以MethodSwizzle为例研究:
名称为load成员方法是否支持hook钩挂替换:
能不能hook钩挂替换?
首先看下Objc语系中成员方法所持有的实例发生交换的原理,下面是一段典型的实现 实例方法 交换的代码:

//字段名和实例(实现IMP)属于独立的2部分,彼此互不干涉
//完整的成员方法可以分为:成员方法的字段名+成员方法的实例(实现IMP)
Class class = [self class];//类对象
// 原方法名和替换方法名
SEL originalSelector = @selector(isEqualToString:);//字段名选择器selector
SEL swizzledSelector = @selector(swizzle_IsEqualToString:);//字段名选择器selector
// 原方法和替换方法
Method originalMethod = class_getInstanceMethod(class, originalSelector);//实现IMP
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);//实现IMP
// 如果当前类没有原方法的实现IMP,先调用class_addMethod来给原方法添加实现
BOOL didAddMethod = class_addMethod(class,
                                    originalSelector,
                            method_getImplementation(swizzledMethod),
                                    method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {// 添加方法实现IMP成功后,替换方法实现
    class_replaceMethod(class,
                        swizzledSelector,
                        method_getImplementation(originalMethod),
                        method_getTypeEncoding(originalMethod));
} else { // 有原方法,交换两个方法的实现
    method_exchangeImplementations(originalMethod, swizzledMethod);
}


实现交换的关键方法class_replaceMethod和 method_exchangeImplementations,两个方法的实现原理:
class_replaceMethod原理(底层指令):

IMP 
class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
{
    if (!cls) return nil;
    mutex_locker_t lock(runtimeLock);
    return addMethod(cls, name, imp, types ?: "", YES);
}


class_replaceMethod内部实际上调用了addMethods函数,入参replace = YES。
addMethods的实现原理(底层指令):

static IMP 
addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
{
    IMP result = nil;
    runtimeLock.assertLocked();
    checkIsKnownClass(cls);
    ASSERT(types);
    ASSERT(cls->isRealized());
    method_t *m;
    if ((m = getMethodNoSuper_nolock(cls, name))) {
        // already exists
        if (!replace) {
            result = m->imp;
        } else {
            result = _method_setImplementation(cls, m, imp);
        }
    } else {
        // fixme optimize
        method_list_t *newlist;
        newlist = (method_list_t *)calloc(sizeof(*newlist), 1);
        newlist->entsizeAndFlags = 
            (uint32_t)sizeof(method_t) | fixed_up_method_list;
        newlist->count = 1;
        newlist->first.name = name;
        newlist->first.types = strdupIfMutable(types);
        newlist->first.imp = imp;
        prepareMethodLists(cls, &newlist, 1, NO, NO);
        cls->data()->methods.attachLists(&newlist, 1);
        flushCaches(cls);
        result = nil;
    }
    return result;
}


addMethods函数内部调用_method_setImplementation来替换Method的imp以实现方法执行区替换:

static IMP 
_method_setImplementation(Class cls, method_t *m, IMP imp)
{
    runtimeLock.assertLocked();
    if (!m) return nil;
    if (!imp) return nil;
    IMP old = m->imp;
    m->imp = imp;
    // Cache updates are slow if cls is nil (i.e. unknown)
    // RR/AWZ updates are slow if cls is nil (i.e. unknown)
    // fixme build list of classes whose Methods are known externally?
    flushCaches(cls);
    adjustCustomFlagsForMethodChange(cls, m);
    return old;
}


method_exchangeImplementations原理机制(底层指令):

void method_exchangeImplementations(Method m1, Method m2)
{
 if (!m1 || !m2) return;
 mutex_locker_t lock(runtimeLock);
 //交换方法的IMP实际执行区
 IMP m1_imp = m1->imp;
 m1->imp = m2->imp;
 m2->imp = m1_imp;
 // RR/AWZ updates are slow because class is unknown
 // Cache updates are slow because class is unknown
 // fixme build list of classes whose Methods are known externally?
 flushCaches(nil);
 adjustCustomFlagsForMethodChange(nil, m1);
 adjustCustomFlagsForMethodChange(nil, m2);
}


方法交换的核心本质是对Method的imp执行区实施交换以实现方法的交换效果。method_exchangeImplementations函数的入参是Method(Method通过class_getClassMethod或class_getInstanceMethod获取)。class_getClassMethod最终本质还是调用class_getInstanceMethod:

Method class_getClassMethod(Class cls, SEL sel)
{
 if (!cls || !sel) return nil;
 return class_getInstanceMethod(cls->getMeta(), sel);
}


class_getInstanceMethod通过搜索方法列表找到对应方法元素(调用栈较长)。
通过分析:如果名称为load的声明方法已存放在类的方法列表中作为类方法列表元素,则能实现方法交换效果。
新建ClassA类带有load方法和替换方法swizzle_load方法:

@implementation ClassA
+ (void)load {
    NSLog(@"load");
}
+ (void)swizzle_load {
    NSLog(@"swizzle_load");
}
@end


在main函数中写一段测试代码,获取ClassA类下的方法列表:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        runTests(ClassA.class);
    }
    return 0;
}

void runTests (Class c ) {
    unsigned int count;
    //class_copyMethodList方法列表函数的入参涉及int基本数据类型的字段名count的地址&count,函数底层指令中必然存在*(&count)指针访问(已实现对字段名count的修改赋值)
    //方法列表容器methods
    Method *methods = class_copyMethodList(c, &count);
    //count栈区内数据值即为方法列表中所囊括的方法元素的数目
    for (int i = 0; i < count; i++) {
        Method method = methods[i];
        SEL selector = method_getName(method);
        NSString *name = NSStringFromSelector(selector);
        NSLog(@"方法名:%@",name);
    }
}


ClassA类涉及的方法列表当前是空的(count==0)。

什么时机hook:我们经常在load方法中hook其他方法,那hook load方法在什么时机呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HaiJunYa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值