ioshook详解

发布时间:2023-05-18

一、Ioshook父类方法

1.方法简介

Ioshook是一个基于Cycript实现的库,它可以hook Objective-C方法并在运行时对其进行修改、添加与删除。下面我们会从多个方面进行详细阐述。

2.方法具体实现

/**
 * Hook一个对象的类方法
 *
 * @param cls 类对象
 * @param selector 方法选择器
 * @param callback 回调函数
 *
 * @return 原始IMP地址
 */
+ (void *)hookClassMethod:(Class)cls selector:(SEL)selector callback:(void *)callback;
/**
 * Hook一个对象的实例方法
 *
 * @param cls 类对象
 * @param selector 方法选择器
 * @param callback 回调函数
 *
 * @return 原始IMP地址
 */
+ (void *)hookInstanceMethod:(Class)cls selector:(SEL)selector callback:(void *)callback;

3.方法使用示例

以Hook UIAlertViewshow 方法为例,我们可以实现自定义 UIAlertView 显示时的行为。

Class cls = objc_getClass("UIAlertView");
SEL sel = @selector(show);
[Ioshook hookInstanceMethod:cls selector:sel callback:&showCallback];
// 实现 showCallback 方法
void showCallback(id self, SEL sel) {
    NSLog(@"custom show action");
}

二、子类重写

1.重写方法实现

除了通过 hook 来修改方法的行为,我们还可以通过 subclass 的方法来重写方法的实现,以此实现目标。

@interface TargetClass : NSObject
- (void)originalMethod;
@end
@implementation TargetClass
- (void)originalMethod {
    NSLog(@"original method");
}
@end
@interface SubClass : TargetClass@end
@implementation SubClass
- (void)originalMethod {
    NSLog(@"new method");
}
@end

2.重写方法使用示例

以 Hook UIViewControllerviewDidAppear: 方法为例,我们可以在所有继承于 UIViewController 的子类中增加上报事件:

@implementation UIViewController (CustomEvent)
+ (void)load {
    // Only add once
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // subclass
        [self mk_customHookMethod:@selector(viewDidAppear:) tarClass:self.newClass usingBlock:^(MKAspectInfo *info) {
            UIViewController *controller = (UIViewController *)info.instance;
            NSLog(@"custom event report: %@", NSStringFromClass([controller class]));
        }];
    });
}
@end
@interface UIViewController (CustomEvent) @end
@implementation UIViewController (CustomEvent)
// Override this method to return subclass
+ (Class)newClass {
    // Allocate a new class, subclass of the original
    NSString *className = NSStringFromClass(self);
    Class newClass = objc_allocateClassPair(self, [className stringByAppendingString:@"_CustomEvent"].UTF8String, 0);
    // Update hook for subclasses
    [newClass aspect_hookSelector:@selector(viewDidAppear:) withOptions:AspectPositionAfter usingBlock:^(id info) {
        UIViewController *controller = (UIViewController *)info.instance;
        NSLog(@"custom event report: %@", NSStringFromClass([controller class]));
    } error:nil];
    objc_registerClassPair(newClass);
    return newClass;
}
@end

三、使用场景

1.日志打印

在开发和调试过程中,日志打印是件非常重要的事情。我们可以通过 hook 的形式实现全局的日志打印。下面以 YYLog 框架为例:

void _objc_msgForward(void) {
    // Hook it
    orig_objc_msgForward();
    // Do something with it
    NSLog(@"classname : %@  SEL: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
}

2.检查错误

在发布的应用中,一个小小的错误有可能导致崩溃。通过 hook 可以实现全局的错误检测和上报。

// Define a new method 
- (void)xxx_NSLog:(NSString *)format, ... {
    va_list argp;
    va_start(argp, format);
    NSString *str = [[NSString alloc] initWithFormat:format arguments:argp];
    va_end(argp);
    // Do something with the log
    NSLog(@"%@", str);
    // Call the old method
    [self xxx_NSLog:format,...];
}
// Hook NSUncaughtExceptionHandler
if (getenv("DEBUG_MODE")) { // debug mode
    NSSetUncaughtExceptionHandler(&CBLDebugHandleException);
} else { // release mode
    NSSetUncaughtExceptionHandler(&CBLReleaseHandleException);
}
// Implement CBLDebugHandleException and CBLReleaseHandleException functions to handle errors

3.统计调用次数

很多应用都需要进行功能点调用次数的统计。通过 hook 可以实现全局的统计,提高开发效率。

@implementation UIView (Count)
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self aspect_hookSelector:@selector(didMoveToSuperview) withOptions:AspectPositionAfter usingBlock:^() {
            // Statistics code here
        } error:nil];
    });
}
@end

4.网络监测

通过 hook 可以实现网络请求的监测,从而实现全局的网络监测。

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class cls = objc_getClass("__NSCFURLSessionTask");
        [Ioshook hookInstanceMethod:cls selector:@selector(cancel) callback:(void *)&HookedNSURLSessionTaskCancel];
    });
}
void HookedNSURLSessionTaskCancel(NSURLSessionTask *self, SEL _cmd) {
    // Statistics network interrupt times
    [self HookedNSURLSessionTaskCancel];
}

四、小结

使用 hook 的方式,可以实现很多应用所需要的功能,例如日志打印、错误检测、调用次数统计、网络监测等等。对了解 iOS 运行机制和 Objective-C 方法有较深入的理解有一定帮助。以上是一些功能点的例子,我们也可以根据实际需要进行策略和实现。希望对大家有所帮助。