5.3 实例演示

在翻山越岭之前,本节将针对刚才讲过的理论作一次全面的实战演练,让大家更牢固地掌握所学的知识,以便更平稳地过渡到第6章。本次实战演练的内容是一个真实的示例,它按照5.2节所示的套路,完整地讲述了笔者iOS 6插件“Speaker SBSettings Toggle”(如图5-14所示)的开发过程。当时笔者还不会使用IDA和LLDB,所有的线索几乎都来自class-dump和误打误撞,能够比较好地代表iOS逆向工程初学者的状态。

图5-14 Speaker SBSettings Toggle

注意  下面的具体步骤已不适用于iOS 8,请大家当做案例,了解思路,作为参考就好。

5.3.1 得到灵感

2012年3月底,笔者收到一个伊朗裔加拿大人Shoghian发来的邮件,邮件中分享了一个创意:iOS通话时用户可以从听筒切换到免提,但很少有人知道,接听来电时是可以默认打开免提的,这个功能对那些开车、做饭或工作时双手不方便接电话的人非常有用。但是这么有用的功能却被iOS藏在了“设置”→“通用”→“辅助功能”→“来电使用”的四级目录里(如图5-15所示),设置起来非常繁琐。SBSettings上各种各样的开关就是为解决这类问题而存在的,因此笔者打算把这个功能做成一个toggle,帮这家酒香不怕巷子深的饭店找一个临街的门面,把好的事物呈现在更多人的面前。

图5-15 Incoming Calls界面

5.3.2 定位文件

因为这个功能位于“设置”中,所以笔者的第一反应自然是在“/Applications/Preferences.app”和“/System/Library/PreferenceBundles/”里寻找可疑文件,大致步骤如下。

1.将iOS系统语言换成英文

因为iOS的文件系统是全英文的,所以在开始分析之前,笔者先把iOS的系统语言设置成了英文,这样在浏览文件系统时看到的关键词与UI上显示的关键词就更有可能产生对应关系。

2.发现“Accessibility”关键词

切换语言后,“设置”→“通用”→“辅助功能”→“来电使用”翻译成了“Settings”→“General”→“Accessibility”→“Incoming Calls”,其中Accessibility关键词引起了笔者的注意,因为不结合语境的话,Accessibility是不会直译成“辅助功能”的。于是笔者ssh到iOS中,以Accessibility为关键词进行了一次grep操作,如下:


FunMaker-4s:~ root# grep -r Accessibility /
grep: /Applications/Activator.app/Default-568h@2x~iphone.png: No such file or directory
grep: /Applications/Activator.app/Default.png: No such file or directory
grep: /Applications/Activator.app/Default~iphone.png: No such file or directory
grep: /Applications/Activator.app/LaunchImage-700-568h@2x.png: No such file or directory
Binary file /Applications/Activator.app/en.lproj/Localizable.strings matches
grep: /Applications/Activator.app/iOS7-Default-Landscape@2x.png: No such file or directory
grep: /Applications/Activator.app/iOS7-Default-Portrait@2x.png: No such file or directory
Binary file /Applications/AdSheet.app/AdSheet matches
Binary file /Applications/Compass.app/Compass matches
……

得到的结果很多,但最吸引笔者的是下面这几个以strings为后缀的文件:


Binary file /Applications/Preferences.app/English.lproj/General-Simulator.strings matches
Binary file /Applications/Preferences.app/English.lproj/General~iphone.strings matches
Binary file /Applications/Preferences.app/General-Simulator.plist matches
Binary file /Applications/Preferences.app/General.plist matches
Binary file /Applications/Preferences.app/Preferences matches
Binary file /Applications/Preferences.app/en_GB.lproj/General-Simulator.strings matches
Binary file /Applications/Preferences.app/en_GB.lproj/General~iphone.strings matches

如果不出意外,它们是App字符串本地化的配置文件,里面应该含有Accessibility在代码中的符号名。用Xcode自带的plutil工具查看strings文件非常方便,先来看看“/Applications/Preferences.app/English.lproj/General~iphone.strings”,如下:


