使用mpaas的iOS客户端如何一包支持任意环境切换(理论篇)

一个包任意切换不同测试环境是一直想做的。

以前开发任务少,打包少,就感觉和自己离的远。

这几天重新提起,于是用了三天时间,分析思路与实现。

记录一下这份喜悦。

首先mpaas客户端切换环境需要更改6个位置的配置代码文件:无线保镖图片,info.pllist,配置文件(纯文本),还有3个代码文件。

1.info.pllist 通过重写系统的NSBundle类的infoDictionary属性的set方法实现。这个在之前的博客中已经写过不再复述。链接:更改bundleid后蚂蚁金服MPSafeKeyboard的安全键盘不显示的解决办法

2.配置文件(纯文本)通过

    方法一:fishhook系统open方法(实践表明只有第一次安装客户端才会走open)

    方法二:hook系统NSBundle的- [pathForResource:ofType:],然后替换为相应文件

3.代码文件通过全局变量或沙盒标记,直接判断

4.无线保镖图片通过

  方法一:对系统NSData由file创建方法

  方法二:UIimage由file创建方法,

  方法三:NSBundle路径获取方法,

  方法四:open方法

  方法五:hopper搜索图片名字

  方法六:find.命令查找图片名字,确定其所在库

  方法七:根据后来发现mpaas提供了支持不同环境读取不同图片的方法,得以实现。

5.切换环境后还要对上个环境在沙盒的缓存数据进行清除,实验表明切换后需要删掉沙盒 Preferences文件夹内容,即仅删除[NSUserDefaults standardUserDefaults]下所有内容即可

以下是详细过程

上来先找图片和配置文件的open方法,结果控制台把内容都打印完了也没走到我的open方法,但其他文件会走。那就是fishhook晚了,由main方法里改到了第一个执行的load方法里。知识点:每个类都有load,哪个类的load先走?

答案是:Build Phases的Compile Sources文件上下顺序。

放到这个load里面,果然我的代码比其他任何代码都先走,但还是打印完其他信息才走我的open,而且只有第一次卸载安装客户端才会。但是打印信息肯定用NSLog,直接替换为我的MyNSLog,这下找到了打印第一个配置信息的类。

竟然是写到了定位方法里。说明客户端在load阶段就将所有配置信息都完成了。

然后看看第二个打印

这个打印出了配置文件的内容,然后hopper一下这个方法,果然直接用的pathForResource

