一、背景

由于某些历史原因,我们产品中50%以上活跃用户是弱账户。即 客户端按照某种规则生成的一个伪id 存在keychain 里,作为这个用户的唯一标识,实现快速登录。正常情况下是不会有问题。

最近,公司的apple 账号需要更换,这样我们的iOS app 需要从老账号 迁移到 新账号上,苹果也提供了迁移功能。那么,问题来了,apple 账号的变换 会导致 teamid 的变换,而keychain里面的存储 key与 teamid 有所关联。

说白了,teamid 变了,你keychain老账号存储的 伪id就读不到了,那不就等于 50%的弱账户丢失了么?

二、解决方案

1. 通过一些活动引导玩家,弱账户绑定成强账号;(用户行为)

2. 更换存储机制,实现 伪id 在不同apple账户(teamid不同情况下)依然保持一样,或是能够映射过去;

a. 因为keychain 与 teamid关联,所以转移周期内,转存储沙箱;缺点用户卸载,后期再安装将丢失弱账户;

b. 粘贴板,考虑到转移过程的长期性 以及 粘贴板自身的特性,并不能很好解决问题;

c. 浏览器缓存,跟粘贴板一样,限制太多;

3. 偷偷的实现弱账号 绑定成 强账号;(用户无感知)

4. 放弃部分玩家;(通过客服找回)

还有其它一些小方案都不是很合适,就不一一列出。最后我们选择了,1、2.a、3、4同步进行,尽可能减少弱账号的丢失。我们重点讲下第三点,其它没有什么好讲的。存储沙箱时候注意加密。

三、“弱账号” 绑定成 “强账号”

想偷偷实现 “弱账号” 绑定成 “强账号”,读取真实的 苹果设备号、手机号 等等,能想到的、以前能做到的都已经被苹果封堵了。如果通过一些非官方api,被发现,那问题更大。

那么只能退而寻求第三方辅助。移动有推出“一键免密登录”的SDK,感觉有点靠谱,不需要用户填写短信验证码、手机号码,可以实现三网的一键免密登录。

它们SDK提供了一个接口:

//显式登录
+ (void)getTokenExpWithController:(UIViewController *)vc
complete:(void (^)(id sender))complete;

 可以通过这个接口获取:(拿到openId 和 securityphone 可以实现强绑定)

{
capaids = "4,7";
openId = "eVMQwBpV1osTsEq64HBpWWag9WX-HdRA7DwKEOPh5UG2maarLm1g";
phonescrip = 968B5C447F12383C0767526C123847B60AF29BE543D735D0E4EB841EE3988AE1EF67B5DC21978332BC36876C9FF9C9CD8BA1139465419B4D600FF05144298FE876FF4A6D3D02C5ADA0DB9CA94291981F0DF882D3671052560CBC031CE9103162E1F22EBEE4E539F6A1880EA5201AA27B6CC279E98922DF356BB69032DC135250;
privateKey = 83AFF2D124CA4133;
securityphone = "173****3919";
}

  但是必须要弹出移动的授权界面:

好的,有这么一步,用户体验 会很不好,我们得想办法不展示这个界面,依然能拿到我们想要的数据。

四、人肉逆向

1.首先我用了class-dump,看看能不能搞出一些头文件,以失败告终。

然后我就想不弹出他们页面,依然能拿到数据,那必须知道他们界面的名称。

2.因为这个界面是模态出来的,所以method swizzling了模态方法:

- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^ __nullable)(void))completion NS_AVAILABLE_IOS(5_0);

 从而知道他们的导航类是 UANavigationController;

3.写了个定时器延迟10s获取最顶层页面,并且打印了VC栈:

UABufferViewController,UAOneKeyViewController(这里有打印这2个类的所有属性 以及 方法)

通过UAOneKeyViewController的指针,意外的发现了我需要的参数在一个字典里面phoneNumAccount

,这个字典里面存储了

{
capaids = "4,7";
openId = "eVMQwBpV1osTsEq64HBpWWag9WX-HdRA7DwKEOPh5UG2maarLm1g";
phonescrip = 968B5C447F12383C0767526C123847B60AF29BE543D735D0E4EB841EE3988AE1EF67B5DC21978332BC36876C9FF9C9CD8BA1139465419B4D600FF05144298FE876FF4A6D3D02C5ADA0DB9CA94291981F0DF882D3671052560CBC031CE9103162E1F22EBEE4E539F6A1880EA5201AA27B6CC279E98922DF356BB69032DC135250;
privateKey = 83AFF2D124CA4133;
securityphone = "173****3919";
}

  这样离我想要的东西又进了一步。