snakeninnys-MacBook:~ snakeninny$ plutil -p ~/General\~iphone.strings 
{
  "Videos..." => "? Videos..."
  "Wallpaper" => "Wallpaper"
  "TV_OUT" => "TV Out"
  "SOUND_EFFECTS" => "Sound Effects"
  "d_MINUTES" => "%@ Minutes"
……
  "ACCESSIBILITY" => "Accessibility"
  "Multitasking_Gestures" => "Multitasking Gestures"
……
}

由“ACCESSIBILITY”=>“Accessibility”基本可以断定,“ACCESSIBILITY”就是代码中使用的符号名。

3.发现General.plist

有了新的线索后,以大写的ACCESSIBILITY为关键词,又grep了一遍,如下:


FunMaker-4s:~ root# grep -r ACCESSIBILITY /
grep: /Applications/Activator.app/Default-568h@2x~iphone.png: No such file or directory
grep: /Applications/Activator.app/Default.png: No such file or directory
grep: /Applications/Activator.app/Default~iphone.png: No such file or directory
grep: /Applications/Activator.app/LaunchImage-700-568h@2x.png: No such file or directory
grep: /Applications/Activator.app/iOS7-Default-Landscape@2x.png: No such file or directory
grep: /Applications/Activator.app/iOS7-Default-Portrait@2x.png: No such file or directory
Binary file /Applications/Preferences.app/Dutch.lproj/General-Simulator.strings matches
Binary file /Applications/Preferences.app/Dutch.lproj/General~iphone.strings matches
Binary file /Applications/Preferences.app/English.lproj/General-Simulator.strings matches
Binary file /Applications/Preferences.app/English.lproj/General~iphone.strings matches
Binary file /Applications/Preferences.app/French.lproj/General-Simulator.strings matches
Binary file /Applications/Preferences.app/French.lproj/General~iphone.strings matches
Binary file /Applications/Preferences.app/General-Simulator.plist matches
Binary file /Applications/Preferences.app/General.plist matches
Binary file /Applications/Preferences.app/German.lproj/General-Simulator.strings matches
Binary file /Applications/Preferences.app/German.lproj/General~iphone.strings matches
……

得到的结果与刚才grep结果的重合度很高,其中,刚才没有留意的“/Applications/Preferences.app/General.plist”显得格外醒目。在5.2.2节中,特意提到了PreferenceBundle的概念,此处General.plist既是plist格式的文件,又包含关键词,我们看看它里面有什么:


snakeninnys-MacBook:~ snakeninny$ plutil -p ~/General.plist 
{
  "title" => "General"
  "items" => [
    0 => {
      "cell" => "PSGroupCell"
    }
    1 => {
      "detail" => "AboutController"
      "cell" => "PSLinkCell"
      "label" => "About"
    }
    2 => {
      "cell" => "PSLinkCell"
      "id" => "SOFTWARE_UPDATE_LINK"
      "detail" => "SoftwareUpdatePrefController"
      "label" => "SOFTWARE_UPDATE"
      "cellClass" => "PSBadgedTableCell"
    }
        ……
    24 => {
      "detail" => "PSInternationalController"
      "cell" => "PSLinkCell"
      "label" => "INTERNATIONAL"
    }
    25 => {
      "cell" => "PSLinkCell"
      "bundle" => "AccessibilitySettings"
      "label" => "ACCESSIBILITY"
      "requiredCapabilities" => [
        0 => "accessibility"
      ]
      "isController" => 1
    }
    26 => {
      "cell" => "PSGroupCell"
    }
        ……
  ]
}

4.发现AccessibilitySetting.bundle

果不其然,这个文件就是一个标准的preference specifier plist,大写的“ACCESSIB-ILITY”来自25号单元。对比preferences specifier plist格式,将目标锁定在Accessibility-Settings这个bundle中;由AccessibilitySettings的名字,自然地猜测这个bundle可能负责Accessibility下的所有功能。根据5.2.2节中的文件固定位置理论,AccessibilitySettings.bundle一定安安静静地躺在“/System/Library/PreferenceBundles/”中,对将要发生在自己身上的事情浑然不知。

