前言
最近在写debug工具,需要hook一些方法,但是不希望debug工具依赖其他业务库,以前实现这种方式,是通过category,比如需要hook NSURLSessionConfiguration
的defaultSessionConfiguration
方法,用下面的方式实现,没毛病,一直都是这样搞的
@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 网络库里SKIOTCPClient
的sendData
的相关方法,不通过category的方式
#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");
}
}
调用
+ (void)load{
Class SKIOTCPClientClass = NSClassFromString(@"SKIOTCPClient");
SEL debugSendDataContent = @selector(debugSendDataContent:writeBlock:);
SEL sendDataContent = @selector(sendDataContent:writeBlock:);
[SKDTTCPHook hookClass:SKIOTCPClientClass originalSelector:sendDataContent toClass:[SKDTTCPHook class] swizzleSelector:debugSendDataContent];
}
- (void)debugSendDataContent:(NSObject *)content writeBlock:(void(^)(NSInteger errorCode))writeBlock{
[self debugSendDataContent:content writeBlock:writeBlock];
// ...
}
注意
用于exchange的方法里的self,指向的是被hook的类,所以无法通过self无法调用除自身之外的其他外部方法,除非你知道你的self都有哪些可以执行的方法,建议使用类方法,objc_msgSend或者NSNotification等方式进行转发