Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ISSUE-005

FirebaseSDK读取登录状态失败

常见表现

使用谷歌帐号登录的游戏,第二次打开时出现闪退,或是需要重新登录。

相关源码

FIRAuthKeychainServices.m

- (nullable NSData *)itemWithQuery:(NSDictionary *)query error:(NSError **_Nullable)error {
  NSMutableDictionary *returningQuery = [query mutableCopy];
  returningQuery[(__bridge id)kSecReturnData] = @YES;
  returningQuery[(__bridge id)kSecReturnAttributes] = @YES;
  returningQuery[(__bridge id)kSecMatchLimit] = @2;

  CFArrayRef result = NULL;
  OSStatus status =
      SecItemCopyMatching((__bridge CFDictionaryRef)returningQuery, (CFTypeRef *)&result);

  if (status == noErr && result != NULL) {
    NSArray *items = (__bridge_transfer NSArray *)result;
    if (items.count == 0) {
      return nil;
    }
    //...
  }
}

原因分析

FirebaseSDK传入参数kSecMatchLimit=2,并期望返回结果是一个NSArray

但目前PlayCover的代码并没有正确处理kSecMatchLimit=2的情况,PlayCover返回的是一个NSDictionary,类型不匹配导致出错。

解决方法

根本解决方法:修改PlayCover代码。

临时解决方法一:每次打开游戏之前,右键游戏图标,清除PlayChain数据可解决闪退。

临时解决方法二:插入跳转指令,从A处直接跳转到B处。

Firebase SDK旧版本(Objcetive-C):