看看“/System/Library/PreferenceBundles/AccessibilitySetting.bundle”里面有什么:


FunMaker-4s:~ root# ls -la /System/Library/PreferenceBundles/Accessibility Settings.bundle 
total 240
drwxr-xr-x 37 root wheel   2414 Mar 10  2013 .
drwxr-xr-x 40 root wheel   1360 Jan 14  2014 ..
-rw-r--r--  1 root wheel   2146 Mar 10  2013 Accessibility.plist
-rwxr-xr-x  1 root wheel 438800 Mar 10  2013 AccessibilitySettings
-rw-r--r--  1 root wheel    238 Dec 22  2012 BluetoothDeviceConfig.plist
-rw-r--r--  1 root wheel    252 Mar 10  2013 BrailleStatusCellSettings.plist
-rw-r--r--  1 root wheel   4484 Dec 22  2012 ColorWellRound@2x.png
-rw-r--r--  1 root wheel    916 Dec 22  2012 ColorWellSquare@2x.png
drwxr-xr-x  2 root wheel    646 Feb  7  2013 Dutch.lproj
drwxr-xr-x  2 root wheel    646 Dec 22  2012 English.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 French.lproj
drwxr-xr-x  2 root wheel    646 Dec 22  2012 German.lproj
-rw-r--r--  1 root wheel    703 Mar 10  2013 GuidedAccessSettings.plist
-rw-r--r--  1 root wheel    807 Mar 10  2013 HandSettings.plist
-rw-r--r--  1 root wheel    652 Mar 10  2013 HearingAidDetailSettings.plist
-rw-r--r--  1 root wheel    507 Mar 10  2013 HearingAidSettings.plist
-rw-r--r--  1 root wheel    383 Dec 22  2012 HomeClickSettings.plist
-rw-r--r--  1 root wheel    447 Dec 22  2012 IconPlay@2x.png
-rw-r--r--  1 root wheel   1113 Dec 22  2012 IconRecord@2x.png
-rw-r--r--  1 root wheel    170 Dec 22  2012 IconStop@2x.png
-rw-r--r--  1 root wheel    907 Mar 10  2013 Info.plist
drwxr-xr-x  2 root wheel    646 Feb  7  2013 Italian.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 Japanese.lproj
-rw-r--r--  1 root wheel    364 Dec 22  2012 LargeFontsSettings.plist
-rw-r--r--  1 root wheel    217 Mar 10  2013 NavigateImagesSettings.plist
-rw-r--r--  1 root wheel   1030 Dec 22  2012 QuickSpeakSettings.plist
-rw-r--r--  1 root wheel    346 Dec 22  2012 RegionNamesNonLocalized.strings
drwxr-xr-x  2 root wheel    646 Feb  7  2013 Spanish.lproj
-rw-r--r--  1 root wheel    394 Dec 22  2012 SpeakerLoad1@2x.png
-rw-r--r--  1 root wheel    622 Mar 10  2013 TripleClickSettings.plist
-rw-r--r--  1 root wheel    467 Dec 22  2012 VoiceOverBrailleOptions.plist
-rw-r--r--  1 root wheel   2477 Mar 10  2013 VoiceOverSettings.plist
-rw-r--r--  1 root wheel    540 Mar 10  2013 VoiceOverTypingFeedback.plist
-rw-r--r--  1 root wheel    480 Dec 22  2012 ZoomSettings.plist
drwxr-xr-x  2 root wheel    102 Dec 22  2012 _CodeSignature
drwxr-xr-x  2 root wheel    646 Feb  7  2013 ar.lproj
-rw-r--r--  1 root wheel   8371 Dec 22  2012 bottombar@2x~iphone.png
-rw-r--r--  1 root wheel   2701 Dec 22  2012 bottombarblue@2x~iphone.png
-rw-r--r--  1 root wheel   2487 Dec 22  2012 bottombarblue_pressed@2x~iphone.png
-rw-r--r--  1 root wheel   2618 Dec 22  2012 bottombarred@2x~iphone.png
-rw-r--r--  1 root wheel   2426 Dec 22  2012 bottombarred_pressed@2x~iphone.png
-rw-r--r--  1 root wheel   2191 Dec 22  2012 bottombarwhite@2x~iphone.png
-rw-r--r--  1 root wheel   2357 Dec 22  2012 bottombarwhite_pressed@2x~iphone.png
drwxr-xr-x  2 root wheel    646 Feb  7  2013 ca.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 cs.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 da.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 el.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 en_GB.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 fi.lproj
-rw-r--r--  1 root wheel    955 Dec 22  2012 hare@2x.png
drwxr-xr-x  2 root wheel    646 Feb  7  2013 he.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 hr.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 hu.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 id.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 ko.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 ms.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 no.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 pl.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 pt.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 pt_PT.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 ro.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 ru.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 sk.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 sv.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 th.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 tr.lproj
-rw-r--r--  1 root wheel    998 Dec 22  2012 turtle@2x.png
drwxr-xr-x  2 root wheel    646 Feb  7  2013 uk.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 vi.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 zh_CN.lproj
drwxr-xr-x  2 root wheel    646 Feb  7  2013 zh_TW.lproj