void +[MPLiteSettingService initialize](void * self, void * _cmd) {
    var_-48 = self;
    if ([self checkCoverInstallation] != 0x0) {
            rbx = [[NSUserDefaults standardUserDefaults] retain];
            [rbx removeObjectForKey:@"mPaaSConfig_meta"];
            [rbx release];
            rbx = [[NSUserDefaults standardUserDefaults] retain];
            [rbx synchronize];
            [rbx release];
    }
    rbx = [[NSBundle mainBundle] retain];
    r14 = [[rbx pathForResource:@"meta" ofType:@"config"] retain];
    [rbx release];
    rdx = r14;
    [MPLiteSettingService generateMetaConfigWithPath:rdx type:@"meta"];
    rbx = [[MPaaSInterface sharedInstance] retain];
    r13 = [rbx enableSettingService];
    [rbx release];
    if (r13 != 0x0) {
            var_-112 = r14;
            r14 = [[NSBundle mainBundle] retain];
            rbx = [[r14 pathForResource:@"Settings" ofType:@"bundle"] retain];
            [r14 release];
            var_-56 = rbx;
            if (rbx != 0x0) {
                    rcx = @"bundle";
                    rbx = [[NSUserDefaults standardUserDefaults] retain];
                    rdx = @"mPaasSettingServiceRegistered";
                    r14 = [[rbx objectForKey:rdx, rcx] retain];
                    [rbx release];
                    if (r14 == 0x0) {
                            rbx = [[NSUserDefaults standardUserDefaults] retain];
                            [rbx setObject:@"Registered" forKey:@"mPaasSettingServiceRegistered"];
                            [rbx release];
                            [var_-48 registerDefaultsFromSettingsWithPlist:@"Root.plist" forBundle:var_-56];
                            rdx = @"Service.plist";
                            rcx = var_-56;
                            [var_-48 registerDefaultsFromSettingsWithPlist:rdx forBundle:rcx];
                            rbx = [[NSUserDefaults standardUserDefaults] retain];
                            [rbx synchronize];
                            [rbx release];
                    }
                    rbx = [[NSUserDefaults standardUserDefaults] retain];
                    rdx = @"kMPSelectedEnvironment";
                    r13 = [[rbx objectForKey:rdx, rcx] retain];
                    [rbx release];
                    rsi = @selector(length);
                    var_-48 = r13;
                    rbx = _objc_msgSend;
                    if (_objc_msgSend(r13, rsi, rdx, rcx) != 0x0) {
                            var_-64 = r14;
                            if ([var_-48 isEqualToString:@"Customizing"] != 0x0) {
                                    r15 = rbx;
                                    rax = (r15)(@class(NSMutableDictionary), @selector(alloc), @"Customizing");
                                    rax = (r15)(rax, @selector(init), @"Customizing");
                                    rdi = *__settings;
                                    *__settings = rax;
                                    [rdi release];
                                    rbx = [(r15)(@class(NSUserDefaults), @selector(standardUserDefaults), @"Customizing") retain];
                                    r14 = [(r15)(rbx, @selector(objectForKey:), @"rpcGW") retain];
                                    [rbx release];
                                    var_-104 = r14;
                                    r12 = @"";
                                    if (r14 == 0x0) {
                                            r12 = @"";
                                            r14 = r12;
                                    }
                                    (r15)(*__settings, @selector(setObject:forKey:), r14);
                                    rbx = [(r15)(@class(NSUserDefaults), @selector(standardUserDefaults), r14) retain];
                                    r14 = [(r15)(rbx, @selector(objectForKey:), @"logGW") retain];
                                    [rbx release];
                                    var_-96 = r14;
                                    if (r14 == 0x0) {
                                            r12 = @"";
                                            r14 = r12;
                                    }
                                    (r15)(*__settings, @selector(setObject:forKey:), r14, @"logGW");
                                    rbx = [(r15)(@class(NSUserDefaults), @selector(standardUserDefaults), r14, @"logGW") retain];
                                    r14 = [(r15)(rbx, @selector(objectForKey:), @"mpaasapi", @"logGW") retain];
                                    [rbx release];
                                    var_-88 = r14;
                                    if (r14 == 0x0) {
                                            r12 = @"";
                                            r14 = r12;
                                    }
                                    (r15)(*__settings, @selector(setObject:forKey:), r14, @"mpaasapi");
                                    rbx = [(r15)(@class(NSUserDefaults), @selector(standardUserDefaults), r14, @"mpaasapi") retain];
                                    r14 = [(r15)(rbx, @selector(objectForKey:), @"syncserver", @"mpaasapi") retain];
                                    [rbx release];
                                    var_-80 = r14;
                                    if (r14 == 0x0) {
                                            r12 = @"";
                                            r14 = r12;
                                    }
                                    (r15)(*__settings, @selector(setObject:forKey:), r14, @"syncserver");
                                    rbx = [(r15)(@class(NSUserDefaults), @selector(standardUserDefaults), r14, @"syncserver") retain];
                                    r14 = [(r15)(rbx, @selector(objectForKey:), @"syncport", @"syncserver") retain];
                                    [rbx release];
                                    var_-72 = r14;
                                    rdx = r14;
                                    if (r14 == 0x0) {
                                            r12 = @"";
                                            rdx = r12;
                                    }
                                    (r15)(*__settings, @selector(setObject:forKey:), rdx, @"syncport");
                                    rbx = [(r15)(@class(NSUserDefaults), @selector(standardUserDefaults), rdx, @"syncport") retain];
                                    r13 = [(r15)(rbx, @selector(objectForKey:), @"appId", @"syncport") retain];
                                    [rbx release];
                                    rdx = r13;
                                    if (r13 == 0x0) {
                                            r12 = @"";
                                            rdx = r12;
                                    }
                                    (r15)(*__settings, @selector(setObject:forKey:), rdx, @"appId");
                                    rbx = [(r15)(@class(NSUserDefaults), @selector(standardUserDefaults), rdx, @"appId") retain];
                                    r14 = [(r15)(rbx, @selector(objectForKey:), @"workspaceId", @"appId") retain];
                                    [rbx release];
                                    if (r14 != 0x0) {
                                            r12 = r14;
                                    }
                                    rsi = @selector(setObject:forKey:);
                                    rdx = r12;
                                    rcx = @"workspaceId";
                                    (r15)(*__settings, rsi, rdx, rcx);
                                    [r14 release];
                                    [r13 release];
                                    [var_-72 release];
                                    [var_-80 release];
                                    [var_-88 release];
                                    [var_-96 release];
                                    rdi = var_-104;
                            }
                            else {
                                    r15 = rbx;
                                    r14 = [(r15)(@class(NSBundle), @selector(bundleWithPath:), var_-56) retain];
                                    rbx = [(r15)(r14, @selector(pathForResource:ofType:), var_-48, @"config") retain];
                                    [r14 release];
                                    rsi = @selector(generateMetaConfigWithPath:type:);
                                    rdx = rbx;
                                    rcx = var_-48;
                                    (r15)(@class(MPLiteSettingService), rsi, rdx, rcx);
                                    rdi = rbx;
                            }
                            [rdi release];
                            r14 = var_-64;
                    }
                    [var_-48 release];
                    [r14 release];
            }
            [var_-56 release];
            r14 = var_-112;
    }
    NSLog(@"Setting service with values: %@", *__settings);
    [r14 release];
    return;
}

