在我們深入討論之前,請注意:
話雖如此,這個bug還是可以遠程觸發的,而且可以導致受影響的設備上任何正在處理遠程消息(iMessage、Facebook Messenger、WhatsApp等)的iOS應用程序都會崩潰!
“Patrick,我想中國黑了我的iPhone”
雖然我通常會對這種難以置信的情景一笑置之,但我正在研究我的情緒智力,耐心地詢問為什么我的臺灣朋友會這樣想。
她聲稱,每當她輸入臺灣或更糟的詞時,都會收到一條帶有臺灣旗幟(????)的消息,這會讓她的iOS設備上的應用程序崩潰。
我仍然有點懷疑,但如下所示,這正是正在發生的事情!
我給她發了多個臺灣旗幟,導致她的iMessage、Facebook Messenger和WhatsApp持續崩潰。
在這篇文章中,我們將說明如何分析和跟蹤這個遠程iOS缺陷的根本原因。
崩潰的設備是運行iOS 11.3(當時最新版本的iOS)的iPhone 7:
Device: iPhone 7, iPhone9,1 (US)
iOS: 11.3 (15E216)
Language: English, followed by Chinese
Region: United States
Jailbroken: No
以下是iMessenger(MobileSMS)提供的經過刪減的崩潰報告。它是從設備(通過Settings -> Privacy, Analytics, Analytics Data)獲取的:
{"app_name":"MobileSMS","timestamp":"2018-04-18 22:27:25.13 -0700","app_version":"5.0",
"slice_uuid":"feac9bde-20a2-37c2-86e0-119fb8b9b650","adam_id":0,"build_version":"1.0",
"bundleID":"com.apple.MobileSMS","share_with_app_devs":false,"is_first_party":true,
"bug_type":"109","os_version":"iPhone OS 11.3
(15E216)","incident_id":"9EE5610B-7A0C-4558-895F-CF876DEB6B07","name":"MobileSMS"}
Incident Identifier: 9EE5610B-7A0C-4558-895F-CF876DEB6B07
CrashReporter Key: 69340bb1126c092b97b9af069f4f6f037466ee0c
Hardware Model: iPhone9,1
Process: MobileSMS [10417]
Path: /Applications/MobileSMS.app/MobileSMS
Identifier: com.apple.MobileSMS
Version: 1.0 (5.0)
Code Type: ARM-64 (Native)
Role: Foreground
Parent Process: launchd [1]
Coalition: com.apple.MobileSMS [2015]
Date/Time: 2018-04-18 22:27:24.9896 -0700
Launch Time: 2018-04-18 22:26:16.9044 -0700
OS Version: iPhone OS 11.3 (15E216)
Baseband Version: 3.66.00
Report Version: 104
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000
Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [0]
Triggered by Thread: 6
Filtered syslog:
None found
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0:
0 libsystem_kernel.dylib 0x00000001824b3e08 0x1824b3000 + 3592
1 libsystem_kernel.dylib 0x00000001824b3c80 0x1824b3000 + 3200
2 CoreFoundation 0x00000001829f6e40 0x182909000 + 974400
3 CoreFoundation 0x00000001829f4908 0x182909000 + 964872
4 CoreFoundation 0x0000000182914da8 0x182909000 + 48552
5 GraphicsServices 0x00000001848f7020 0x1848ec000 + 45088
6 UIKit 0x000000018c8f578c 0x18c5d8000 + 3266444
7 MobileSMS 0x0000000100e1867c 0x100df8000 + 132732
8 libdyld.dylib 0x00000001823a5fc0 0x1823a5000 + 4032
....
Thread 6 name: Dispatch queue: com.apple.ResponseKit
Thread 6 Crashed:
0 CoreFoundation 0x0000000182922efc 0x182909000 + 106236
1 CoreEmoji 0x00000001886b2354 0x1886a6000 + 50004
2 CoreEmoji 0x00000001886b2354 0x1886a6000 + 50004
3 CoreEmoji 0x00000001886b2c80 0x1886a6000 + 52352
4 CoreEmoji 0x00000001886a8ebc 0x1886a6000 + 11964
5 ResponseKit 0x00000001968754ac 0x19683d000 + 230572
6 ResponseKit 0x0000000196872e9c 0x19683d000 + 220828
7 ResponseKit 0x00000001968739b4 0x19683d000 + 223668
8 ResponseKit 0x0000000196862e78 0x19683d000 + 155256
9 ResponseKit 0x0000000196862c00 0x19683d000 + 154624
10 ResponseKit 0x00000001968619f0 0x19683d000 + 150000
11 libdispatch.dylib 0x0000000182340b24 0x18233f000 + 6948
12 libdispatch.dylib 0x0000000182340ae4 0x18233f000 + 6884
13 libdispatch.dylib 0x000000018234aa38 0x18233f000 + 47672
14 libdispatch.dylib 0x000000018234b380 0x18233f000 + 50048
15 libdispatch.dylib 0x000000018234bd4c 0x18233f000 + 52556
16 libdispatch.dylib 0x000000018235411c 0x18233f000 + 86300
17 libsystem_pthread.dylib 0x0000000182673e70 0x182673000 + 3696
18 libsystem_pthread.dylib 0x0000000182673b08 0x182673000 + 2824
Thread 6 crashed with ARM Thread State (64-bit):
x0: 0x0000000000000000 x1: 0x00000001add1ad38 x2: 0x0000000000000000 x3: 0x00000001ad364438
x4: 0x0000000000000000 x5: 0x0000000000000001 x6: 0x0000000000000000 x7: 0x0000000000000000
x8: 0x0000000000000000 x9: 0x00000001b4e15930 x10: 0x0000000ffffffff8 x11: 0x0000000000000040
x12: 0xffffffffffffffff x13: 0x0000000000000001 x14: 0x0000000000000000 x15: 0x00002d0000002d00
x16: 0x0000000000000000 x17: 0x0000000000002d00 x18: 0x0000000000000000 x19: 0x0000000000000000
x20: 0x00000001add1ad38 x21: 0x0000000000000000 x22: 0x0000000000000000 x23: 0x00000001c4864cc0
x24: 0x00000001000404ef x25: 0x0000000000050000 x26: 0x0000000103d059e4 x27: 0x0000000103d059e4
x28: 0x0000000000000000 fp: 0x000000016f1a5b20 lr: 0x00000001886b2354
sp: 0x000000016f1a5b00 pc: 0x0000000182922efc cpsr: 0x80000000
Binary Images:
0x100df8000 - 0x100e43fff MobileSMS arm64 <feac9bde20a237c286e0119fb8b9b650> /Applications/MobileSMS.app/MobileSMS
0x182909000 - 0x182c9ffff CoreFoundation arm64 <cf162b3ca2883453b2914300d4f19612> /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
0x1886a6000 - 0x1886b7fff CoreEmoji arm64 <6d18237f09d23ce6aa6abb287d7aa515> /System/Library/PrivateFrameworks/CoreEmoji.framework/CoreEmoji
0x19683d000 - 0x19693ffff ResponseKit arm64 <4f7abc9a8f803cb2bff0172b8c69f13e> /System/Library/PrivateFrameworks/ResponseKit.framework/ResponseKit
我們將更詳細地討論這個問題,但是簡單說就是在地址0x00000000000000處發生一個EXC_BAD_ACCESS (SIGSEGV) (Exception Subtype: KERN_INVALID_ADDRESS)。
這類崩潰通常表示空指針解引用(NULL-pointer dereference)。在這里,這個錯誤似乎是在iOS執行某種類型的表情處理時觸發的(這與iMessenger接收到的臺灣標志的觸發相吻合)。
iOS系統崩潰報告中的其他相關信息包括:
我們的目標是現在追查崩潰的原因。也就是說,為什么0x0000000182922efc上的指令取消引用一個空指針?
我們將從反轉調用堆棧中出現的動態庫(ResponseKit、CoreEmoji和CoreFoundation)開始。具體來說,我們將檢查在調用堆棧中出現的這些dylib中的地址的代碼。
因為我朋友的手機沒有越獄,所以我們只能從設備中獲取dylib二進制文件.我們必須從其他地方獲取它們。事實證明,最簡單的是來自于iOS 11.3的恢復映像。這類還原映像包含iOS系統二進制文件,例如我們要尋找的dylib。我們可以從ipsw.me獲取iOS 11.3還原映像(iPhone_4.7_P3_11.0_11.3_15E216_Restore.ipsw)
下載這個文件后,我們就可以通過hdiutil命令掛載058-97716-127.dmg磁盤映像:
$ hdiutil attach iPhone_4.7_P3_11.0_11.3_15E216_Restore/058-97716-127.dmg expected CRC32 $BDE79F12 /dev/disk2 GUID_partition_scheme
/dev/disk2s1 EFI
/dev/disk2s2 Apple_APFS
/dev/disk3 EF57347C-0000-11AA-AA11-0030654 /dev/disk3s1 41504653-0000-11AA-AA11-0030654 /Volumes/Emet15E216.D10D101D20D201OS
由于我們要尋找的dylib被嵌入到dyld共享緩存(dyld_shared_cache_arm64)中,所以我們必須提取它們。
使用jtool,這是最直接的,只需指定要提取的dylib的名稱(例如CoreEmoji),然后指定共享緩存的路徑:
jtool -e "CoreEmoji" /Volumes/Emet15E216.D10D101D20D201OS/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64
Extracting /System/Library/PrivateFrameworks/CoreEmoji.framework/CoreEmoji at 0x6b4e000 into dyld_shared_cache_arm64.CoreEmoji
提取了以下dylib(在崩潰線程的調用堆棧中分別引用了這些dylib):
yld_shared_cache_arm64_CoreEmoji
version: 69.3.0
sha1: 20F6BECF7C76A3FEAFEB8D2321F593388A3CB9B6
dyld_shared_cache_arm64_CoreFoundation
version: 1452.23.0
sha1: AD3A226884BB3612694B9AB37DF18F42452D5139
dyld_shared_cache_arm64_ResponseKit
version 109.0.0
sha1: BDA7F1F329321C20539499EAF1C36693823CF60E
不幸的是,我們必須符號化這些dylib,因為他們的大多數符號都被“編輯”了:
$ nm dyld_shared_cache_arm64_ResponseKit
0000000194ce6f9c t <redacted>
0000000194ce701c t <redacted>
0000000194ce7090 t <redacted>
0000000194ce70c4 t <redacted>
0000000194ce71e4 t <redacted>
0000000194ce72e0 t <redacted>
0000000194ce72e8 t <redacted>
0000000194ce72f0 t <redacted>
0000000194ce735c t <redacted>
0000000194ce7444 t <redacted>
...
不過,首先讓我們對提取的iOS dylib進行重新定位,以便它們的地址(在反匯編程序/反編譯器中查看時)與崩潰報告中的地址相匹配。
要對每個提取的dylib進行重新定位,我們使用崩潰報告中的基地址(例如CoreEmoji的0x1886a6000)。在Hopper中,可以通過Modify -> Change File Base Address…來重新建立二進制文件的基地址:
一旦重設好了,我們就可以解決符號的問題了。
我不確定這樣做的最佳方式,所以我只是利用了每個dylib的MacOS版本。具體來說,我將符號化的x64匯編(MacOS)與無符號的ARM64匯編(iOS)“匹配”。雖然有手動過程,但這工作得很好!
例如,以地址0x0000000196862c00(來自調用堆棧中的第9幀)為例。下面是方法的完整反編譯(在IOS ResponseKit dylib中),其中包含地址0x00000196862c00:
//iOS ResponseKit
int <redacted>_194d0ab58(int arg0) {
r25 = loc_19147d5e0(r2);
r22 = loc_19147d5e0(r4);
loc_19147d5e0(r5);
r27 = *_RKMessageResponseDontOverrideLanguageID | r7;
loc_19147d5e0(r6);
loc_19147d5d8(arg0, 0x1b3e37900, r25, r3, r22, r5, &var_58, 0x0, r27);
loc_19147d5e8();
loc_19147d5dc(r26);
loc_19147d5dc(r22);
loc_19147d5dc(r25);
loc_19147d5e0(var_58);
loc_19147d5dc(r20);
loc_19147d5dc(r21);
r0 = loc_19147d7cc(r19);
return r0;
}
如果我們反編譯MacOS的ResponseKit(/System/Library/PrivateFrameworks/ResponseKit.framework),我們可以在RKMessageResponseManager responsesForMessageImp:maximumResponses:forConversationHistory:forContext:withLanguage:options:方法中找到“匹配的”x64反編譯(請注意對RKMessageResponseDontOverrideLanguageID符號的引用):
* @class RKMessageResponseManager */
-(void *)responsesForMessageImp:(void *)arg2 maximumResponses:(unsigned long long)arg3 forConversationHistory:(void *)arg4 forContext:(void *)arg5 withLanguage:(void *)arg6 options:(unsigned long long)arg7 {
r14 = [arg2 retain];
r15 = [arg4 retain];
var_38 = [arg5 retain];
var_30 = arg6;
rbx = *_RKMessageResponseDontOverrideLanguageID;
r13 = [arg6 retain];
rax = [self responsesForMessageWithLanguageDetectionImp:r14 maximumResponses:arg3 forConversationHistory:r15 forContext:arg5 withLanguage:&var_30 inputModes:0x0 options:rbx | arg7];
[var_38 release];
[r15 release];
[r14 release];
r14 = [rax retain];
rbx = [var_30 retain];
[r13 release];
[rbx release];
rax = [r14 autorelease];
return rax;
}
現在我們知道iOS ResponseKit dylib中的int_194d0ab58方法實際上是RKMessageResponseManager responsesForMessageImp:maximumResponses:forConversationHistory:forContext:withLanguage:options:方法(注意對RKMessageResponseDontOverrideLanguageID符號的引用)。
一旦對dylib進行了重新基和(手動)符號化,就更容易理解這個bug了,因為方法名對它們的用途有相當的描述性。
我們將從分析崩潰線程(線程6)的調用堆棧中的地址開始,以揭示這個遠程iOS bug的根本原因。
跳過對libDispatch.dylib的調用,從堆棧幀#10開始,并將每個地址映射到它所屬的符號化方法(或塊):
#10 ResponseKit 0x00000001968619f0
-[RKMessageResponseManager responsesForMessage:maximumResponses:forContext:withLanguage:options:completionBlock:]
#9 ResponseKit 0x0000000196862c00
-[RKMessageResponseManager responsesForMessageImp:maximumResponses:forConversationHistory:forContext:withLanguage:options:]
#8 ResponseKit 0x0000000196862e78
-[RKMessageResponseManager responsesForMessageWithLanguageDetectionImp:maximumResponses:forConversationHistory:forContext:withLanguage:inputModes:options:]:
#7 ResponseKit 0x00000001968739b4
+[RKMessageClassifier messageClassification:withLanguageIdentifier:conversationTurns:]:
#6 ResponseKit 0x0000000196872e9c
-[NSLinguisticTagger languageOfRange:withAdditionalContext:withPreferredLanguages:]
#5 ResponseKit 0x00000001968754ac
+[RKUtilities removeEmoji:]
#4 CoreEmoji 0x00000001886a8ebc
CEMStringContainsEmoji
#3 CoreEmoji 0x00000001886b2c80
unnamed subroutine
#2 CoreEmoji 0x00000001886b2354
unnamed subroutine
#1 CoreEmoji 0x00000001886b2354
unnamed subroutine
#0 CoreFoundation 0x0000000182922efc
CFStringCompare + 0x38
好吧發生什么事了?看起來是這樣的,當接收到消息時,ResponseKit會對消息進行分類,并且(如果某些分類是真的話)調用+[RKUtilities removeEmoji:]方法。這方法調用CoreEmoji動態庫來執行實際的表情符號移除。
iOS為什么要刪除表情符號?我們很快就會講到的!
在調用一些未命名的子程序后,CoreEmoji調用CFStringCompare函數,該函數在地址0x0000000182922efc處的指令處崩潰。
地址0x0000000182922efc是故障指令的地址。它是調用堆棧(即幀#0)中的最終地址,也是崩潰報告“ARM線程狀態”部分中的pc(程序計數器)寄存器中的最終地址。
CFStringCompare在0x0000000182922efc有什么指令?
0000000180dcaefc ldr x8, [x21]
ldr arm指令將“程序相對偏移或外部地址加載到寄存器”(arm)。在這里,它試圖取消引用,并將值從x21寄存器加載到x8寄存器中。
查看崩潰報告中的“ARM線程狀態”部分,可以看到x21寄存器在崩潰時是空的(0x00000000000000):
Thread 6 crashed with ARM Thread State (64-bit):
x0: 0x0000000000000000 x1: 0x00000001add1ad38 x2: 0x0000000000000000 x3: 0x00000001ad364438
x4: 0x0000000000000000 x5: 0x0000000000000001 x6: 0x0000000000000000 x7: 0x0000000000000000
x8: 0x0000000000000000 x9: 0x00000001b4e15930 x10: 0x0000000ffffffff8 x11: 0x0000000000000040
x12: 0xffffffffffffffff x13: 0x0000000000000001 x14: 0x0000000000000000 x15: 0x00002d0000002d00
x16: 0x0000000000000000 x17: 0x0000000000002d00 x18: 0x0000000000000000 x19: 0x0000000000000000
x20: 0x00000001add1ad38 x21: 0x0000000000000000 x22: 0x0000000000000000 x23: 0x00000001c4864cc0
x24: 0x00000001000404ef x25: 0x0000000000050000 x26: 0x0000000103d059e4 x27: 0x0000000103d059e4
x28: 0x0000000000000000 fp: 0x000000016f1a5b20 lr: 0x00000001886b2354
sp: 0x000000016f1a5b00 pc: 0x0000000182922efc cpsr: 0x80000000
如果試圖取消引用空地址(指針),這將與EXC_BAD_ACCESS(SIGSEGV)一起崩潰,這正是崩潰報告中給出的確切原因:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000
那么x21中的值應該是什么呢?很明顯,一個有效地址。
但是,查看故障指令之前的指令(在CFStringCompare函數中)的arm64反匯編代碼,我們可以看到它是傳遞給CFStringCompare的第一個參數。
_CFStringCompare:
0000000182922ec4 stp x22, x21, [sp, #-0x30]!
0000000182922ec8 stp x20, x19, [sp, #0x10]
0000000182922ecc stp x29, x30, [sp, #0x20]
0000000182922ed0 add x29, sp, #0x20
0000000182922ed4 mov x19, x2
0000000182922ed8 mov x20, x1
0000000182922edc mov x21, x0
0000000182922ee0 tbz x21, 0x3f, loc_182922efc ;take this
loc_182922ee4:
0000000182922ee4 adrp x8, #0x1b3519000
0000000182922ee8 ldr x1, [x8, #0x308]
0000000182922eec mov x0, x21
0000000182922ef0 bl 0x181c1c900
0000000182922ef4 mov x3, x0
0000000182922ef8 b loc_182922fd0
loc_182922efc:
0000000182922efc ldr x8, [x21] ; b00m, we crash as x21 is NULL
也許反編譯更能說明問題:
_CFStringCompare
r21 = theString1;
if ((r21 & 0xffffffff80000000) != 0x0) {
r3 = loc_181c1c900(r21, *0x1b3519308);
}
else
{
r8 = *r21; //b00m, we crash as this is NULL
}
反匯編0x0000000182922edc,我們可以看到第一個參數(在X0寄存器中傳遞)被移動到x21寄存器中:
mov x21, x0
在0x0000000182922ee0(檢測指針是否被“標記”)的測試之后,代碼跳轉到地址0x0000000182922efc,在那里取消對空x21寄存器的引用,從而導致崩潰。
寄存器x21上的檢查(在此程序集指令中實現;TBZ x21、0x3f、loc_182922efc)是檢查指針是否“標記”的檢查。
標記指針是在iOS 7和MacOSX10.7中為64位架構引入的.帶標記的指針是一種特殊的指針,它將數據直接存儲到指針中,而不是進行內存分配。這具有明顯的性能優勢。blog.timac.org
CFStringCompare的函數定義是:
CFComparisonResult CFStringCompare(CFStringRef theString1, CFStringRef theString2, CFStringCompareFlags compareOptions);
第一個參數是一個命名為theString1的CFStringRef。由于崩潰是對第一個參數(X0,移動到x21)的取消引用,我們現在知道有些東西正在傳入theString1參數的空值!
好了,我們已經找到了崩潰的直接原因:傳遞給CFStringCompare的空字符串。
讓我們回顧一下調用堆棧跟蹤,找出為什么會錯誤地傳入這樣一個空值!
回想一下,CFStringCompare是由地址為0x00000001886b22ec的CoreEmoji.dylib(dyld_shared_cache_arm64_CoreEmoji)中的一個未命名函數調用的。
由于提取的CoreEmoji二進制文件(來自dyld共享緩存)不是符號化的,因此只需從dylib的MacOS版本中刪除這個子例程的分解代碼就更簡單了。
下面是這兩個版本的反編譯代碼(為了說明MacOS和iOS版本中的代碼是相同的):
//iOS (arm64)
int <redacted>_186b5a2ec {
var_10 = r20;
stack[-24] = r19;
r31 = r31 + 0xffffffffffffffe0;
saved_fp = r29;
stack[-8] = r30;
if (*qword_1b1c9baf8 != -0x1) {
dispatch_once(0x1b37f3af8, 0x1add1a6f8);
}
r20 = loc_182938048();
r19 = loc_1829387c8();
loc_1829111e8(r20);
if (*(int8_t *)byte_1b1c9bb00 != 0x0) {
r0 = 0x0;
}
else {
r0 = loc_182922ec4(r19, 0x1add1ad38, 0x0);
if (r0 != 0x0) {
if (CPU_FLAGS & NE) {
r0 = 0x1;
}
}
}
return r0;
}
//macOS (x64)
int sub_b9fe() {
if (*qword_128e8 != -1)
{
dispatch_once(qword_128e8, ^ {/* block implemented at sub_ba72 */ } });
}
rbx = CFLocaleCopyCurrent();
r14 = CFLocaleGetValue(rbx, **_kCFLocaleCountryCode);
CFRelease(rbx);
if (*(int8_t *)byte_128f0 != 0x0) {
rax = 0x0;
}
else {
rax = CFStringCompare(r14, @"CN", 0x0);
rax = rax != 0x0 ? 0x1 : 0x0;
}
return rax;
}
在arm64反編譯中,以下行表示對CFStringCompare的調用:
r0 = loc_182922ec4(r19, 0x1add1ad38, 0x0);
寄存器r19是第一個空參數(theString1),因此觸發了崩潰。
再查找幾行,我們可以看到r19被設置為調用loc_1829387c8()的返回值;
r19 = loc_1829387c8();
多虧了MacOS符號化的反編譯,我們可以看到這是對CFLocaleGetValue()的調用。
Apple記錄了以下功能:
CFTypeRef CFLocaleGetValue(CFLocaleRef locale, CFLocaleKey key);返回區域設置的鍵值對的給定鍵的對應值。
通過反編譯,我們可以確定locale是來自CFLocaleCopyCurrent()的返回值,而鍵是_kCFLocaleCountryCode。
因此,源代碼看起來可能如下所示:
CFLocaleRef locale = CFLocaleCopyCurrent();
CFStringRef countryCode = CFLocaleGetValue (locale, kCFLocaleCountryCode);
這段代碼后面緊接著是對布爾標志的檢查(iOS:byte_1b1c9bb00,MacOS:byte_128f0)。
堅持使用符號化的macOS dylib,我們可以找到這個值的交叉引用(x-ref),以確定它設置在哪里(sub_ba72):
void sub_ba72(void * _block) {
rbx = CFPreferencesCopyValue(@"Country", **_kCFPreferencesAnyApplication, **_kCFPreferencesAnyUser, **_kCFPreferencesCurrentHost);
if (rbx != 0x0) {
r14 = CFEqual(rbx, @"CN") != 0x0 ? 0x1 : 0x0;
CFRelease(rbx);
}
else {
r14 = 0x0;
}
*(int8_t *)byte_128f0 = r14;
return;
}
這段代碼(sub_ba72)確定用戶當前的‘國家’首選項。
如果不是中國(“cn”),則該標志設置為0x1(True)。如果國家是中國,或者CFPreferencesCopyValue()中止并返回NULL,則標志設置為0x0(False)。
我朋友手機的區域和語言沒有設置為“cn”,所以這個標志(AFAIK)應該設置為0x1(真):
但是,由于代碼用了else(反過來調用CFStringCompare(),它將指示該標志必須為0x0。
//check some flag ('CN')
if (*(int8_t *)byte_1b1c9bb00 != 0x0) {
r0 = 0x0;
}
//we take this path
else {
//call to CFStringCompare() that crashes
r0 = loc_182922ec4(r19, 0x1add1ad38, 0x0);
...
}
一種解釋是,由于某些原因,對CFPreferencesCopyValue(@“Country”.)調用失敗,會將標志設置為0x0。或者代碼認為由于某種(未知的)原因,手機的區域設置為“cn”?
無論如何,調用CFStringCompare時,第一個參數(寄存器r19)設置為NULL:
//call CFStringCompare()
// first parameter is NULL, and thus crashes
// second parameter is @"CN"
r0 = loc_182922ec4(r19, 0x1add1ad38, 0x0);
請注意,只有在對CFLocaleGetValue()的調用失敗(即返回NULL)時,r19寄存器才能為空。
一種解釋是,對CFLocaleCopyCurrent的調用返回null,這反過來會導致CFLocaleGetValue也返回null(這反過來會將null傳遞給CFStringCompare(),從而導致崩潰)。
如果我們查看蘋果代碼中的其他地方,比如它們的CFStringCompareWithOptionsAndLocale函數,可以看到它們在這里檢查CFLocaleCopyCurrent()的返回值:
locale = CFLocaleCopyCurrent();
langCode = ((NULL == locale) ? NULL : (const uint8_t *)_CFStrGetLanguageIdentifierForLocale(locale));
這意味著CFLocaleCopyCurrent()確實可能失敗,并返回NULL(因此應該檢查!)
不幸的是,我的理解只到這一點上了。也就是說,我不知道為什么以及在什么條件下:CFLocaleGetValue(CFLocaleCopyCurrent(),kCFLocaleNationalCode)可以返回NULL。但是它可以,而且這是不做檢查的!因此,用NULL調用CFStringCompare(),應用程序就會崩潰!
蘋果強調:
在某些情況下,如果設備的語言/區域設置不正確,即缺少區域代碼,則可以返回NULL。若要觸發此操作,必須將設備設置為不支持區域的無支持狀態。
兩年多來,她的手機一直無法輸入“臺灣”,或者每當手機收到臺灣旗幟表情符號時,她的手機就會被“遠程攻擊”,只不過是把這個地區從美國、中國大陸,再回到美國,一目了然。
我不能百分之百地確定為什么(或者如何修復它),但我猜它要么將‘Country’值設置為‘us’,所以現在boolan標志(在byte_1b1c9bb00處)被設置為0x1,這意味著CFStringCompare()從來沒有被調用過,或者,對CFLocaleCopyCurrent()/CFLocaleGetValue()的調用不再返回null,這意味著一個有效的字符串被傳遞給了CFStringCompare()。
由于我不確定還有多少其他iOS用戶受到影響,我也向蘋果報告了這個問題。他們給它分配了CVE-2018-4290,并在iOS 11.4.1中對其進行了修復:
我還沒有機會研究蘋果的補丁,但我提出以下建議作為一個簡單的解決方案:
為了避免這種崩潰,代碼應該只檢查調用CFLocaleGetValue()的結果,如果調用失敗(即返回NULL),則跳過對CFStringCompare()的調用:
CFLocaleRef locale = CFLocaleCopyCurrent();
CFStringRef countryCode = CFLocaleGetValue (locale, kCFLocaleCountryCode);
//fix!
// make sure to check this!!
if(NULL != countryCode)
{
CFStringCompare(countryCode, @"CN", 0x0);
}
//otherwise handle case where `countryCode` is NULL
else
{
....
}
到目前為止,本文深入研究揭示并解釋了一個(遠程)iOS崩潰的技術原因。然而,仍然存在一個尚未回答但相當有趣的問題:“不管怎么說,這段代碼到底想要完成什么?”
回顧:
這么多中國!嗯,那又有什么用呢!
答案可以在表情包網站上找到,上面寫著:
此標志隱藏在設置為中國的iOS設備上的表情符號鍵盤上。中國的iPhone不會顯示這個標志,而是會顯示一個缺失的字符豆腐(?)。
在本文中,我們找到了遠程iOS缺陷的原因。
盡管它的影響僅限于拒絕服務(空指針取消引用),但它為分析iOS代碼提供了一個有趣的案例研究。