上次介绍了一个如何用 Xcode 来构建 Substrate 插件,但是开发的具体过程还没有涉及,而这往往又正是初学者最难下手的地方,所以有了本文的后续。
不过在开始之前你要先做好思想准备,相比较开发一般的 App,开发插件的过程需要大量的探索、尝试,因为未知但又必须知道的东西很多很多,于是有时候运气甚至比技术更重要。
Hook?Substrate?插件?简明释义!
Hook 就是通过某种手段替换掉某个类或者对象的方法的实现,从而达到运行时注入代码的目的。
MobileSubstrate 则为开发者提供了一个方便快捷安全可靠的代码注入开发框架。
插件(这里当然是说 Substrate 插件了)就是指基于这个平台开发的动态库。
获取头文件
Class Dump 是一个用来研究 Objective-C 程序运行时的工具,它可以提取 Mach-O 文件中的类、类别、协议的声明文件。拿到了这些头文件,我们才能知道代码要注入到哪个类,哪个方法。
Class Dump 可以从 Cydia 中获得。
典型用法举例,将 SpringBoard 的头文件输出到 Headers 文件夹:
class-dump -H -o Headers SpringBoard
无参数运行 class-dump 可以查询可用参数及其说明。
又一个例子:SpringBoard.h
//SpringBoard.h #import "_ABAddressBookCopyLocalizedLabel.h" #import "UIApplicationDelegate-Protocol.h" @class NSDate, NSDictionary, NSMutableArray, NSMutableSet, NSNumberFormatter, NSSet, NSString, NSTimer, NSURL, SBAppContextHostManager, SBApplication, SBDimmingWindow, SBUIController, UIWindow; @interface SpringBoard : _ABAddressBookCopyLocalizedLabel { SBUIController *_uiController; NSTimer *_menuButtonTimer; NSTimer *_lockButtonTimer; NSTimer *_idleTimer; NSTimer *_autoLockTimer; double _lastUndimEventTime; double _lastTimeIdleCausedDim; double _headsetButtonDownTime; struct __GSEvent *_headsetDownEvent; int _headsetClickCount; SBDimmingWindow *_simulatedBlankingWindow; unsigned int _headsetButtonClickCount:8; unsigned int _menuButtonClickCount:8; unsigned int _screenWasDimOnMenuDown:1; unsigned int _waitingForMenuDoubleTapAfterActingOnSingleTap:1; unsigned int _screenshotWasTaken:1; unsigned int _disableAutoDimming:1; unsigned int _dontLockOnNextLockUp:1; unsigned int _poweringDown:1; unsigned int _headsetDownDelayedActionPerformed:1; unsigned int _isSeekingInMedia:1; unsigned int _forcePortraitStatusBarOrientation:1; unsigned int _lockScreenCameraWantsIdleTimerDisabled:1; int _mediaSeekDirection; float _currentBacklightLevel; unsigned int _springBoardRequestsAccelerometerEvents; int _activeInterfaceOrientation; NSURL *_menuDoubleTapURL; int _notifyDontAnimateREOToken; int _notifyDontAllowMediaHUDToken; BOOL _expectsFaceContact; BOOL _expectsFaceContactInLandscape; BOOL _proximityEventsEnabled; NSSet *_restrictionDisabledApplications; double _sampleSystemStartTime; NSDictionary *_startAppsCPUTimes; struct __CFDictionary *_registeredSimpleRemoteAppToPriority; SBApplication *_registeredSimpleRemoteApp; SBApplication *_nowPlayingApp; SBApplication *_menuButtonInterceptApp; BOOL _menuButtonInterceptAppEnabledForever; NSString *_originatingOpenURLDisplayId; NSMutableArray *_disableNowPlayingHUDAssertionBundleIds; NSMutableArray *_appsRegisteredForVolumeEvents; NSNumberFormatter *_decimalFormatter; NSNumberFormatter *_percentFormatter; NSTimer *_midnightTimer; NSDate *_midnightFireDate; struct _opaque_pthread_t *_backgroundMIGServerThread; struct _opaque_pthread_t *_iconGenerationMIGServerThread; SBAppContextHostManager *_springBoardContextHostManager; UIWindow *_springBoardContextHostWindow; NSMutableSet *_displaysRequestingAggressiveJetsamMode; } + (BOOL)registerForSystemEvents; + (BOOL)rendersLocally; - (id)init; - (void)_createLogFile; - (void)writeLogFile; - (void)handleKeyEvent:(struct __GSEvent *)fp8; - (void)setHardwareKeyboardLayoutName:(id)fp8; - (void)updateStackshotSettings; - (void)updatePowerlog; - (void)_performDeferredLaunchWork; - (void)applicationDidFinishLaunching:(id)fp8; - (void)appleIconViewRemoved; - (BOOL)launchedAfterLanguageRestart; - (void)clearLaunchedAfterLanguageRestart; - (id)_settingLanguageStringForNewLanguage; - (void)languageChanged; - (void)_rotateView:(id)fp8 toOrientation:(int)fp12; - (void)wipeDeviceNow; - (void)checkPasscodeCompliance; - (void)showEDGEActivationFailureAlert:(id)fp8 reason:(id)fp12 forMMS:(BOOL)fp16; - (void)_effectiveSettingsDidChange; - (void)_assistantPreferenceDidChange:(id)fp8; - (void)_profileListDidChange; - (void)userDefaultsDidChange:(id)fp8; - (void)_lockdownActivationChanged:(id)fp8; - (void)_testPhoneAlerts; - (void)runFieldTestScript; - (void)_significantTimeChange; - (void)significantTimeChange; - (void)batteryStatusDidChange:(id)fp8; - (BOOL)shouldRunFieldTestScript; - (BOOL)iapIsInExtendedMode; - (BOOL)canShowLockScreenHUDControls; - (BOOL)lockScreenCameraSupported; - (BOOL)canShowLockScreenCameraKnob; - (BOOL)canShowNowPlayingControls; - (void)setAppDisabledNowPlayingHUD:(BOOL)fp8 bundleIdentifier:(id)fp12; - (BOOL)respondImmediatelyToMenuSingleTapAllowingDoubleTap:(char *)fp8; - (void)goToSpotlight:(BOOL)fp8; - (BOOL)handleDoubleTapAction; - (void)handleMenuDoubleTap; - (void)_primeMenuButtonAssistant; - (void)_setMenuButtonTimer:(id)fp8; - (void)_setLockButtonTimer:(id)fp8; - (void)cancelMenuButtonRequests; - (void)clearMenuButtonTimer; - (void)_menuButtonWasHeld; - (double)_menuHoldTime; - (void)menuButtonDown:(struct __GSEvent *)fp8; - (void)menuButtonUp:(struct __GSEvent *)fp8; - (void)_giveUpOnMenuDoubleTap; - (void)_keyboardAvailabilityChanged; - (void)_runActivateAssistantTest; - (void)_activateAssistantWithEvent:(int)fp8 withCompletion:(id)fp(null); - (void)activateAssistantWithOptions:(id)fp8 withCompletion:(id)fp(null); - (void)_startSeekWithDirection:(id)fp8; - (void)mediaKeyDown:(struct __GSEvent *)fp8; - (void)mediaKeyUp:(struct __GSEvent *)fp8; - (void)_handleMenuButtonEvent; - (void)lockButtonDown:(struct __GSEvent *)fp8; - (void)lockButtonWasHeld; - (void)extendButtonTimersForWake; - (void)_powerDownNow; - (void)_rebootNow; - (void)reboot; - (void)powerDown; - (BOOL)isPoweringDown; - (void)powerDownRequested:(id)fp8; - (void)powerDownCanceled:(id)fp8; - (BOOL)relaunchingForSetupLanguageChange; - (void)relaunchSpringBoard; - (void)_relaunchSpringBoardNow; - (void)lockButtonUp:(struct __GSEvent *)fp8; - (void)_performDelayedHeadsetActionForAssistant; - (void)_performDelayedHeadsetActionForVoiceControl; - (void)_performDelayedHeadsetClickTimeout; - (id)simpleRemoteDestinationApp; - (void)sendSimpleRemoteActionToRegisteredApp:(int)fp8; - (void)_iapServerConnectionDiedNotification:(id)fp8; - (void)_iapExtendedModeReset; - (void)_imagesMounted; - (void)_setDeferredHeadsetButtonDownEvent:(struct __GSEvent *)fp8; - (void)headsetButtonDown:(struct __GSEvent *)fp8; - (void)headsetButtonUp:(struct __GSEvent *)fp8; - (void)headsetAvailabilityChanged:(struct __GSEvent *)fp8; - (void)smsPrefsChanged; - (void)ALSPrefsChanged:(id)fp8; - (void)profileConnectionDidReceiveEffectiveSettingsChangedNotification:(id)fp8 userInfo:(id)fp12; - (void)updateCapabilitiesAndIconVisibility; - (BOOL)isDisplayIdentifierRestrictionDisabled:(id)fp8; - (void)loadDebuggingAndDemoPrefs; - (void)debuggingAndDemoPrefsWereChanged; - (void)_localeChanged; - (void)localeChanged; - (void)autoLockPrefsChanged; - (void)pinPolicyChanged; - (void)profileConnectionDidReceiveRestrictionChangedNotification:(id)fp8 userInfo:(id)fp12; - (void)profileConnectionDidReceivePasscodePolicyChangedNotification:(id)fp8 userInfo:(id)fp12; - (void)ringerChanged:(int)fp8; - (void)_updateRingerStateWithVisuals:(BOOL)fp8 updatePreferenceRegister:(BOOL)fp12; - (void)accessoryKeyStateChanged:(struct __GSEvent *)fp8; - (unsigned int)_frontmostApplicationPort; - (void)quitTopApplication:(struct __GSEvent *)fp8; - (void)applicationExited:(struct __GSEvent *)fp8; - (void)anotherApplicationFinishedLaunching:(struct __GSEvent *)fp8; - (void)applicationSuspend:(struct __GSEvent *)fp8; - (void)applicationSuspended:(struct __GSEvent *)fp8; - (void)applicationSuspendedSettingsUpdated:(struct __GSEvent *)fp8; - (void)statusBarReturnActionTap:(struct __GSEvent *)fp8; - (int)statusBar:(id)fp8 styleForRequestedStyle:(int)fp12 overrides:(int)fp16; - (void)hideSpringBoardStatusBar; - (void)showSpringBoardStatusBar; - (void)setMetaHostingEnabled:(BOOL)fp8; - (BOOL)isMetaHostingEnabled; - (id)metaHostView; - (id)metaHostWindow; - (void)showAlertForUnhandledURL:(id)fp8 error:(int)fp12; - (void)_applicationOpenURL:(id)fp8 event:(struct __GSEvent *)fp12; - (BOOL)applicationCanOpenURL:(id)fp8 publicURLsOnly:(BOOL)fp12; - (void)applicationOpenURL:(id)fp8; - (void)applicationOpenURL:(id)fp8 publicURLsOnly:(BOOL)fp12; - (void)applicationOpenURL:(id)fp8 publicURLsOnly:(BOOL)fp12 animating:(BOOL)fp16; - (void)applicationOpenURL:(id)fp8 publicURLsOnly:(BOOL)fp12 animating:(BOOL)fp16 sender:(id)fp20; - (void)applicationOpenURL:(id)fp8 publicURLsOnly:(BOOL)fp12 animating:(BOOL)fp16 additionalActivationFlag:(unsigned int)fp20; - (void)applicationOpenURL:(id)fp8 publicURLsOnly:(BOOL)fp12 animating:(BOOL)fp16 sender:(id)fp20 additionalActivationFlag:(unsigned int)fp24; - (void)_openURLCore:(id)fp8 display:(id)fp12 publicURLsOnly:(BOOL)fp16 animating:(BOOL)fp20 additionalActivationFlag:(unsigned int)fp24; - (BOOL)canOpenURL:(id)fp8; - (BOOL)openURL:(id)fp8; - (void)setMenuButtonInterceptApp:(id)fp8 forever:(BOOL)fp12; - (id)menuButtonInterceptApp; - (BOOL)menuButtonInterceptAppEnabledForever; - (void)setWantsVolumeButtonEvents:(BOOL)fp8; - (void)setAppRegisteredForVolumeEvents:(id)fp8 isActive:(BOOL)fp12; - (id)appsRegisteredForVolumeEvents; - (void)volumeChanged:(struct __GSEvent *)fp8; - (void)setBacklightFactorPending:(float)fp8; - (void)setBacklightFactor:(float)fp8 keepTouchOn:(BOOL)fp12; - (void)setBacklightFactor:(float)fp8; - (void)animateBacklightToFactor:(float)fp8 duration:(double)fp12 keepTouchOn:(BOOL)fp20 didFinishTarget:(id)fp24 selector:(SEL)fp28; - (void)animateBacklightToFactor:(float)fp8 duration:(double)fp12 didFinishTarget:(id)fp20 selector:(SEL)fp24; - (void)setBacklightLevel:(float)fp8; - (void)setBacklightLevel:(float)fp8 permanently:(BOOL)fp12; - (float)currentBacklightLevel; - (float)systemBacklightLevel; - (void)systemWillSleep; - (void)setupMidnightTimer; - (void)_midnightPassed; - (void)_adjustMidnightTimerAfterSleep; - (void)setBacklightFactorToZero; - (void)cancelSetBacklightFactorToZeroAfterDelay; - (void)setBacklightFactorToZeroAfterDelay; - (void)showSimulatedScreenBlank; - (void)hideSimulatedScreenBlank; - (void)dimToBlackKeepingTouchOn; - (void)undim; - (void)lockAfterCall; - (BOOL)shouldDimToBlackInsteadOfLock; - (void)autoLock; - (void)didIdle; - (double)nextIdleTimeDuration; - (double)nextLockTimeDuration; - (void)clearIdleTimer; - (void)resetIdleTimerAndUndim; - (void)resetIdleTimerAndUndim:(BOOL)fp8; - (void)_proximityChanged:(id)fp8; - (BOOL)caseIsEnabledAndLatched; - (BOOL)allowCaseLatchLockAndUnlock; - (void)keyboardOrCaseLatchWantsToAttemptUnlock:(id)fp8; - (void)noteCaseHardwarePresent; - (void)caseLatchWantsToAttemptLock; - (void)userEventOccurred; - (void)_updateRejectedInputSettingsForInCallState:(BOOL)fp8 isOutgoing:(BOOL)fp12 triggeredbyRouteWillChangeToReceiverNotification:(BOOL)fp16; - (void)updateRejectedInputSettingsForInCallState:(BOOL)fp8 isOutgoing:(BOOL)fp12; - (void)updateRejectedInputSettings; - (void)updateRejectedInputSettingsTriggeredByRouteChangeToReceiverNotification:(BOOL)fp8; - (void)lockDevice:(struct __GSEvent *)fp8; - (void)showThermalAlertIfNecessary; - (void)respondToCurrentThermalCondition; - (void)_beginThermalJetsamCPUSampling; - (void)_killThermallyActiveApplication; - (id)_newAppsCPUTimesDictionary; - (void)didReceiveMemoryWarning; - (void)noteSubstantialTransitionOccured; - (void)updateMirroredDisplayOrientation; - (void)noteInterfaceOrientationChanged:(int)fp8; - (void)noteInterfaceOrientationChanged:(int)fp8 updateMirroredDisplays:(BOOL)fp12; - (int)activeInterfaceOrientation; - (int)activeInterfaceOrientationWithoutConsideringAlerts; - (int)_frontMostAppOrientation; - (int)interfaceOrientationForCurrentDeviceOrientation; - (void)reportStatusBarOrientationAsPortrait:(BOOL)fp8; - (int)statusBarOrientation; - (void)_overrideDefaultInterfaceOrientationWithOrientation:(int)fp8; - (void)_removeDefaultInterfaceOrientatationOverride; - (id)displayIDForURLScheme:(id)fp8 isPublic:(BOOL)fp12; - (BOOL)_alertWindowShouldRotate; - (double)windowRotationDuration; - (void)setSystemVolumeHUDEnabled:(BOOL)fp8 forAudioCategory:(id)fp12; - (void)_migrateMenuDoubleTapSetting; - (void)updateMenuDoubleTapSettings; - (void)setZoomTouchEnabled:(BOOL)fp8; - (BOOL)proximityEventsEnabled; - (void)setProximityEventsEnabled:(BOOL)fp8; - (BOOL)expectsFaceContact; - (BOOL)expectsFaceContactInLandscape; - (void)setExpectsFaceContact:(BOOL)fp8 inLandscape:(BOOL)fp12; - (void)setExpectsFaceContact:(BOOL)fp8; - (void)updateProximitySettings; - (void)frontDisplayDidChange; - (void)applicationWillOrderInContext:(id)fp8 windowLevel:(float)fp12 windowOutput:(int)fp16; - (void)applicationDidOrderOutContext:(id)fp8; - (void)didDismissMiniAlert; - (void)willDisplayMiniAlert:(int *)fp8; - (void)willDismissMiniAlert:(int *)fp8 andShowAnother:(BOOL)fp12; - (void)setHasMiniAlerts:(BOOL)fp8; - (BOOL)canShowAlerts; - (BOOL)isLocked; - (int)alertInterfaceOrientation; - (void)launchMusicPlayerSuspended; - (void)_launchMusicPlayerSuspendedAndStartMusic; - (void)_tearDownNow; - (void)tearDown; - (void)_nowPlayingAppDidChangeNotification:(id)fp8; - (BOOL)isMusicPlayerInNowPlayingView; - (id)nowPlayingApp; - (BOOL)isNowPlayingAppPlaying; - (BOOL)isMusicPlayerPlaying; - (void)setNowPlayingInfo:(id)fp8 forApplication:(id)fp12; - (void)_updateRegisteredSimpleRemoteApp; - (void)setSimpleRemoteRoutingPriority:(unsigned int)fp8 forApplication:(id)fp12; - (unsigned int)simpleRemoteRoutingPriorityForApplication:(id)fp8; - (unsigned int)simpleRemoteRoutingPriority; - (void)setIdleTimerDisabled:(BOOL)fp8; - (void)setSuspensionAnimationDelay:(double)fp8; - (BOOL)isCameraApp; - (id)formattedDecimalStringForNumber:(id)fp8; - (id)formattedPercentStringForNumber:(id)fp8; - (id)_accessibilityFrontMostApplication; - (id)_accessibilityTopDisplay; - (id)_accessibilityRunningApplications; - (BOOL)_accessibilityIsSystemGestureActive; - (BOOL)_accessibilityObjectWithinProximity; - (void)_accessibilitySetEventTapCallback:(void *)fp8; - (void *)_accessibilityEventTapCallback; - (void)_accessibilityProcessHIDEvent:(struct __IOHIDEvent *)fp8; - (BOOL)_accessibilityIsSBStealingEvents; - (double)_accessibilityActivationAnimationStartDelay; - (void)_accessibilityActivationAnimationWillBegin; - (double)_accessibilityDeactivationAnimationStartDelay; - (void)_accessibilityDeactivationAnimationWillBegin; - (BOOL)_isSwitcherShowing; - (void)_setStatusBarShowsProgress:(BOOL)fp8; - (void)_spokenLanguageChanged; - (void)beginListeningForAssistantActivationGesture; - (void)stopListeningForAssistantActivationGesture; - (void)setSystemAggressiveJetsamEnabled:(BOOL)fp8 forDisplay:(id)fp12; @end
这样我们就知道了 SpringBoard 中有一个 SpringBoard 类,以及这个类中声明的所有方法的名称、返回类型、参数类型和数量。然后就可以按照之前介绍的方法修改你想要修改的方法的实现了。
但为了实现既定目标到底该修改哪个方法的实现,又该如何修改,这就是需要不断尝试和探索的地方了。
冒险才刚刚开始
方法名称提供了很多的信息,让我们知道这个方法大概和什么有关,但是参数和返回值的意义就不太清晰了。这里给个小提示,可以通过注入 NSLog 输出参数和返回值来获取更多信息。
总之,想方设法获取需要的信息,而直接按照常理猜测原始实现的各种可能性来试验也不失为一个好方法。
最后的最后,再啰嗦一句,万一有同学不知道的话。编译好的动态库要放到 iOS 设备的这个目录:
/Library/MobileSubstrate/DynamicLibraries