然后对这个pathForResource进行hook.创建新的分类NSBundle+YYY放到Build Phases的Compile Sources最上面。发现还是没拦截住,原因应该是NSBundle的分类按照主类所在的顺序执行,因此交换方法写在上面那个load里,但是这里。。。

有新发现。

交换后的方法还在分类写,执行交换的方法在上面load写,竟然没有问题。配置文件获取成功。

还有一张图片没找到

试了很多办法

然后记得以前find. 命令搜到过 图片名字就在那个库里,hopper就没有

在对整个工程find 搜matches竟然都没有。直接搜名字 

然后搜出来了这个

//
//  MPaaSConfigInfo.h
//  APMPaaS
//
//  Created by yangwei on 17/4/25.
//  Copyright © 2017年 Alipay. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface MPaaSConfigInfo : NSObject

/**
 *  当前App的无线保镖图片,是否需要区分平台。
 *  设置为NO,表示无线保镖SDK不需要区分平台,使用默认的 yw_1222.jpg 安全图片;
 *  设置为YES,表示无线保镖SDK需要区分平台,通过分配的 authCode,来指定在当前App中使用的无线保镖安全图片,并将图片设置为yw_1222_authCode.jpg;
 *
 *  默认返回为NO。钱包中不需要关心;
 *  mPaaS用户一般也不需要修改,只有在 yw_1222.jpg 图片与其他平台发生冲突时(如同时使用 mPaaS与阿里百川相关服务),需要在 category 中重写此方法,返回YES来区分图片。
 *
 * @return 默认返回NO
 */
+ (BOOL)enableMPaaSAuthCode;

/**
 *  获取设置无线保镖接口的authCode值
 *
 *  根据 < enableMPaaSAuthCode > 的返回值,若为NO,此方法返回nil;若为 YES,此方法返回 @"1000";
 *  钱包中默认为nil,mPaaS 用户根据需要重写 < enableMPaaSAuthCode > 方法
 *
 *  @return 默认返回 nil
 */
+ (NSString *)openSecurityAuthCode;

@end

这个图片就使用蚂蚁提供的来做了。

但还想看看为啥找不到?

对enableMPaaSAuthCode进行跟踪。因为它会把名字拼到一起

bool +[MPaaSConfigInfo enableMPaaSAuthCode](void * self, void * _cmd) {
    rax = 0x1 & 0xff;
    return rax;
}

继续看看哪里调用了enableMPaaSAuthCode

只有

void * +[MPaaSConfigInfo openSecurityAuthCode](void * self, void * _cmd) {
    if ([MPaaSConfigInfo enableMPaaSAuthCode] != 0x0) {
            rbx = @"1000";
            [rbx retain];
    }
    else {
            rbx = 0x0;
    }
    rax = [rbx autorelease];
    return rax;
}

然后找openSecurityAuthCode

0000000102085728         dq         0x1018c8f10                                 ; @selector(openSecurityAuthCode), 
"openSecurityAuthCode", DATA XREF=
-[DTRpcOperation signRequest:]+1241, 
-[DTRpcOperation signRequest:]+1584, 
-[DTURLRequestOperation rpcV1Sign:newSign:request:]+568,
 -[DTURLRequestOperation rpcV1Sign:newSign:request:]+832,
 +[APSyncUtils signString:appKey:]+272,
 +[NSString safetySignatureWithInputStr:]+265

看哪个呢这个吧[DTRpcOperation signRequest:]