- (nullable NSData *)itemWithQuery:(NSDictionary *)query error:(NSError **_Nullable)error {
  CFArrayRef result = NULL;
  OSStatus status =
      SecItemCopyMatching((__bridge CFDictionaryRef)returningQuery, (CFTypeRef *)&result);

  if (status == noErr && result != NULL) {
    NSArray *items = (__bridge_transfer NSArray *)result;
    
    /* --------- A --------- */
    if (items.count == 0) { 
      // throw error
    } else if (items.count > 1) {
      // log("Keychain query returned multiple results")
    }

	NSDictionary *item = items[0];
	
	/* --------- B --------- */
    return item[(__bridge id)kSecValueData];
  }
}
0x4C8A04    STR    XZR, [SP,#0x130+result]
0x4C8A08    ADD    X1, SP, #0x130+result ; result
0x4C8A0C    MOV    X0, X19 ; query
0x4C8A10    BL     _SecItemCopyMatching
0x4C8A14    MOV    X3, X0
0x4C8A18    CBNZ   W0, loc_4C8B4C
0x4C8A1C    LDR    X20, [SP,#0x130+result]
0x4C8A20    CBZ    X20, loc_4C8B4C
0x4C8A24    MOV    X0, X20
0x4C8A28    BL     _objc_msgSend$count   ; 改为B #0x110,跳转至0x4C8B38
0x4C8A2C    CBZ    X0, loc_4C8BCC
0x4C8A30    MOV    X0, X20
0x4C8A34    BL     _objc_msgSend$count
0x4C8A38    CMP    X0, #2
0x4C8A3C    B.CC   loc_4C8A64

0x4C8B1C loc_4C8B1C
0x4C8B1C    MOV    X0, X21
0x4C8B20    BL     _objc_release
0x4C8B24    MOV    X0, X21
0x4C8B28    MOV    X2, #0
0x4C8B2C    BL     _objc_msgSend$objectAtIndexedSubscript_
0x4C8B30    MOV    X29, X29
0x4C8B34    BL     _objc_retainAutoreleasedReturnValue
0x4C8B38    MOV    X21, X0               ; 跳转目标位置
0x4C8B3C    ADRP   X8, #_kSecValueData_ptr@PAGE
0x4C8B40    LDR    X8, [X8,#_kSecValueData_ptr@PAGEOFF]
0x4C8B44    LDR    X2, [X8]
0x4C8B48    B      loc_4C8BA8

0x4C8BA8 loc_4C8BA8
0x4C8BA8    BL     _objc_msgSend$objectForKeyedSubscript_
0x4C8BAC    MOV    X29, X29
0x4C8BB0    BL     _objc_retainAutoreleasedReturnValue
0x4C8BB4    MOV    X22, X0
0x4C8BB8    MOV    X0, X21
0x4C8BBC    BL     _objc_release         ; 屏蔽这条指令,避免重复调用_objc_release
0x4C8BC0    B      loc_4C8C04

Firebase SDK新版本(Swift):

func getItemLegacy(query: [String: Any]) throws -> Data? {
	var returningQuery = query
	returningQuery[kSecReturnData as String] = true
	returningQuery[kSecReturnAttributes as String] = true
	returningQuery[kSecMatchLimit as String] = 2
	
	var result: AnyObject?
	let status = keychainStorage.get(query: returningQuery, result: &result)
	
	if let items = result as? [[String: Any]], status == noErr {
      /* --------- A --------- */
	  if items.isEmpty {
	    // throw error
	  } else if items.count > 1 {
	    // log("Keychain query returned multiple results")
	  }
		
      let item = items[0];
	  
	  /* --------- B --------- */
	  return item[kSecValueData as String] as? Data
	}
}

情况一:存在FirebaseAuth.framework

0x36A94    ADRL    X0, _$sSaySDySSypGGMd    ; 修改这两行,将[[String: Any]]类型改为[String: Any]类型
0x36A9C    ADRL    X1, _$sSaySDySSypGGMR    ; 在AuthErrorUtils.extractJSONObjectFromString()中能找到[String: Any]的type_metadata
0x36AA4    BL      ___swift_instantiateConcreteTypeFromMangledNameV2
0x36AA8    MOV     X3, X0
0x36AAC    ADRP    X8, #_$syXlN_ptr@PAGE
0x36AB0    LDR     X8, [X8,#_$syXlN_ptr@PAGEOFF]
0x36AB4    ADD     X0, SP, #0xB0+var_90
0x36AB8    ADD     X1, SP, #0xB0+var_70
0x36ABC    ADD     X2, X8, #8
0x36AC0    MOV     W4, #6
0x36AC4    BL      _swift_dynamicCast
0x36AC8    TBZ     W0, #0, loc_36B08
0x36ACC    LDR     X23, [SP,#0xB0+var_90]
0x36AD0    BL      _$s6Darwin5noErrs5Int32Vvg ; noErr.getter
0x36AD4    CMP     W20, W0
0x36AD8    B.NE    loc_36B00
0x36ADC    LDR     X8, [X23,#0x10]
0x36AE0    STP     X26, X8, [SP,#0xB0+var_A0]
0x36AE4    CMP     X8, #1                       ; 改为MOV X20, X23
0x36AE8    B.EQ    loc_36CB0                    ; 跳转至0x36D90

0x36D70 loc_36D70
0x36D70    LDR     X8, [X23,#0x10]
0x36D74    LDR     X19, [SP,#0xB0+var_A8]
0x36D78    CBZ     X8, loc_36F14
0x36D7C    LDR     X20, [X23,#0x20]
0x36D80    MOV     X0, X20
0x36D84    BL      _swift_bridgeObjectRetain
0x36D88    MOV     X0, X23
0x36D8C    BL      _swift_bridgeObjectRelease
0x36D90    ADRP    X8, #_kSecValueData_ptr@PAGE  ; 跳转目标位置
0x36D94    LDR     X8, [X8,#_kSecValueData_ptr@PAGEOFF]
0x36D98    LDR     X0, [X8]
0x36D9C    BL      _$sSS10FoundationE36_unconditionallyBridgeFromObjectiveCySSSo8NSStringCSgFZ

情况二:不存在FirebaseAuth.framework,和其他代码编译在一起了

0x6D96C    ADRL    X0, unk_BD971F8      ; 修改此行,将[[String: Any]]类型改为[String: Any]类型
0x6D974    BL      sub_35660            ; 搜索字符串Error while converting assumed plist to dictionary,往前找dynamicCast,可找到[String: Any]的type_metadata
0x6D978    MOV     X3, X0
0x6D97C    ADRP    X8, #_$syXlN_ptr@PAGE
0x6D980    LDR     X8, [X8,#_$syXlN_ptr@PAGEOFF]
0x6D984    ADD     X2, X8, #8
0x6D988    ADD     X0, SP, #0xB0+var_A0
0x6D98C    ADD     X1, SP, #0xB0+var_80
0x6D990    MOV     W4, #6
0x6D994    BL      j__swift_dynamicCast
0x6D998    TBZ     W0, #0, loc_6D9D0
0x6D99C    LDR     X22, [SP,#0xB0+var_A0]
0x6D9A0    BL      j__$s6Darwin5noErrs5Int32Vvg ; noErr.getter
0x6D9A4    CMP     W20, W0
0x6D9A8    B.NE    loc_6D9C8
0x6D9AC    LDR     X28, [X22,#0x10]
0x6D9B0    CMP     X28, #1                      ; 改为STR X27, [SP](暂存Exception信息)
0x6D9B4    B.EQ    loc_6DB88                    ; 改为MOV X20, X22
0x6D9B8    CBNZ    X28, loc_6DA70               ; 跳转至0x6DD04

0x6DCE4 loc_6DCE4
0x6DCE4    MOV     X0, X22
0x6DCE8    BL      j__swift_bridgeObjectRelease
0x6DCEC    ADRL    X1, unk_BD96718
0x6DCF4    ADD     X0, SP, #0xB0+var_80
0x6DCF8    BL      sub_35728
0x6DCFC    MOV     X0, X22
0x6DD00    BL      j__swift_bridgeObjectRelease
0x6DD04    ADRP    X8, #_kSecValueData_ptr@PAGE  ; 跳转目标位置
0x6DD08    LDR     X8, [X8,#_kSecValueData_ptr@PAGEOFF]
0x6DD0C    LDR     X0, [X8]
0x6DD10    BL      j__$sSS10FoundationE36_unconditionallyBridgeFromObjectiveCySSSo8NSStringCSgFZ