这里的GuidedAccess、HearingAid和HomeClick等字眼和我们在“Accessibility”中看到的内容吻合(如图5-16所示),它们印证了笔者的猜测。

5.发现“ACCESSIBILITY_DEFAULT_HEADSET”关键词

借助强大的grep,以“Incoming”为关键词搜索一下这个bundle,如下:

图5-16 关键词重合度高


FunMaker-4s:~ root# grep -r Incoming /System/Library/PreferenceBundles/AccessibilitySettings.bundle 
Binary file /System/Library/PreferenceBundles/AccessibilitySettings.bundle/English.lproj/Accessibility~iphone.strings matches
Binary file /System/Library/PreferenceBundles/AccessibilitySettings.bundle/en_GB.lproj/Accessibility~iphone.strings matches

搜索的结果同本小节开始时的场景如出一辙。打开“/System/Library/PreferenceBundles/AccessibilitySettings.bundle/English.lproj/Accessibility~iphone.strings”看看:


snakeninnys-MacBook:~ snakeninny$ plutil -p ~/Accessibility\~iphone.strings 
{
  "HAC_MODE_POWER_REDUCTION_N90" => "Hearing Aid Mode improves performance with some hearing aids, but may reduce cellular reception."
  "LEFT_RIGHT_BALANCE_SPOKEN" => "Left-Right Stereo Balance"
  "QUICKSPEAK_TITLE" => "Speak Selection"
  "LeftStereoBalanceIdentifier" => "L"
  "ACCESSIBILITY_DEFAULT_HEADSET" => "Incoming Calls"
  "HEADSET" => "Headset"
  "CANCEL" => "Cancel"
  "ON" => "On"
  "CUSTOM_VIBRATIONS" => "Custom Vibrations"
  "CONFIRM_INVERT_COLORS_REMOVAL" => "Are you sure you want to disable inverted colors?"
  "SPEAK_AUTOCORRECTIONS" => "Speak Auto-text"
  "DEFAULT_HEADSET_FOOTER" => "Choose route for incoming calls."
  "HEARING_AID_COMPLIANCE_INSTRUCTIONS" => "Improves compatibility with hearing aids in some circumstances. May reduce 2G cellular coverage."
  "DEFAULT_HEADSET" => "Default to headset"
  "ROOT_LEVEL_TITLE" => "Accessibility"
  "HEARING_AID_COMPLIANCE" => "Hearing Aid Mode"
  "CUSTOM_VIBES_INSTRUCTIONS" => "Assign unique vibration patterns to people in Contacts. Change the default pattern for everyone in Sounds settings."
  "VOICEOVERTOUCH_TEXT" => "VoiceOver is for users with
blindness or vision disabilities."
  "IMPORTANT" => "Important"
  "COGNITIVE_HEADING" => "Learning"
  "HAC_MODE_EQUALIZATION_N94" => "Hearing Aid Mode improves audio quality with some hearing aids."
  "SAVE" => "Save"
  "HOME_CLICK_TITLE" => "Home-click Speed"
  "AIR_TOUCH_TITLE" => "AssistiveTouch"
  "CONFIRM_ZOT_REMOVAL" => "Are you sure you want to disable Zoom?"
  "VOICEOVER_TITLE" => "VoiceOver"
  "OFF" => "Off"
  "GUIDED_ACCESS_TITLE" => "Guided Access"
  "ZOOMTOUCH_TEXT" => "Zoom is for users with low-vision acuity."
  "INVERT_COLORS" => "Invert Colors"
  "ACCESSIBILITY_SPEAK_AUTOCORRECTIONS" => "Speak Auto-text"
  "LEFT_RIGHT_BALANCE_DETAILS" => "Adjust the audio volume balance between left and right channels."
  "MONO_AUDIO" => "Mono Audio"
  "CONTRAST" => "Contrast"
  "ZOOM_TITLE" => "Zoom"
  "TRIPLE_CLICK_HEADING" => "Triple-click"
  "OK" => "OK"
  "SPEAKER" => "Speaker"
  "AUTO_CORRECT_TEXT" => "Automatically speak auto-corrections
and auto-capitalizations."
  "HEARING" => "Hearing"
  "LARGE_FONT" => "Large Text"
  "CONFIRM_VOT_USAGE" => "VoiceOver"
  "CONFIRM_VOT_REMOVAL" => "Are you sure you want to disable VoiceOver?"
  "HEARING_AID_TITLE" => "Hearing Aids"
  "FLASH_LED" => "LED Flash for Alerts"
  "VISION" => "Vision"
  "CONFIRM_ZOOM_USAGE" => "Zoom"
  "DEFAULT" => "Default"
  "MOBILITY_HEADING" => "Physical & Motor"
  "TRIPLE_CLICK_TITLE" => "Triple-click Home"
  "RightStereoBalanceIdentifier" => "R"
}

