標籤:version mes 定時器 ffffff numa nes 曆史 限制 ken
一、背景
由於某些曆史原因,我們產品中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/logReportBlock:{ 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=F43348BE1ED50B97B2F2BB392C0DE7632C73124EBlock:{ resultCode = 103000; resultdata = { openId = "eVMQwBpV1osTsEq64HBpWWag9WX-HdRA7DwKEOPh5UG2maarLm1g"; phonescrip = 968B5C447F12383C0767526C123847B60AF29BE543D735D0E4EB841EE3988AE1EF67B5DC21978332BC36876C9FF9C9CD8BA1139465419B4D600FF05144298FE876FF4A6D3D02C5ADA0DB9CA94291981F0DF882D3671052560CBC031CE9103162E1F22EBEE4E539F6A1880EA5201AA27B6CC279E98922DF356BB69032DC135250; securityphone = "173****3919"; };}
然後,我們就可以偷偷的實現強綁定了,但是前提是使用者有使用了sim卡,並且開著4G網路。
總結:我是不是過分了,不推薦大家這樣做。
iOS “弱帳號” 暗轉 “強帳號”