非category的方式hook任意方法

195 阅读1分钟

最近在写debug工具,需要hook一些方法,但是不希望debug工具依赖其他业务库,以前实现这种方式,是通过category,比如需要hook NSURLSessionConfigurationdefaultSessionConfiguration方法,用下面的方式实现,没毛病,一直都是这样搞的

@implementation NSURLSessionConfiguration (SKDTDebug)+ (void)load {    Method method1 = class_getClassMethod([NSURLSessionConfiguration class], @selector(defaultSessionConfiguration));    Method method2 = class_getClassMethod([NSURLSessionConfiguration class], @selector(LL_defaultSessionConfiguration));    method_exchangeImplementations(method1, method2);}+ (NSURLSessionConfiguration *)LL_defaultSessionConfiguration {    NSURLSessionConfiguration *config = [NSURLSessionConfiguration LL_defaultSessionConfiguration];    // ...
    return config;}@end

但是,因为是debug工具库,不能依赖其他工具或者业务库,比如我需要hook 网络库里SKIOTCPClientsendData的相关方法,不能通过category的方式,那就可以通过这种方式

#import "SKDTTCPHook.h"#import <objc/runtime.h>#import <objc/message.h>

@implementation SKDTTCPHook+ (void)load{    Class SKIOTCPClientClass = NSClassFromString(@"SKIOTCPClient");    SEL debugSendDataContent = @selector(debugSendDataContent:writeBlock:);    SEL sendDataContent = @selector(sendDataContent:writeBlock:);    Method debugSendDataContentMethod_SKDTTCPHook = class_getInstanceMethod([SKDTTCPHook class], debugSendDataContent);    IMP debugSendDataContentImp_SKDTTCPHook = method_getImplementation(debugSendDataContentMethod_SKDTTCPHook);    char *debugSendDataContentTypes_SKDTTCPHook = (char *)method_getTypeEncoding(debugSendDataContentMethod_SKDTTCPHook);
    // 给SKIOTCPClient添加SKDTTCPHook的debugSendDataContent方法    BOOL success = class_addMethod(SKIOTCPClientClass, @selector(debugSendDataContent:writeBlock:), debugSendDataContentImp_SKDTTCPHook, debugSendDataContentTypes_SKDTTCPHook);    if (success) {        // 获取SKIOTCPClient里的debugSendDataContent方法        Method debugSendDataContentMethod_SKIOTCPClient = class_getInstanceMethod(SKIOTCPClientClass, debugSendDataContent);        Method originalMethod = class_getInstanceMethod(SKIOTCPClientClass, sendDataContent);        // 交换SKIOTCPClient里的两个方法        method_exchangeImplementations(originalMethod, debugSendDataContentMethod_SKIOTCPClient);        NSLog(@"exchange success");    }else{        NSLog(@"exchange failure, method already exist");    }}- (void)debugSendDataContent:(NSObject *)content writeBlock:(void(^)(NSInteger errorCode))writeBlock{    [self debugSendDataContent:content writeBlock:writeBlock];    // ...}@end

调用class_addMethod给需要hook的类手动添加你需要exchange的方法,就相当于实现了category,需要关注的是,添加了你自己的方法后,需要再通过class_getInstanceMethod将你添加的方法从被hook的类里拿出来,再执行method_exchangeImplementations

抽取出来

+ (void)hookClass:(Class)originalClass originalSelector:(SEL)originalSelector toClass:(Class)swizzleClass swizzleSelector:(SEL)swizzleSelector{    Method swizzleMethod = class_getInstanceMethod(swizzleClass, swizzleSelector);    IMP swizzleImp = method_getImplementation(swizzleMethod);    char *swizzleTypes = (char *)method_getTypeEncoding(swizzleMethod);    // 将要exchange的方法添加到原始类中    BOOL success = class_addMethod(originalClass, swizzleSelector, swizzleImp, swizzleTypes);    if (success) {        // 从原始类中重新读取出添加的方法        Method swizzleMethod_original = class_getInstanceMethod(originalClass, swizzleSelector);        Method originalMethod = class_getInstanceMethod(originalClass, originalSelector);        // 交换原始类中两个方法实现        method_exchangeImplementations(originalMethod, swizzleMethod_original);        NSLog(@"exchange success");    }else{        NSLog(@"exchange failure, method already exist");    }}