4.紧接着,我method swizzling了phoneNumAccount的set方法,并且在-(void)qiye_setPhoneNumAccount:(NSDictionary*)dic;  这个方法里面打了断点,这样我就能看到写值phoneNumAccount的详细堆栈;

我画红线的2个步骤非常关键,一个是跳过移动api的方法,另外一个可以拿到所有通信的内容。

是的我不需要调用移动+ (void)getTokenExpWithController:(UIViewController *)vc complete:(void (^)(id sender))complete; 这个方法,直接绕过他们强制出现的用户同意界面。

然后我只要调用:

    UIViewController * vc = [[NSClassFromString(@"UABufferViewController") alloc] init];
[vc performSelector: NSSelectorFromString(@"loginExplicitly")];

  就可以通过-(void)qiye_setPhoneNumAccount:(NSDictionary*)dic;拿到openId 和 securityphone ;

5. 还有一个,UANetwork这个类暴露出来了,我们通过method swizzling:

+(void)qiye_requestNetworkWithURL:(id) url params:(id)params method:(id)method completion:(void (^)(id))completion
{
NSLog(@"qiye_requestNetworkWithURL........%@ %@ %@",url,params,method);
void (^netBlock)(id) = ^(id value){
NSLog(@"Block:%@",value);
if(completion) completion(value);
};
[self qiye_requestNetworkWithURL:url params:params method:method completion:netBlock];
}

 然后他们的网络请求参数,以及返回 什么的 ,都拿到了。

https://www.cmpassport.com/unisdk/rs/ckRequest
Block:{
capaids = "4,7";
desc = success;
eappid = "6h/kM9lZGkjoGumpei8nYQ==";
epackage = "Y6PKTCDWcDJJnDxs6pwKBkqR5fo14Zygd8lupt+9fLc=";
esign = "<null>";
privateKey = 83AFF2D124CA4133;
resultCode = 103000;
servertime = 17;
sourceid = 800120180112107085;
} https://wap.cmpassport.com:8443/log/logReport
Block:{
config = {
crashlog = 1;
limitM = 50;
limitN = 3;
limitX = 100;
norlog = 1;
sizelimit = 1;
timelimit = 1;
};
desc = success;
resultCode = 103000;
} http://www.cmpassport.com/unisdk/rs/getTelecomPhoneNumberNotify?ver=1.0&result=0&state=%7B%22timeStamp%22%3A%2220180115184428437%22%2C%22clientType%22%3A%221%22%2C%22appId%22%3A%228013416909%22%2C%22format%22%3A%22json%22%2C%22params%22%3A%22800120180112107085%22%2C%22version%22%3A%221.1%22%7D&msg=success&mobile=8E482352DBECD75680483DB0B4F8EBC5&sign=F43348BE1ED50B97B2F2BB392C0DE7632C73124E
Block:{
resultCode = 103000;
resultdata = {
openId = "eVMQwBpV1osTsEq64HBpWWag9WX-HdRA7DwKEOPh5UG2maarLm1g";
phonescrip = 968B5C447F12383C0767526C123847B60AF29BE543D735D0E4EB841EE3988AE1EF67B5DC21978332BC36876C9FF9C9CD8BA1139465419B4D600FF05144298FE876FF4A6D3D02C5ADA0DB9CA94291981F0DF882D3671052560CBC031CE9103162E1F22EBEE4E539F6A1880EA5201AA27B6CC279E98922DF356BB69032DC135250;
securityphone = "173****3919";
};
}

  

然后,我们就可以偷偷的实现强绑定了,但是前提是用户有使用了sim卡,并且开着4G网络。

总结:我是不是过分了,不推荐大家这样做。