"ACCESSIBILITY_DEFAULT_HEADSET"=>"Incoming Calls"给了我们非常明显的提示,以它为线索继续查找。

6.定位Accessibility.plist


FunMaker-4s:~ root# grep -r ACCESSIBILITY_DEFAULT_HEADSET /System/Library/PreferenceBundles/AccessibilitySettings.bundle 
Binary file /System/Library/PreferenceBundles/AccessibilitySettings.bundle/Accessibility.plist matches
Binary file /System/Library/PreferenceBundles/AccessibilitySettings.bundle/Dutch.lproj/Accessibility~iphone.strings matches
...

除了一个plist文件外,其他都是strings文件,那就是它了。看看它里面有什么:


snakeninnys-MacBook:~ snakeninny$ plutil -p ~/Accessibility.plist 
{
  "title" => "ROOT_LEVEL_TITLE"
  "items" => [
    0 => {
      "label" => "VISION"
      "cell" => "PSGroupCell"
      "footerText" => "AUTO_CORRECT_TEXT"
    }
    1 => {
      "cell" => "PSLinkListCell"
      "label" => "VOICEOVER_TITLE"
      "detail" => "VoiceOverController"
      "get" => "voiceOverTouchEnabled:"
    }
    2 => {
      "cell" => "PSLinkListCell"
      "label" => "ZOOM_TITLE"
      "detail" => "ZoomController"
      "get" => "zoomTouchEnabled:"
    }
    ……
    18 => {
      "cell" => "PSLinkListCell"
      "label" => "HOME_CLICK_TITLE"
      "detail" => "HomeClickController"
      "get" => "homeClickSpeed:"
    }
    19 => {
      "detail" => "PSListItemsController"
      "set" => "accessibilitySetPreference:specifier:"
      "validValues" => [
        0 => 0
        1 => 1
        2 => 2
      ]
      "get" => "accessibilityPreferenceForSpecifier:"
      "validTitles" => [
        0 => "DEFAULT"
        1 => "HEADSET"
        2 => "SPEAKER"
      ]
      "requiredCapabilities" => [
        0 => "telephony"
      ]
      "cell" => "PSLinkListCell"
      "label" => "ACCESSIBILITY_DEFAULT_HEADSET"
      "key" => "DefaultRouteForCall"
    }
  ]
}