void -[DTRpcOperation signRequest:](void * self, void * _cmd, void * arg2) {
    r13 = self;
    r14 = [arg2 retain];
    r15 = [[r13 request] retain];
    rbx = [[r13 httpBodyParameters] retain];
    [rbx release];
    if (rbx != 0x0) {
            rbx = [[OpenSecurityGuardManager getInstance] retain];
            r12 = [[rbx getSecureSignatureComp] retain];
            [rbx release];
            if (r12 != 0x0) {
                    var_88 = r12;
                    var_78 = r15;
                    var_68 = r14;
                    var_70 = [[NSMutableString string] retain];
                    rbx = [[r13 method] retain];
                    r14 = [[rbx operationType] retain];
                    [r14 release];
                    [rbx release];
                    COND = r14 == 0x0;
                    r14 = var_70;
                    r13 = r13;
                    if (!COND) {
                            r12 = [[r13 method] retain];
                            rbx = [[r12 operationType] retain];
                            rcx = rbx;
                            [r14 appendFormat:@"Operation-Type=%@"];
                            [rbx release];
                            [r12 release];
                    }
                    r15 = [[r13 httpBodyParameters] retain];
                    r12 = var_68;
                    rbx = [[r15 objectForKey:@"requestData"] retain];
                    [r15 release];
                    var_90 = rbx;
                    if (rbx != 0x0) {
                            rbx = [var_90 retain];
                            if ([r13 isProtocolBuffers] == 0x0) {
                                    r14 = [[rbx dataUsingEncoding:0x4] retain];
                                    r15 = [[r14 base64EncodedStringWithOptions:0x0] retain];
                                    [rbx release];
                                    [r14 release];
                                    rbx = r15;
                                    r14 = var_70;
                            }
                            COND = [r14 length] == 0x0;
                            rcx = @"&";
                            if (COND) {
                                    rcx = @"";
                            }
                            [r14 appendFormat:@"%@Request-Data=%@"];
                            [rbx release];
                    }
                    if (r12 != 0x0) {
                            COND = [r14 length] == 0x0;
                            rcx = @"&";
                            if (COND) {
                                    rcx = @"";
                            }
                            [r14 appendFormat:@"%@Ts=%@"];
                    }
                    NSLog(@"Rpc sign string v2:\n%@", r14);
                    r14 = [[DTRpcInterface sharedInstance] retain];
                    rbx = [[r13 request] retain];
                    rdx = rbx;
                    var_80 = [[r14 signKeyForRequest:rdx] retain];
                    [rbx release];
                    [r14 release];
                    r12 = [DTRpcUtils useNewSign];
                    rbx = [[r13 customAppKey] retain];
                    r15 = [rbx length];
                    [rbx release];
                    if (r15 != 0x0) {
                            r15 = var_78;
                            rbx = [[r13 customAppKey] retain];
                            [var_80 release];
                            var_80 = rbx;
                            rcx = @"appkey";
                            rdx = rbx;
                            [r15 setValue:rdx forHTTPHeaderField:rcx];
                            var_60 = @"input";
                            var_58 = var_70;
                            rbx = [[NSDictionary dictionaryWithObjects:rdx forKeys:rcx count:0x1] retain];
                            r14 = [[OpenSecurityGuardParamContext createParamContextWithAppKey:var_80 paramDict:rbx requestType:*_OPEN_ENUM_SIGN_COMMON_MD5] retain];
                            [rbx release];
                            r12 = [[MPaaSConfigInfo openSecurityAuthCode] retain];
                            rsi = @selector(signRequest:authCode:);
                            rdx = r14;
                            rbx = [_objc_msgSend(var_88, rsi) retain];
                            [r12 release];
                            if (rbx != 0x0) {
                                    rsi = @selector(setValue:forHTTPHeaderField:);
                                    rdx = rbx;
                                    _objc_msgSend(r15, rsi);
                            }
                            [rbx release];
                            rdi = r14;
                    }
                    else {
                            r15 = var_78;
                            if (r12 != 0x0) {
                                    var_50 = @"input";
                                    var_40 = var_70;
                                    *(&var_50 + 0x8) = @"atlas";
                                    *(&var_40 + 0x8) = @"a";
                                    rax = [NSDictionary dictionaryWithObjects:rdx forKeys:rcx count:0x2];
                                    rax = [rax retain];
                                    var_A0 = rax;
                                    r12 = [[OpenSecurityGuardParamContext createParamContextWithAppKey:var_80 paramDict:rax requestType:*_OPEN_ENUM_SIGN_ATLAS] retain];
                                    var_98 = r12;
                                    rbx = [[MPaaSConfigInfo openSecurityAuthCode] retain];
                                    r14 = [[var_88 signRequest:r12 authCode:rbx] retain];
                                    [rbx release];
                                    if (r14 != 0x0) {
                                            [r15 setValue:r14 forHTTPHeaderField:@"Sign"];
                                            [r15 setValue:@"1" forHTTPHeaderField:@"SignType"];
                                            NSLog(@"%@", cfstring_a);
                                    }
                                    rsi = r14;
                                    rdx = var_70;
                                    NSLog(@"sign v2:%@,content:%@", rsi, rdx);
                                    [r14 release];
                                    [var_98 release];
                                    rdi = var_A0;
                            }
                            else {
                                    var_60 = @"input";
                                    var_58 = var_70;
                                    rbx = [[NSDictionary dictionaryWithObjects:rdx forKeys:rcx count:0x1] retain];
                                    r14 = [[OpenSecurityGuardParamContext createParamContextWithAppKey:var_80 paramDict:rbx requestType:*_OPEN_ENUM_SIGN_COMMON_MD5] retain];
                                    [rbx release];
                                    r12 = [[MPaaSConfigInfo openSecurityAuthCode] retain];
                                    rsi = @selector(signRequest:authCode:);
                                    rdx = r14;
                                    rbx = [_objc_msgSend(var_88, rsi) retain];
                                    [r12 release];
                                    if (rbx != 0x0) {
                                            rsi = @selector(setValue:forHTTPHeaderField:);
                                            rdx = rbx;
                                            _objc_msgSend(r15, rsi);
                                    }
                                    [rbx release];
                                    rdi = r14;
                            }
                    }
                    [rdi release];
                    r14 = var_68;
                    r12 = var_88;
                    if ([r13 isProtocolBuffers] == 0x0) {
                            rbx = [[var_90 dataUsingEncoding:0x4] retain];
                            [r15 setHTTPBody:rbx];
                            [rbx release];
                    }
                    [var_80 release];
                    [var_90 release];
                    [var_70 release];
            }
            [r12 release];
    }
    [r15 release];
    [r14 release];
    if (*___stack_chk_guard != *___stack_chk_guard) {
            __stack_chk_fail();
    }
    return;
}

