条条大路通罗马
Method Swizzling:
突发奇想,我要Hook一个init:
@implementation NSArray (Swizzling)
+ (void)load{
Method orig_init = class_getInstanceMethod([self class], @selector(init));
Method amer_init = class_getInstanceMethod([self class], @selector(amer_init));
method_exchangeImplementations(orig_init, amer_init);
}
- (instancetype)amer_init{
NSLog(@"-[NSArray init] is hooked!");
// invoke 'origin_init'
return [self amer_init];
}
@end
Unexpected results——我意料之外的事
完整的打印结果
2016-11-04 14:10:31.166 HookTest[3436:335400] -[NSArray init] is hooked!
2016-11-04 14:10:31.168 HookTest[3436:335367] -[NSArray init] is hooked!
2016-11-04 14:10:31.167 HookTest[3436:335399] -[NSArray init] is hooked!
2016-11-04 14:10:31.168 HookTest[3436:335367] -[CADisplay amer_init]: unrecognized selector sent to instance 0x600000030bc0
2016-11-04 14:10:31.170 HookTest[3436:335399] -[NSUserDefaults amer_init]: unrecognized selector sent to instance 0x60000004a6e0
2016-11-04 14:10:31.170 HookTest[3436:335367] -[NSArray init] is hooked!
2016-11-04 14:10:31.170 HookTest[3436:335400] -[NSArray init] is hooked!
2016-11-04 14:10:31.170 HookTest[3436:335399] -[NSArray init] is hooked!
2016-11-04 14:10:31.170 HookTest[3436:335367] -[NSArray init] is hooked!
2016-11-04 14:10:31.170 HookTest[3436:335400] -[__NSBundleTables amer_init]: unrecognized selector sent to instance 0x600000261500
2016-11-04 14:10:31.170 HookTest[3436:335399] -[NSArray init] is hooked!
2016-11-04 14:10:31.171 HookTest[3436:335400] -[NSArray init] is hooked!
2016-11-04 14:10:31.171 HookTest[3436:335400] -[NSArray init] is hooked!
2016-11-04 14:10:31.174 HookTest[3436:335367] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CADisplay amer_init]: unrecognized selector sent to instance 0x600000030bc0'
*** First throw call stack:
(
0 CoreFoundation 0x00000001086d734b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x000000010813821e objc_exception_throw + 48
2 CoreFoundation 0x0000000108746f34 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
3 CoreFoundation 0x000000010865cc15 ___forwarding___ + 1013
4 CoreFoundation 0x000000010865c798 _CF_forwarding_prep_0 + 120
5 HookTest 0x0000000107b5c74c -[NSArray(Swizzling) amer_init] + 60
6 QuartzCore 0x000000010d242c66 -[CADisplay _initWithDisplay:] + 45
7 QuartzCore 0x000000010d2429a2 _ZL15ensure_displaysv + 688
8 QuartzCore 0x000000010d242c16 +[CADisplay mainDisplay] + 9
9 BackBoardServices 0x000000010c016af0 BKSDisplayServicesStart + 295
10 UIKit 0x0000000108afb04b _UIApplicationMainPreparations + 221
11 UIKit 0x0000000108afaf04 UIApplicationMain + 111
12 HookTest 0x0000000107b5da37 main + 583
13 libdyld.dylib 0x000000010b4b268d start + 1
14 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
Explore the unexpected——探讨意料之外的事
- 我的hook成功了,可喜可贺!
- 应用启动初始化时候创建了NSArray对象
- 从First throw call stack看来:
- dyld启动 ->
- start()->
- main()->
- UIApplicationMain->
- 链接UIKit->
- BackBoardServices->
- 链接QuartzCore ->
- CADisplay调用init(CADisplay无init方法)->
- 方法查找super class(super 未实现init方法)->
- 逐级查找最终调用NSObject的init方法(此时发现init方法是amer_init)->
- 在NSObject 方法调度表中未查找amer_init(因为amer_init是NSObject子类NSArray的方法)->
- CF_forwarding_prep_0准备进入消息转发->
- 消息转发失败->
- objc_exception_throw 程序抛出异常
Unexpected harvest——意料之外的收获
子类继承父类的方法,但是并没有默认重写父类方法。Hook要从在low-level层做,在一个子类最Hook,如果该方法是父类且子类没有重写,则父类的相应方法会被Hook。
The solution——解决方案
@implementation NSArray (Swizzling)
+ (void)load{
Method orig_init = class_getInstanceMethod([self class], @selector(init));
Method amer_init = class_getInstanceMethod([self class], @selector(amer_init));
method_exchangeImplementations(orig_init, amer_init);
}
- (instancetype)amer_init{
NSLog(@"-[NSArray init] is hooked!");
// invoke 'origin_init'
return [self amer_init];
}
- (instancetype)init{
NSLog(@"-[NSArray init],origin init");
if (self= [super init]) {
// do some things
}
return self;
}
@end
- FishHook:
正在使用,有时间再做总结