iOS “弱账号” 暗转 “强账号”的更多相关文章

  1. 李洪强iOS开发之苹果企业开发者账号申请流程

    李洪强iOS开发之苹果企业开发者账号申请流程 一. 开发者账号类型选择 邓白氏码 DUNS number,是Data Universal Numbering System的缩写,是一个独一无二的9位数 ...

  2. Xamarin iOS教程之申请付费开发者账号下载证书

    Xamarin iOS教程之申请付费开发者账号下载证书 Xamarin iOS使用真机测试应用程序 在讲解iOS Simulator时,已经提到了虽然iOS Simulator可以模仿真实的设备,但是 ...

  3. iOS -转载-开发之个人开发者账号转公司开发者账号

    ps  :  个人开发者账号升级公司开发者账号的话需要账号开启双重认证,没有开启的话需要开启(不然走到可以升级的那步的话,点击update升级会提示为了安全起见需要账号开启双双重认证,反正我走到upd ...

  4. ebay如何确定同一电脑登陆了多个账号,以及同一账号登陆过多台电脑

    转自hilton 的BLOG http://jimqu.blog.51cto.com/105370/654691 一切要从ebay的买家保护说起 ebay作为一个电子商务平台,之所以可以汇聚如此众多的 ...

  5. RHEL账号总结一:账号的分类

    账号是一种用来记录单个用户或者多个用户的数据.RHEL中每一个合法的用户都必须拥有账号,才能使用RHEL. 在RHEL上的账号可以分为两类: 用户账号:用来存储单一用户的数据,你也可以使用一个用户账号 ...

  6. [转]10+倍性能提升全过程--优酷账号绑定淘宝账号的TPS从500到5400的优化历程

    摘要: # 10+倍性能提升全过程--优酷账号绑定淘宝账号的TPS从500到5400的优化历程 ## 背景说明 > 2016年的双11在淘宝上买买买的时候,天猫和优酷土豆一起做了联合促销,在天猫 ...

  7. iOS-个人开发者账号转公司开发者账号(邓白氏码申请教程)

    邓白氏编码申请 个人开发者账号转公司开发者账号,首先要申请邓白氏编码-DUNS,打开https://developer.apple.com/support/进行DUNS申请! 步骤如下: 1.选择Me ...

  8. iOS开发 .framework的Optional(弱引用)和Required(强引用)区别, 有错误 Library not found………………

    http://www.cnblogs.com/wanyakun/p/3494323.html 强引用(Required)的framework是一定会被加载到内存的,但是弱引用(Optional)的fr ...

  9. iOS—如何申请苹果公司开发者账号流程详细图文介绍(包括邓白氏编码的申请方法详细介绍)

    我们要申请开发者账号,首先就需要先注册一个苹果的apple id,然后再这个账号的基础上去继续,这个相信大家都知道 这是申请appleid的地址:https://appleid.apple.com/a ...

随机推荐

  1. px像素单位与IOS像素单位的换算

    本文转载至  http://blog.csdn.net/fanyuna/article/details/24032663 30px转成磅为单位=22磅=二号 磅=(像素/96)*72 =(30/96) ...

  2. android基础---->AccessibilityService的简单使用(一)

    AccessibilityService类可以帮助我们实现监听手机上别的应用,以下做一个简单的总结.我总是勇敢的离开一个人 却不懂如何巧妙的靠近一个人. AccessibilityService的使用 ...

  3. gulp 报错'wacth' errord

    gulp.wacth(...).watch is not a function 如图: 检查了gulpfile.js文件中的wacth事件:发现这样的写法出错: gulp.task('watch', ...

  4. 【BZOJ2879】[Noi2012]美食节 动态加边网络流

    [BZOJ2879][Noi2012]美食节 Description CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食 ...

  5. LeetCode 笔记系列15 Set Matrix Zeroes [稍微有一点hack]

    题目:Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place. Fol ...

  6. Sonnet-十四行诗

    <Wish> Of our best wishes we could desire increase, That thereby rose's aroma might never die, ...

  7. Fat URLs Client Identification

    w在每个URL后面都附加一个用户特有的标识码. HTTP The Definitive Guide Some web sites keep track of user identity by gene ...

  8. zipline自制data bundles

    Databundle zipline 缺省提供了一些行情的data bundle , 可以通过 zipline bundles 查看 其中 quandl 数据源是从 https://www.quand ...

  9. 如何获取Input标签自定义属性的值?

    HTML代码: <input type="hidden" value="${Name?if_exists}" id='ID' busCode = &quo ...

  10. requests+BeautifulSoup详解

    简介 Python标准库中提供了:urllib.urllib2.httplib等模块以供Http请求,但是,它的 API 太渣了.它是为另一个时代.另一个互联网所创建的.它需要巨量的工作,甚至包括各种 ...