r12 = [[MPaaSConfigInfo openSecurityAuthCode] retain];

rsi = @selector(signRequest:authCode:);

rdx = r14;

rbx = [_objc_msgSend(var_88, rsi) retain];

[r12 release];

没看到调用就release了。

那肯定在signRequest:authCode这个方法里传值了

void * -[SecurityGuardOpenSecureSignature signRequest:authCode:](void * self, void * _cmd, void * arg2, void * arg3) {
    rax = loc_100b214f9(self, _cmd, arg2, arg3);
    return rax;
}

这个再点已经跳不过去了

加密的东西肯定保护得好,至此图片名字获取已失败告终

代码写完,配置一个新环境,代码写死后成功开启了。

现在需要一个全局变量控制加载哪个环境的参数。

还要加一个图形界面选择环境切换,重写蚂蚁封装的didfinishLaunch,结果黑屏  此方案kill

另一个方案,先进默认环境,点击某个按钮提示切换环境,选择对应环境做三个事

1.清上面说的NSUserDefaults内容和沙盒内所有文件

2.将环境名称写入本地,沙盒的其他地方新创建文件来做。

3.退出重新进

然后新打开app就是切换后的环境,和以前卸载重装一样

如果直接退出,下次进来则是不会清除上次的数据的,还可以按照上次设置的环境来用。

后记:

蚂蚁也提供了动态切换环境的方法,iOS 环境切换 - 移动开发平台 mPaaS - 阿里云

这个方法和上面的方式同时使用会使上面的失效,解决办法:hook方法pathForResource,在读取Settings.bundle的时候进行拦截

- (nullable NSString *)pathForResource:(nullable NSString *)name ofTypeS:(nullable NSString *)ext;

{

    if ([name containsString:@"Settings"]) {

        name = @"";

    }

    return [self pathForResource:name ofTypeS:ext];

}

再后记:

实现代码见下面这篇博客 拿来即用删掉即走:iOS客户端无侵入、一包任意环境切换实践篇__小呵呵的博客-CSDN博客

 

 。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值