iOS 常用的Hook方式

1,733 阅读2分钟

Aspects 是iOS老牌的AOP库

Aspects主要是利用了forwardInvocation进行转发,Aspects其实利用和kvo类似的原理,通过动态创建子类的方式,把对应的对象isa指针指向创建的子类,然后把子类的forwardInvocation的IMP替成__ASPECTS_ARE_BEING_CALLED__,假设要hook的方法名XX,在子类中添加一个Aspects_XX的方法,然后将Aspects_XX的IMP指向原来的XX方法的IMP,这样方便后面调用原始的方法,再把要hook的方法XX的IMP指向_objc_msgForward,这样就进入了消息转发流程,而forwardInvocation的IMP被替换成了__ASPECTS_ARE_BEING_CALLED__,这样就会进入__ASPECTS_ARE_BEING_CALLED__进行拦截处理

消息转发流程图

image.png

AOP框架Aspects实现原理

Stinger 是饿了么开源的AOP库

没有使用手动消息转发。解析原方法签名,使用libffi中的ffi_closure_alloc构造与原方法参数一致的"函数" -- _stingerIMP,以替换原方法函数指针;此外,生成了原方法和Block的调用的参数模板cif和blockCif。方法调用时,最终会调用到void _st_ffi_function(ffi_cif *cif, void *ret, void **args, void *userdata), 在该函数内,可获取到方法调用的所有参数、返回值位置,主要通过ffi_call根据cif调用原方法实现和切面block。

使用 libffi 实现 AOP

Method Swizzling

Method originalMethod = class_getInstanceMethod([self class], originalSelector);
    Method swizzledMethod = class_getInstanceMethod([self class], swizzledSelector);
    
    BOOL didAddMethod =
    class_addMethod([self class],
                    originalSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));
    
    if (didAddMethod) {
        class_replaceMethod([self class],
                            swizzledSelector,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }

fishhook

通过fishhook:除了可以hook c函数,fishhook+汇编还可以hook objc_msgSend实现全打点.

Cydia Substrate

Cydia Substrate是一个基于Hook的代码修改框架,可以在Android、iOS平台使用,并实现修改系统默认代码。

method swizzling/Aspects/Stinger对比

对比项swizzlingAspectsStinger
速度极快非常快
Api友好度非常差非常好非常好
类的hook支持支持支持
实例对象的hook不支持支持支持
调用原方法时改变selector修改修改不修改
方法可能因命名冲突不会不会
兼容其他hook方式(RAC, JSPactch…)兼容不兼容兼容
支持多线程增加hook自己加锁支持支持
hook可预见性,可追溯性非常差非常好
修改父类方法实现可能会不会不会

参考:Stinger到底能比Aspects快多少