IOS中如何Hook訊息
年過完了,該收心了,該開始學習了。
我們都知道在windows下可以通過API輕鬆的hook很多訊息,IOS裡面貌似還沒有現成的API(可能是我還沒發現吧),前段時間碰巧看到Objective-C運行時的一些東西,於是心想著是不是可以嘗試一下實現hook的功能。
為什麼要hook訊息呢,因為有些時候我們可能無法直接去繼承一個類,卻又想先截獲某些訊息做一些處理,然後再接著進行正常的處理流程。今天使用運行時的一些API實現了基本的hook功能。
下面先直接上源碼:
1 // 2 // TestHookObject.m 3 // TestHookMessage 4 // 5 // Created by mapleCao on 13-2-28. 6 // Copyright (c) 2013年 mapleCao. All rights reserved. 7 // 8 9 #import "TestHookObject.h"10 #import <objc/objc.h>11 #import <objc/runtime.h>12 13 @implementation TestHookObject14 15 // this method will just excute once16 + (void)initialize17 {18 // 擷取到UIWindow中sendEvent對應的method19 Method sendEvent = class_getInstanceMethod([UIWindow class], @selector(sendEvent:));20 Method sendEventMySelf = class_getInstanceMethod([self class], @selector(sendEventHooked:));21 22 // 將目標函數的原實現綁定到sendEventOriginalImplemention方法上23 IMP sendEventImp = method_getImplementation(sendEvent);24 class_addMethod([UIWindow class], @selector(sendEventOriginal:), sendEventImp, method_getTypeEncoding(sendEvent));25 26 // 然後用我們自己的函數的實現,替換目標函數對應的實現27 IMP sendEventMySelfImp = method_getImplementation(sendEventMySelf);28 class_replaceMethod([UIWindow class], @selector(sendEvent:), sendEventMySelfImp, method_getTypeEncoding(sendEvent));29 }30 31 /*32 * 截獲到window的sendEvent33 * 我們可以先處理完以後,再繼續調用正常處理流程34 */35 - (void)sendEventHooked:(UIEvent *)event36 {37 // do something what ever you want38 NSLog(@"haha, this is my self sendEventMethod!!!!!!!");39 40 // invoke original implemention41 [self performSelector:@selector(sendEventOriginal:) withObject:event];42 }43 44 @end
下面我們來逐行分析一下上面的代碼:
首先我們來看19行,這一行主要目的是擷取到UIWindow原生的sendEvent的Method(一個結構體,用來對方法進行描述),接著第20行是擷取到我們自己定義的類中的sendEvent的Method(這兩個方法的簽名必須一樣,否則運行時報錯)。第23行我們通過UIWindow原生的sendEvent的Method擷取到對應的IMP(一個函數指標),第24行使用運行時API Class_addMethod給UIWindow類添加了一個叫sendEventOriginal的方法,該方法使用UIWindow原生的sendEvent的實現,並且有著相同的方法簽名(必須相同,否則運行時報錯)。27行是擷取我們自訂類中的sendEventMySelf的IMP,28行是關鍵的一行,這一行的主要目的是為UIWindow原生的sendEvent指定一個新的實現,我們看到我們將該實現指定到了我們自己定義的sendEventMySelf上。到了這兒我們就完成了偷梁換柱,大功告成。
執行上面這些行以後,我們就成功的將UIWindow的sendEvent重新導向到了我們自己的寫的sendEventMySelf的實現,然後將其原本的實現重新導向到了我們給它新添加的方法sendEventOriginal中。而sendEventMySelf中,我們首先可以對這個訊息進行我們想要的處理,然後再通過41行調用sendEventOriginal方法轉到正常的執行流程。
這塊兒你可能有個困惑 “我們自訂類中明明是沒有sendEventOriginal方法的啊?”
為什麼執行起來不報錯,而且還會正常執行?因為sendEventMySelf是UIWindow的sendEvent重新導向過來的,所以在運行時該方法中的self代表的就是UIWindow的執行個體,而不再是TestHookObject的執行個體了。加上sendEventOriginal是我們通過運行時添加到UIWindow的執行個體方法,所以可以正常調用。當然如果直接通過下面這種方式調用也是可以的,只不過編譯器會提示警告(編譯器沒那麼智能),因此我們採用了performSelector的調用方式。
[self sendEventOriginal:event];
以上就是Hook的實現,使用時我們只需要讓TestHookObject類執行一次初始話操作就可以了,執行完以後。UIWindow的sendEvent訊息就會會hook到我們的sendEventMySelf中了。
下面是調用代碼:
Install Hook
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; // Override point for customization after application launch. self.viewController = [[[TestHookViewController alloc] initWithNibName:@"TestHookViewController" bundle:nil] autorelease]; self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; //hook UIWindow‘s SendEvent method TestHookObject *hookSendEvent = [[TestHookObject alloc] init]; [hookSendEvent release]; UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; btn.center = CGPointMake(160, 240); btn.backgroundColor = [UIColor redColor]; [btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventAllEvents]; [self.window addSubview:btn]; [btn release]; return YES;}
代碼中我們還專門添加了一個button來驗證,hook完以後訊息是否正常傳遞。經驗證訊息流程轉完全正常。
2013年寫的第一篇部落格,希望大家能喜歡!!!
註:歡迎轉載,轉載請註明出處!
參考資料:Objective-C運行時原始碼
Objective-C Runtime Reference
一片-楓葉
2013年2月28日