-
-
[原创]【iOS逆向与安全】编写一个使应用保持前台运行的系统插件
-
发表于: 2023-8-28 12:31 4609
-
前言
iOS越狱为用户打开了无限的可能性,其中之一是便是开发系统级插件,为了确保应用程序一直保持在前台,即使在意外情况下也是如此。
本文将向您展示如何轻松编写这样的插件,让我们开始探索iOS系统插件的世界吧!
一、目标
学会创建功能强大的iOS系统插件。
二、开发环境和工具清单
- mac系统
- frida:动态调试
- 已越狱iOS设备(iOS12.5.5):动态调试及插件测试
三、步骤
1、定位关键函数
SpringBoard是iOS系统非常重要的一个系统应用程序,它为用户提供了访问和管理应用程序的主要方式,同时也是用户与设备交互的一个重要界面。咱们今天的调试目标就是它。
使用frida-trace动态调试地,如何给定关键词来缩小函数范围,我在这有几点经验:
根据类的前缀来减少不相关的类:
如果你要跟踪的库为CoreFoundation.framework。那对应的trace为
frida-trace -UF -m "*[CF* *]"
同样,如果你要跟踪的库为AVFoundation.framework。那对应的trace为
frida-trace -UF -m "*[AV* *]"
这种命名约定的背后是,iOS系统库中的类名通常采用库名的首字母缩写加上具体类名的形式,以确保类名的唯一性和可读性。例如,MapKit.framework中的类MKMapView.h、CoreLocation.framework中的类CLLocationManager.h、以及AVFoundation.framework中的类AVCaptureSession.h都遵循了这一约定。
为什么UIKit.framework里的是UILabel.h,而不是UIKLabel.h呢?因为这货出现得太早了,当时的命名规范还未形成。
为什么Foundation.framework里的是NSString.h,而不是FString.h呢?这货也一样,太早了,命名不规范。NS的全称是NeXTStep框架,也就是Objective-c语言早期的实现之一,更多信息请google。
根据你的目标来限定关键词,如你想要跟踪的功能和定位有关系。那你可以尝试trace
"*[*Location* *]"
或"*[* *Location*]"
。或者你要跟踪的和发送消息有关系。那你可以尝试trace"*[* *sendMsg*]"
或"*[* *sendMsg]"
。注:在这需要注意字母的大小写。比如Location,这个L有可能是写,这种情况你可以这样trace"*[* ocation*]"
,不写这个L就好了嘛。
那试想一下。我们今天的插件是不是和应用有关系,那它的关键词是不是就应该是Application或App。还有状态有关系。应用的切换,就在在各种状态之前变化,那这关键词是不是就是State或state。根据经验的第一条。咱们目标应用SpringBoard,那类名的前缀是不是应该是SB。
连上你的手机,并启动任意程序。在终端执行frida-trace -U -m "*[SB* *]" -o a.log SpringBoard
,然而,以SB开头的类太多了,这命令trace了3万多个函数,范围太广,手机直接卡死了......
重新越狱调整命令为frida-trace -U -m "*[SB*pp* *]" -o a.log SpringBoard
,将应用切换到后台,获取到关键日志如下:
1 2 3 4 5 6 7 | ...... ...... ...... ...... ...... ...... 仍然太多了,你也可以一点点分析,看看哪些函数可疑,然后再跟踪。 |
再调试trace命令,加上statefrida-trace -U -m "*[SB*pp* *tate*]" -o a.log SpringBoard
,将应用切换到后台,获取到的部分关键日志如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | - [SBMainDisplayWorkspaceAppInteractionEventSource layoutStateTransitionCoordinator: 0x283547690 transitionDidEndWithTransitionContext: 0x28275c900 ] - [SBAppToAppWorkspaceTransaction shouldPerformToAppStateCleanupOnCompletion] - [SBToAppsWorkspaceTransaction performToAppStateCleanup] - [SBWorkspaceApplicationSceneTransitionContext layoutState] - [SBWorkspaceApplicationSceneTransitionContext previousLayoutState] - [SBApplication _noteProcess: 0x109b51300 didChangeToState: 0x283be0880 ] - [SBApplication _updateProcess:<FBApplicationProcess: 0x109b51300 ; Cydia (com.saurik.Cydia); pid: 20212 > withState:<FBProcessState: 0x283be0880 ; pid: 20212 ; taskState: Running; visibility: Background>] - [SBApplication _internalProcessState] - [SBApplicationProcessState taskState] - [SBApplication _internalProcessState] - [SBApplicationProcessState _initWithProcess: 0x109b51300 stateSnapshot: 0x283be0880 ] - [SBApplication _setInternalProcessState: 0x283bf8f40 ] - [SBApplicationProcessState taskState] - [SBApplicationProcessState taskState] - [SBApplicationAutoLaunchService _applicationProcessStateDidChange: 0x2835676f0 ] - [SBApplication processState] - [SBApplicationProcessState taskState] - [SBApplication _noteProcess: 0x109b51300 didChangeToState: 0x283b07b20 ] - [SBApplication _updateProcess:<FBApplicationProcess: 0x109b51300 ; Cydia (com.saurik.Cydia); pid: 20212 > withState:<FBProcessState: 0x283b07b20 ; pid: 20212 ; taskState: Suspended; visibility: Background>] - [SBApplication _internalProcessState] - [SBApplicationProcessState taskState] - [SBApplication _internalProcessState] - [SBApplicationProcessState _initWithProcess: 0x109b51300 stateSnapshot: 0x283b07b20 ] - [SBApplication _setInternalProcessState: 0x283b98ea0 ] - [SBApplicationProcessState taskState] - [SBApplicationProcessState taskState] - [SBApplicationAutoLaunchService _applicationProcessStateDidChange: 0x283544690 ] - [SBApplication processState] - [SBApplication _internalProcessState] |
从以上信息我们提取出关键类为SBApplication
,可疑函数-[SBApplication _noteProcess:0x109b51300 didChangeToState:0x283b07b20]
,跟踪该类frida-trace -U -m "-[SBApplication _noteProcess:didChangeToState:]" -o a.log SpringBoard
,js代码如下:
1 2 3 4 5 6 7 | { onEnter(log, args, state) { log(`-[SBApplication _noteProcess:${ObjC.Object(args[2])} didChangeToState:${ObjC.Object(args[3])}]`); }, onLeave(log, retval, state) { } } |
应用切到后台,获取到日志如下:
1 2 | - [SBApplication _noteProcess:<FBApplicationProcess: 0x113b1e540 ; Cydia (com.saurik.Cydia); pid: 20869 > didChangeToState:<FBProcessState: 0x283bd71e0 ; pid: 20869 ; taskState: Running; visibility: Background>] - [SBApplication _noteProcess:<FBApplicationProcess: 0x113b1e540 ; Cydia (com.saurik.Cydia); pid: 20869 > didChangeToState:<FBProcessState: 0x283b82660 ; pid: 20869 ; taskState: Suspended; visibility: Background>] |
根据日志信息,可以看出。第一个参数里包含了我们当前的应用信息。第二个参数则是应用的状态。Running、Suspended、Not Running。
至此,目标函数已找到。
注:目标函数不止这一个,只要你通过你自己的关键词定位,尝试,也许是找到的其他函数。只要应用的状态正确且唯一,都可以使用。比如我之前调试过程中找到的函数是[SBMainWorkspace process:stateDidChangeFromState:toState:]
,也是可以用的。
2、编写插件代码
使用Xcode的MonkeyDev插件的Logos Tweak来创建插件工程:
应用状态hook代码如下:
%hook SBMainWorkspace -(void)process:(id)arg1 stateDidChangeFromState:(id)arg2 toState:(id)arg3{ %orig; // arg1的类型为:FBApplicationProcess,获取包名的方法名为bundleIdentifier // arg3的类型为:FBProcessState,获取到状态的方法名为taskState // 在这查找类对应的头文件:0acK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1k6i4k6W2L8r3!0H3k6i4u0Q4x3X3g2D9K9h3#2F1k6h3!0K6i4K6u0W2L8X3g2@1i4K6u0r3K9h3&6V1k6i4S2Q4x3X3g2H3K9s2l9`. NSString *bundleID = [arg1 valueForKey:@"bundleIdentifier"]; BOOL isValid = [AppAngel validBundleID:bundleID]; int toState = [[arg3 valueForKey:@"taskState"] intValue]; // 调试可得:2运行,3后台,1杀死 NSLog(@"witwit =%@=%@=", bundleID, arg3); NSLog(@"witwit hook=%d=%d=", isValid, toState); if (isValid { if (toState == 3) { [AppAngel launchApp:bundleID]; } else if (toState == 1) { [AppAngel performSelector:@selector(launchApp:) withObject:bundleID afterDelay:1]; } } } %end
由于插件开启后,目标应用无法切换到后台,这时你想关闭插件咋办?于是有了关闭快捷键音量-或电源键,相关hook代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | % hook VolumeControl - (void)increaseVolume { NSTimeInterval nowtime = [[NSDate date] timeIntervalSince1970]; NSLog(@ "witwit iosother increaseVolume" ); if (nowtime - g_volume_up_time < 1 ) { g_volume_up_count + = 1 ; if (g_volume_up_count > = 2 ) { g_volume_up_count = 0 ; [AppAngel enablePlugins:YES]; } } else { g_volume_up_count = 0 ; } % orig; g_volume_up_time = nowtime; } - (void)decreaseVolume { NSTimeInterval nowtime = [[NSDate date] timeIntervalSince1970]; NSLog(@ "witwit iosother decreaseVolume" ); if (nowtime - g_volume_down_time < 1 ) { g_volume_down_count + = 1 ; if (g_volume_down_count > = 2 ) { g_volume_down_count = 0 ; [AppAngel enablePlugins:NO]; } } else { g_volume_down_count = 0 ; } % orig; g_volume_down_time = nowtime; } / / 在使用音量键开启或关闭插件的toast在iOS12系统中会和音量弹窗重叠,hook音量弹窗 - ( BOOL )_HUDIsDisplayableForCategory:(NSString * )category { if ([category isEqualToString:@ "Audio/Video" ]) { return NO; } return % orig; } % end |
电源键监听相关代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | / / 注册锁屏通知 notify_register_dispatch( "com.apple.springboard.lockstate" , ¬ifyToken, dispatch_get_main_queue(), ^( int token) { uint64_t state = 0 ; notify_get_state(token, &state); BOOL isScreenLocked = state = = 1 ; if (isScreenLocked) { NSLog(@ "witwit 锁屏了" ); if ( [AppAngel getPreferencesWithKey:@ "HookEnable" ]) { [AppAngel enablePlugins:NO]; } } }); |
由于我们可以会对特定的App进行配置。于是使用AppList插件来实现该界面:
<img src="bf4K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6F1j5i4y4Q4x3X3g2%4K9i4c8U0K9r3q4F1i4K6u0W2j5$3!0E0i4K6y4m8y4e0R3H3x3W2)9J5c8X3W2F1k6r3g2^5i4K6u0W2M7r3S2H3i4K6u0r3M7#2)9J5c8Y4c8T1j5#2u0&6h3V1A6y4x3W2S2V1L8U0x3$3L8#2)9J5c8Y4m8J5k6i4k6A6k6i4M7`." alt="image-20230826004858208" style="zoom:50%;" />
相关源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | <?xml version = "1.0" encoding = "UTF-8" ?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "fd2K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3q4H3M7r3I4W2i4K6u0W2j5$3!0E0i4K6u0r3c8q4c8p5M7#2)9J5c8W2m8J5L8%4m8W2M7Y4c8&6e0r3W2K6N6q4)9J5k6o6q4Q4x3X3f1H3i4K6u0W2k6s2c8V1" > <plist version = "1.0" > < dict > <key>entry< / key> < dict > <key>bundle< / key> <string>AppList< / string> <key>cell< / key> <string>PSLinkCell< / string> <key>icon< / key> <string> / Library / PreferenceLoader / Preferences / AppAngelIcon.png< / string> <key>isController< / key> <string> 1 < / string> <key>label< / key> <string>应用天使< / string> <key>ALSettingsPath< / key> <string> / var / mobile / Library / Preferences / com.witchan.AppAngel.plist< / string> <key>ALSettingsKeyPrefix< / key> <string>witwit - < / string> <key>ALChangeNotification< / key> <string>com.rpetrich.applist.sample.notification< / string> <key>ALAllowsSelection< / key> <string> 1 < / string> <key>ALSectionDescriptors< / key> <array> < dict > <key>items< / key> <array> < dict > <key>text< / key> <string>关于我< / string> <key>action< / key> <string>launchURL< / string> <key>url< / key> <string>https: / / mp.weixin.qq.com / s / WERMNPzW6WV5YGFthVqCRg< / string> < / dict > < / array> < / dict > < dict > <key>footer - title< / key> <string>连按音量 + 键开启插件,连按音量 - 或电源键停止插件 < / string> <key>items< / key> <array> < dict > <key>cell - class - name< / key> <string>ALSwitchCell< / string> <key>default< / key> <string>NO< / string> <key>ALSettingsKey< / key> <string>witwit - HookEnable< / string> <key>text< / key> <string>插件开关< / string> < / dict > < / array> < / dict > < dict > <key>title< / key> <string>用户应用< / string> <key>predicate< / key> <string>isSystemApplication = FALSE< / string> <key>cell - class - name< / key> <string>ALSwitchCell< / string> <key>icon - size< / key> <string> 29 < / string> <key>suppress - hidden - apps< / key> <string> 1 < / string> < / dict > < dict > <key>title< / key> <string>系统应用< / string> <key>predicate< / key> <string>isSystemApplication = TRUE< / string> <key>cell - class - name< / key> <string>ALSwitchCell< / string> <key>icon - size< / key> <string> 29 < / string> <key>suppress - hidden - apps< / key> <string> 1 < / string> < / dict > < / array> < / dict > < / dict > < / plist> |
总结
本文主要系统插件的实现过程进行了分析及试验,插件名叫【应用天使】已在iOS12和15系统上测试并通过,已上传到bigboss源。需要的同学请可下载使用,当你在使用中遇到任何问题也可向我反馈。如需要插件完整源码,请公众号回复【应用天使】即可获取完整代码。
提示:阅读此文档的过程中遇到任何问题,请关住工众好【
移动端Android和iOS开发技术分享
】或+99 君羊【812546729
】