又是一个标准的preference specifier plist,而且我们知道了这个配置的setter和getter分别是accessibilitySetPreference:specifier:和accessibilityPreferenceForSpecifier:,可以进入下一环节了。

5.3.3 定位函数

根据preference specifier plist标准,在选择“Incoming Calls”中的某一行时,其setter,即accessibilitySetPreference:specifier:函数得到调用。但问题随之而来,这个函数存在于AccessibilitySettings.bundle里,笔者当时不知道怎么将这个bundle加载进内存,因此没法调用这个函数;也不会用IDA和LLDB,在class-dump的函数里找了又找,仍没有发现任何线索,感觉这个问题的难度已经超出笔者的能力范围,一时解决不了,还沮丧地给Shoghian发了封邮件,如图5-17所示。

图5-17 我和Shoghian之间的交流

这个问题卡了笔者近两个星期,期间笔者一直在想,iOS能在这个函数里干些什么呢?因为preferences specifier plist中提供了PostNotification这一方式来通知别的进程配置文件发生了变动,而AccessibilitySettings的配置与电话相关,正好也是进程间通信的模式,那么,accessibilitySetPreference:specifier:的作用会不会是改动配置文件,然后发出一个通知?于是笔者利用limneos开发的LibNotifyWatch,在手动改变“来电使用”配置时观察系统中是否出现了相关的通知,没想到,还真让笔者歪打正着了,如下:


