在翻山越岭之前,本节将针对刚才讲过的理论作一次全面的实战演练,让大家更牢固地掌握所学的知识,以便更平稳地过渡到第6章。本次实战演练的内容是一个真实的示例,它按照5.2节所示的套路,完整地讲述了笔者iOS 6插件“Speaker SBSettings Toggle”(如图5-14所示)的开发过程。当时笔者还不会使用IDA和LLDB,所有的线索几乎都来自class-dump和误打误撞,能够比较好地代表iOS逆向工程初学者的状态。
图5-14 Speaker SBSettings Toggle
注意 下面的具体步骤已不适用于iOS 8,请大家当做案例,了解思路,作为参考就好。
2012年3月底,笔者收到一个伊朗裔加拿大人Shoghian发来的邮件,邮件中分享了一个创意:iOS通话时用户可以从听筒切换到免提,但很少有人知道,接听来电时是可以默认打开免提的,这个功能对那些开车、做饭或工作时双手不方便接电话的人非常有用。但是这么有用的功能却被iOS藏在了“设置”→“通用”→“辅助功能”→“来电使用”的四级目录里(如图5-15所示),设置起来非常繁琐。SBSettings上各种各样的开关就是为解决这类问题而存在的,因此笔者打算把这个功能做成一个toggle,帮这家酒香不怕巷子深的饭店找一个临街的门面,把好的事物呈现在更多人的面前。
图5-15 Incoming Calls界面
因为这个功能位于“设置”中,所以笔者的第一反应自然是在“/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:,可以进入下一环节了。
根据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的内容吻合。
在经过漫长的推理之后,笔者总算得出了一个可能的解决方案,仅需极少代码即可修改配置文件,然后发出一个通知,就这么简单。这个方案可行吗?怀揣一颗惴惴不安又蠢蠢欲动的心,用激动的双手敲出了下面为数不多的几行代码(那时候还不会用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”,成功啦!
程序的核心功能已经验证完毕,写代码就不用费脑子了。按照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下载量