前语

iOS越狱为用户翻开了无限的可能性,其中之一是便是开发体系级插件,为了保证运用程序一直保持在前台,即便在意外状况下也是如此。

本文将向您展示怎么轻松编写这样的插件,让咱们开端探索iOS体系插件的国际吧!


一、方针

学会创立功能强大的iOS体系插件。

二、开发环境和东西清单

  • mac体系
  • frida:动态调试
  • 已越狱iOS设备(iOS12.5.5):动态调试及插件测验

三、步骤

1、定位要害函数

SpringBoard是iOS体系非常重要的一个体系运用程序,它为用户提供了访问和办理运用程序的首要办法,同时也是用户与设备交互的一个重要界面。咱们今日的调试方针便是它。

运用frida-trace动态调试地,怎么给定要害词来缩小函数范围,我在这有几点经历:

  1. 依据类的前缀来减少不相关的类:

    假如你要盯梢的库为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。

  2. 依据你的方针来限定要害词,如你想要盯梢的功能和定位有联系。那你能够测验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万多个函数,范围太广,手机直接卡死了……

【iOS逆向与安全】编写一个使应用保持前台运行的系统插件

从头越狱调整指令为frida-trace -U -m "*[SB*pp* *]" -o a.log SpringBoard,将运用切换到后台,获取到要害日志如下:

  ......
  ......
  ......
  ......
  ......
  ......
 仍然太多了,你也能够一点点分析,看看哪些函数可疑,然后再盯梢。

再调试trace指令,加上statefrida-trace -U -m "*[SB*pp* *tate*]" -o a.log SpringBoard,将运用切换到后台,获取到的部分要害日志如下:

-[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 SpringBoardjs代码如下:

{
 onEnter(log, args, state) {
  log(`-[SBApplication _noteProcess:${ObjC.Object(args[2])} didChangeToState:${ObjC.Object(args[3])}]`);
  },
 onLeave(log, retval, state) {
  }
}
​

运用切到后台,获取到日志如下:

-[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来创立插件工程:

【iOS逆向与安全】编写一个使应用保持前台运行的系统插件

运用状况hook代码如下:

%hook SBMainWorkspace
​
-(void)process:(id)arg1 stateDidChangeFromState:(id)arg2 toState:(id)arg3{
  %orig;
​
  // arg1的类型为:FBApplicationProcess,获取包名的办法名为bundleIdentifier
  // arg3的类型为:FBProcessState,获取到状况的办法名为taskState
  // 在这查找类对应的头文件:https://developer.limneos.net/index.php
  
  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代码如下:

%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

电源键监听相关代码如下:

 // 注册锁屏告诉
   notify_register_dispatch("com.apple.springboard.lockstate", &notifyToken, 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插件来完成该界面:

【iOS逆向与安全】编写一个使应用保持前台运行的系统插件

相关源码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<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源。需求的同学请可下载运用,当你在运用中遇到任何问题也可向我反馈。如需求插件完好源码,请大众号回复【运用天使】即可获取完好代码。

注:在iOS15体系中,bigboss源的AppList插件不支持,请从repo.palera.in/源里下载并装置AppList,或许自行下载支持iOS15的AppList。

提示:阅读此文档的进程中遇到任何问题,请关住工众好【移动端Android和iOS开发技能分享】或+99 君羊【812546729