FunMaker-4s:~ root# grep LibNotifyWatch: /var/log/syslog
Nov 26 00:09:20 FunMaker-4s Preferences[6488]: LibNotifyWatch: <CFNotification Center 0x1e875600 [0x39b4b100]> postNotificationName:UIViewAnimationDidCommitNotification object:UIViewAnimationState userInfo:{
Nov 26 00:09:20 FunMaker-4s Preferences[6488]: LibNotifyWatch: <CFNotificationCenter 0x1e875600 [0x39b4b100]> postNotificationName:UIViewAnimationDidStopNotification object:<UIViewAnimationState: 0x1ea74f20> userInfo:{
……
Nov 26 00:09:21 FunMaker-4s Preferences[6488]: LibNotifyWatch: CFNotificationCenterPostNotification center=<CFNotificationCenter 0x1dd86bd0 [0x39b4b100]> name=com.apple.accessibility.defaultrouteforcall userInfo=(null) deliverImmediately=1
Nov 26 00:09:21 FunMaker-4s Preferences[6488]: LibNotifyWatch: notify_post com.apple.accessibility.defaultrouteforcall 
……

笔者发现了2条名为“com.apple.accessibility.defaultrouteforcall”的通知!结合前面的一系列推导,想来没有必要再多作解释了。发现了最可疑的通知后,面对的就是另一个同样重要的问题:配置文件在哪里?

第2章说过,“/var/mobile/”中存放了大量用户数据。“/var/mobile/Containers/”中全是App相关数据,“/var/mobile/Media/”中全是媒体文件,而在“/var/mobile/Library/”中稍加浏览就很容易发现“/var/mobile/Library/Preferences/”目录,进而找到“com.apple.Accessibility.plist”,其内容如下:


snakeninnys-MacBook:~ snakeninny$ plutil -p ~/com.apple.Accessibility.plist 
{
      ……
  "DefaultRouteForCallPreference" => 2
  "VOTQuickNavEnabled" => 1
  "CurrentRotorTypeWeb" => 3
  "PunctuationKey" => 2
        ……
  "ScreenCurtain" => 0
  "VoiceOverTouchEnabled" => 0
  "AssistiveTouchEnabled" => 0
}

在iOS中改变“来电使用”的配置,观察DefaultRouteForCallPreference值的变化规律,很容易得出结论:0对应default,1对应headset,2对应speaker,与Accessibility.plist的内容吻合。

5.3.4 测试函数

在经过漫长的推理之后,笔者总算得出了一个可能的解决方案,仅需极少代码即可修改配置文件,然后发出一个通知,就这么简单。这个方案可行吗?怀揣一颗惴惴不安又蠢蠢欲动的心,用激动的双手敲出了下面为数不多的几行代码(那时候还不会用Cycript,所以用tweak测试):


%hook SpringBoard 
- (void)menuButtonDown:(id)down
{
    %orig; 
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary WithContents OfFile:@"/var/mobile/Library/Preferences/com.apple. Accessibility.plist"]; 
    [dictionary setObject:[NSNumber numberWithInt:2] forKey:@"DefaultRouteForCallPreference"]; 
   [dictionary writeToFile:@"/var/mobile/Library/Preferences/com.apple. Accessibility. plist" atomically:YES]; 
    notify_post("com.apple.accessibility.defaultrouteforcall");
} 
%end

编译、运行、安装、respring,闭着眼睛按下home键,然后伴着极快的心跳依次打开“Settings”→“General”→“Accessibility”→“Incoming Calls”——已选项变成了“Speaker”,成功啦!

5.3.5 编写实例代码

程序的核心功能已经验证完毕,写代码就不用费脑子了。按照SBSettings toggle的编写规范完成代码(http://thebigboss.org/guides-iphone-ipod-ipad/sbsettings-toggle-spec ),完整代码如下:


#import <notify.h> 
#define ACCESSBILITY @"/var/mobile/Library/Preferences/com.apple.Accessibility. plist" 
// Required
extern "C" BOOL isCapable() { 
      if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_5_0 && [[[UIDevice currentDevice] model] isEqualToString:@"iPhone"]) 
            return YES;
      return NO;
}
// Required
extern "C" BOOL isEnabled() { 
      NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCont entsOfFile:ACCESSBILITY]; 
      BOOL result = [[dictionary objectForKey:@"DefaultRouteForCallPreference"] intValue] == 0 ? NO : YES; 
      [dictionary release]; 
      return result;
} 
// Optional
// Faster isEnabled. Remove this if it's not necessary. Keep it if isEnabled() is expensive and you can make it faster here.
extern "C" BOOL getStateFast() { 
      return isEnabled();
} 
// Required
extern "C" void setState(BOOL enabled) { 
      NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCont entsOfFile:ACCESSBILITY]; 
      [dictionary setObject:[NSNumber numberWithInt:(enabled ? 2 : 0)] forKey:@"D efaultRouteForCallPreference"]; 
      [dictionary writeToFile:ACCESSBILITY atomically:YES]; [dictionary release]; 
      notify_post("com.apple.accessibility.defaultrouteforcall");
} 
// Required
// How long the toggle takes to toggle, in seconds.
extern "C" float getDelayTime() { 
      return 0.6f;
}

因为程序的创意来自Shoghian,所以笔者在发布这个程序时也标注了他的名字(如图5-18所示)。他很高兴,我们还成了朋友,偶尔也天南地北地扯上一会儿。Speaker SBSettings Toggle是笔者发布在Cydia上的第三个程序,虽然功能简单,也没有作什么宣传,但还是累积了近10000的下载量(如图5-19所示),对此笔者已经很满意了。更重要的是,这个tweak的制作历经坎坷,看似简单的功能却让笔者花费了九牛二虎之力,无疑给了当时刚刚上路,有些轻飘飘的笔者当头一棒!类似的情况出现过若干次后,笔者才意识到仅仅使用class-dump来做逆向工程是不靠谱的,也间接促使笔者下定决心学习IDA和LLDB,从而迈入了iOS逆向工程的新阶段。

图5-18 第二作者是Shoghian

图5-19 积累了近10000下载量