iOS “弱账号” 暗转 “强账号”
一、背景
由于某些历史原因,我们产品中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 “弱账号” 暗转 “强账号”的更多相关文章
- 李洪强iOS开发之苹果企业开发者账号申请流程
李洪强iOS开发之苹果企业开发者账号申请流程 一. 开发者账号类型选择 邓白氏码 DUNS number,是Data Universal Numbering System的缩写,是一个独一无二的9位数 ...
- Xamarin iOS教程之申请付费开发者账号下载证书
Xamarin iOS教程之申请付费开发者账号下载证书 Xamarin iOS使用真机测试应用程序 在讲解iOS Simulator时,已经提到了虽然iOS Simulator可以模仿真实的设备,但是 ...
- iOS -转载-开发之个人开发者账号转公司开发者账号
ps : 个人开发者账号升级公司开发者账号的话需要账号开启双重认证,没有开启的话需要开启(不然走到可以升级的那步的话,点击update升级会提示为了安全起见需要账号开启双双重认证,反正我走到upd ...
- ebay如何确定同一电脑登陆了多个账号,以及同一账号登陆过多台电脑
转自hilton 的BLOG http://jimqu.blog.51cto.com/105370/654691 一切要从ebay的买家保护说起 ebay作为一个电子商务平台,之所以可以汇聚如此众多的 ...
- RHEL账号总结一:账号的分类
账号是一种用来记录单个用户或者多个用户的数据.RHEL中每一个合法的用户都必须拥有账号,才能使用RHEL. 在RHEL上的账号可以分为两类: 用户账号:用来存储单一用户的数据,你也可以使用一个用户账号 ...
- [转]10+倍性能提升全过程--优酷账号绑定淘宝账号的TPS从500到5400的优化历程
摘要: # 10+倍性能提升全过程--优酷账号绑定淘宝账号的TPS从500到5400的优化历程 ## 背景说明 > 2016年的双11在淘宝上买买买的时候,天猫和优酷土豆一起做了联合促销,在天猫 ...
- iOS-个人开发者账号转公司开发者账号(邓白氏码申请教程)
邓白氏编码申请 个人开发者账号转公司开发者账号,首先要申请邓白氏编码-DUNS,打开https://developer.apple.com/support/进行DUNS申请! 步骤如下: 1.选择Me ...
- iOS开发 .framework的Optional(弱引用)和Required(强引用)区别, 有错误 Library not found………………
http://www.cnblogs.com/wanyakun/p/3494323.html 强引用(Required)的framework是一定会被加载到内存的,但是弱引用(Optional)的fr ...
- iOS—如何申请苹果公司开发者账号流程详细图文介绍(包括邓白氏编码的申请方法详细介绍)
我们要申请开发者账号,首先就需要先注册一个苹果的apple id,然后再这个账号的基础上去继续,这个相信大家都知道 这是申请appleid的地址:https://appleid.apple.com/a ...
随机推荐
- Python学习笔记6-字典Dict
Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度. >>> person ...
- 《C++ Primer Plus》第2章 开始学习C++ 学习笔记
C++程序由一个或多个被称为函数的模块组成.程序从main()函数(全部小写)开始执行,因此该函数必不可少.函数由函数头和函数体组成.函数头指出函数的返回值(如果有的话)的类型和函数期望通过参数传递给 ...
- poj_1464 动态规划
题目大意 N个节点构成一棵树形结构,在其中若干个节点上放置士兵,与被放置士兵的节点相连的边会被士兵看守.问需要至少在多少个节点上放置士兵,才能使得N-1条边都被看守. 题目分析 题目描述的结构为树形, ...
- 动态加载script文件
动态加载script文件: http://www.cnblogs.com/skykang/archive/2011/07/21/2112685.html
- 1455: 罗马游戏[左偏树or可并堆]
1455: 罗马游戏 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 1861 Solved: 798[Submit][Status][Discuss] ...
- python3 + selenium + (chrome and firefox)使用
目录 瞎扯一句 简介 最后放模板 瞎扯一句 最近在做一个关于 selenium 相关的项目,在选择浏览器方面,一般有3种方案: chrome phantomJs firefox(推荐) 网上有很多教程 ...
- Spring的泛型依赖注入
Spring 4.x 中可以为子类注入子类对应的泛型类型的成员变量的引用,(这样子类和子类对应的泛型类自动建立关系)具体说明: 泛型注入:就是Bean1和Bean2注入了泛型,并且Bean1和Bean ...
- WingIDE6.0神秘代码
python2: import string import random import sha BASE16 = '0123456789ABCDEF' BASE30 = '123456789ABCDE ...
- CH5201 数组组合【01背包】
5201 数字组合 0x50「动态规划」例题 描述 在N个数中找出其和为M的若干个数.先读入正整数N(1<N<100)和M(1<M<10000), 再读入N个正数(可以有相同的 ...
- 2017 Multi-University Training Contest - Team 1—HDU6035
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6035 题意:一棵树有n个点,每个点有自己的颜色,任意两个不同的点可以组成一条路径.也就是说一共有n(n ...