首页 > 代码库 > 利用ios的hook机制实现adobe air ios ane下appdelegate的动态替换

利用ios的hook机制实现adobe air ios ane下appdelegate的动态替换

开发背景

利用adobe air开发完游戏后,需要针对ios或者android平台进行支付、推送的sdk接入,本文可以用来彻底解决ios平台下delegate生命周期几个回调函数的调用,实现原生的推送、支付功能的接入


hook知识背景

(objc里的Method Swizzling,本节内容转自http://blog.csdn.net/yiyaaixuexi)


在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。
每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。



我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP,
我们可以利用 class_replaceMethod 来修改类,
我们可以利用 method_setImplementation 来直接设置某个方法的IMP,
……
归根结底,都是偷换了selector的IMP,如下图所示:



实现思路

1、在MDChangeDelegateHelper类加载阶段就对adboe air的appdelegate类进行方法hook,确保在adobe air的appdelegate在创建前替换成我们新实现的类

2、需要3个SEL,

一个AppDelegate原SEL:oldSEL,

一个MDChangeDelegateHelper的默认SEL:defaultSEL,用于为原appdelegate添加默认的原oldSEL

一个MDChangeDelegateHelper的目标SEL:newSEL

方法替换思路:


3、替换后对应的sel关系为

oldSEL   -- newMethod

newSEL -- oldMethod 

所以当对应的appdelegate方法被调用时,

oldSEL找到了newMethod的实现,newMethod的实现在MDChangeDelegateHelper.m内的hookedxxx方法

在newMethold中调用了newSEL,newSEL指向oldMethod,实现了原流程的兼容


代码实现

//
//  MDChangeDelegateHelper.m
//  ChangeDelegateDemo
//
//  Created by ashqal on 14-10-31.
//  Copyright (c) 2014年 moredoo. All rights reserved.
//

#import "MDChangeDelegateHelper.h"
#import <UIKit/UIKit.h>

#import <objc/runtime.h>

void hookMethod(SEL oldSEL,SEL defaultSEL, SEL newSEL)
{
    //CTAppController
    Class aClass = objc_getClass("CTAppController");
    if ( aClass == 0 )
    {
        NSLog(@"!!!!!!!!!!!!!did not find adobe class!");
        return;
    }
    Class bClass = [MDChangeDelegateHelper class];
    //把方法加给aClass
    class_addMethod(aClass, newSEL, class_getMethodImplementation(bClass, newSEL),nil);
    class_addMethod(aClass, oldSEL, class_getMethodImplementation(bClass, defaultSEL),nil);
    
    Method oldMethod = class_getInstanceMethod(aClass, oldSEL);
    assert(oldMethod);
    Method newMethod = class_getInstanceMethod(aClass, newSEL);
    assert(newMethod);
    method_exchangeImplementations(oldMethod, newMethod);
  
}




@implementation MDChangeDelegateHelper




+ (void)load
{
    NSLog(@"MDChangeDelegateHelper load");
    //[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(createStarterNotificationChecker:)
                                                 //name:@"UIApplicationWillFinishLaunchingNotification" object:nil];
    //[self changeDelegate];
    
    
    hookMethod( @selector(application:didFinishLaunchingWithOptions:)
               ,@selector(defaultApplication:didFinishLaunchingWithOptions:)
               ,@selector(hookedApplication:didFinishLaunchingWithOptions:)
               );
    hookMethod( @selector(application:handleOpenURL:)
               , @selector(defaultApplication:handleOpenURL:)
               , @selector(hookedApplication:handleOpenURL:)
               );
    hookMethod(@selector(application:openURL:sourceApplication:annotation:)
               , @selector(defaultApplication:openURL:sourceApplication:annotation:)
               , @selector(hookedApplication:openURL:sourceApplication:annotation:));
    
    hookMethod(@selector(application:supportedInterfaceOrientationsForWindow:)
               , @selector(defaultApplication:supportedInterfaceOrientationsForWindow:)
               , @selector(hookedApplication:supportedInterfaceOrientationsForWindow:)
               );
    hookMethod(@selector(applicationDidBecomeActive:)
               , @selector(defaultApplicationDidBecomeActive:)
               , @selector(hookedApplicationDidBecomeActive:)
               );
    
    hookMethod(@selector(init)
               , @selector(init)
               , @selector(hookedInit)
               );
}


-(BOOL)hookedApplication:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)dic
{
    
    NSLog(@"hooked didFinishLaunchingWithOptions");
    [self hookedApplication:application didFinishLaunchingWithOptions:dic];
    return YES;
}

-(id)hookedInit
{
    NSLog(@"hooked init!!!");
    return [self hookedInit];
}




- (BOOL)hookedApplication:(UIApplication *)application handleOpenURL:(NSURL *)url {
    
    NSLog(@"hooked handleOpenURL");
    [self hookedApplication:application handleOpenURL:url];
    return YES;
}

- (BOOL)hookedApplication:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    NSLog(@"hooked openURL sourceApplication annotation");
    [self hookedApplication:application openURL:url sourceApplication:sourceApplication annotation:annotation];
    return YES;
}

- (NSUInteger) hookedApplication:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    NSLog(@"hooked supportedInterfaceOrientationsForWindow");
    [self hookedApplication:application supportedInterfaceOrientationsForWindow:window ];
    return 0;
}

- (void)hookedApplicationDidBecomeActive:(UIApplication *)application
{
    [self hookedApplicationDidBecomeActive:application];
    NSLog(@"hooked applicationDidBecomeActive");
}


- (BOOL)defaultApplication:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)dic{ return YES;}
- (BOOL)defaultApplication:(UIApplication *)application handleOpenURL:(NSURL *)url{return YES;}
- (BOOL)defaultApplication:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{return YES;}
- (NSUInteger) defaultApplication:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{return 0;}
- (void)defaultApplicationDidBecomeActive:(UIApplication *)application{}
- (id)init
{
    self = [super init];
    if (self) {
        
    }
    return self;
}

@end


注意点

a) 在运行时nslog出adobe air的appdelegate名字为CTAppController,所以将此类作为替换对象

b) 在对init函数进行替换时发现CTAppController没有实现init函数,所以将默认的init函数做了基础实现,不然无法创建实例了


利用ios的hook机制实现adobe air ios ane下appdelegate的动态替换