某不知名App越狱检测

1.目标

此篇文本为入门文章,大家莫抱过多期望。此文章的目的是教大家如何从UI入手,去定位自己想要的东西。

2.操作环境

  • mac系统

  • frida-ios-dump:砸壳

  • 已越狱iOS设备:脱壳及frida调试

  • IDA Pro:静态分析

3.流程

寻找切入点

启动App后,界面如下图:

image-20220912164024604

分析过程

从界面可以看出,App检测到越狱后,会弹出一个弹窗,文案为越狱手机存在安全风险,做iOS开发的都知道,最终这文案显示前,会调用UILabel类的setText:方法。我们trace该方法并打印堆栈:

js代码:

{
  onEnter(log, args, state) {
    log(`-[UILabel setText:${new ObjC.Object(args[2])}]`);
	  log('UILabel setText called from:\n' +
	        Thread.backtrace(this.context, Backtracer.ACCURATE)
	        .map(DebugSymbol.fromAddress).join('\n') + '\n');
  },
  onLeave(log, retval, state) {
  }
}

关键日志如下:

-[UILabel setText:越狱手机存在安全风险]
UILabel setText called from:
0x1eaa7d0f4 UIKitCore!-[_UIAlertControllerView _updateMessageLabelContents]
0x1eaa756f8 UIKitCore!-[_UIAlertControllerView _prepareMesssageLabel]
0x1eaa750f8 UIKitCore!-[_UIAlertControllerView _prepareViewsAndAddConstraints]
0x1eaa75048 UIKitCore!-[_UIAlertControllerView setAlertController:]
0x1eaa61740 UIKitCore!-[UIAlertController loadView]
0x1ead35ed8 UIKitCore!-[UIViewController loadViewIfRequired]
0x1ead36628 UIKitCore!-[UIViewController view]
0x1ead4ddd4 UIKitCore!-[UIViewController _setPresentationController:]
0x1ead461f4 UIKitCore!-[UIViewController _presentViewController:modalSourceViewController:presentationController:animationController:interactionController:completion:]
0x1ead47ccc UIKitCore!-[UIViewController _presentViewController:withAnimationController:completion:]
0x1ead4a3a8 UIKitCore!__63-[UIViewController _presentViewController:animated:completion:]_block_invoke
0x1ead4a8a4 UIKitCore!-[UIViewController _performCoordinatedPresentOrDismiss:animated:]
0x1ead4a300 UIKitCore!-[UIViewController _presentViewController:animated:completion:]
0x1ead4a560 UIKitCore!-[UIViewController presentViewController:animated:completion:]
0x10128253c App!0xd253c (0x1000d253c)
0x1be344a38 libdispatch.dylib!_dispatch_call_block_and_release

使用frida-ios-dump砸壳后,再使用IDA Pro编译ipa文件。

跳转到内存0x1000d253c位置

image-20220911160319867

再按F5:

__int64 __fastcall sub_1000D24D4(_QWORD *a1)
{
  void *v1; // x19
  __int64 v2; // x20
  void **v4; // [xsp+8h] [xbp-38h]
  __int64 v5; // [xsp+10h] [xbp-30h]
  __int64 (__fastcall *v6)(); // [xsp+18h] [xbp-28h]
  void *v7; // [xsp+20h] [xbp-20h]
  __int64 v8; // [xsp+28h] [xbp-18h]

  v1 = (void *)a1[4];
  v2 = a1[5];
  v4 = _NSConcreteStackBlock;
  v5 = 3254779904LL;
  v6 = sub_1000D2554;
  v7 = &unk_101A29970;
  v8 = objc_retain(a1[6]);
  objc_msgSend(v1, "presentViewController:animated:completion:", v2, 1LL, &v4);
  return objc_release(v8);
}

查找sub_1000D24D4函数的交叉引用,一层一层往上找,最终找到如下函数:

void __cdecl -[UIViewController cft_presentViewController:presentType:presentCompletionHandler:dismissCompleteHandler:](UIViewController *self, SEL a2, id a3, unsigned __int64 a4, id a5, id a6)
{
  id v6; // x21
  id v7; // x20
  UIViewController *v8; // x24
  void *v9; // x19
  __int64 v10; // x20
  __int64 v11; // x21
  void *v12; // x0
  __int64 v13; // x22
  dispatch_semaphore_t v14; // x23
  void *v15; // x0
  void *v16; // x24
  void *v17; // x0
  void *v18; // x0
  void *v19; // x27
  void *v20; // x0
  __int64 v21; // x28
  void *v22; // x0
  void *v23; // x0
  void *v24; // x25
  void *v25; // x0
  __int64 v26; // x26
  __int64 v27; // [xsp+8h] [xbp-A8h]
  void **v28; // [xsp+10h] [xbp-A0h]
  __int64 v29; // [xsp+18h] [xbp-98h]
  __int64 (__fastcall *v30)(__int64); // [xsp+20h] [xbp-90h]
  void *v31; // [xsp+28h] [xbp-88h]
  __int64 v32; // [xsp+30h] [xbp-80h]
  __int64 v33; // [xsp+38h] [xbp-78h]
  __int64 v34; // [xsp+40h] [xbp-70h]
  UIViewController *v35; // [xsp+48h] [xbp-68h]
  __int64 v36; // [xsp+50h] [xbp-60h]
  __int64 v37; // [xsp+58h] [xbp-58h]

  v6 = a6;
  v7 = a5;
  v8 = self;
  v9 = (void *)objc_retain(a3);
  v10 = objc_retain(v7);
  v11 = objc_retain(v6);
  v37 = 0LL;
  v12 = objc_msgSend(
          v9,
          "aspect_hookSelector:withOptions:usingBlock:error:",
          "viewDidDisappear:",
          0LL,
          &off_101A2BF98,
          &v37);
  objc_unsafeClaimAutoreleasedReturnValue(v12);
  v13 = objc_retain(v37);
  if ( !v13 )
  {
    v14 = dispatch_semaphore_create(0LL);
    v28 = _NSConcreteStackBlock;
    v29 = 3254779904LL;
    v30 = sub_1000D23A0;
    v31 = &unk_101A2BFB8;
    v32 = objc_retain(v9);
    v33 = objc_retain(v11);
    v27 = objc_retain(v14);
    v34 = v27;
    v35 = v8;
    v36 = objc_retain(v10);
    v15 = objc_msgSend(&OBJC_CLASS___NSBlockOperation, "blockOperationWithBlock:", &v28);
    v16 = (void *)objc_retainAutoreleasedReturnValue(v15);
    v17 = objc_msgSend((void *)qword_101F71BF8, "operations");
    v18 = (void *)objc_retainAutoreleasedReturnValue(v17);
    v19 = v18;
    v20 = objc_msgSend(v18, "lastObject");
    v21 = objc_retainAutoreleasedReturnValue(v20);
    objc_release(v21);
    objc_release(v19);
    if ( v21 )
    {
      v22 = objc_msgSend((void *)qword_101F71BF8, "operations");
      v23 = (void *)objc_retainAutoreleasedReturnValue(v22);
      v24 = v23;
      v25 = objc_msgSend(v23, "lastObject");
      v26 = objc_retainAutoreleasedReturnValue(v25);
      objc_msgSend(v16, "addDependency:", v26);
      objc_release(v26);
      objc_release(v24);
    }
    objc_msgSend((void *)qword_101F71BF8, "addOperation:", v16);
    objc_release(v16);
    objc_release(v36);
    objc_release(v34);
    objc_release(v33);
    objc_release(v32);
    objc_release(v27);
  }
  objc_release(v13);
  objc_release(v11);
  objc_release(v10);
  objc_release(v9);
}

接下来,同时跟踪UILabel的setText:方法和UIViewController的XXX_presentViewController:presentType:presentCompletionHandler:dismissCompleteHandler:方法,获取到日志如下:

-[UIViewController cft_presentViewController:0x104928e00 presentType:0x1 presentCompletionHandler:0x16ec4e5a8 dismissCompleteHandler:0x16ec4e580]
UIViewController cft_presentViewController called from:
0x1012b9fdc App!+[CFTAlertPresentController presentAlertController:presentCompletionHandler:dismissCompleteHandler:]
0x1012d464c App!-[LaunchingViewController start]
0x1012d3360 App!0x123360 (0x100123360)
0x1be3457d4 libdispatch.dylib!_dispatch_client_callout
0x1be2f3c1c libdispatch.dylib!_dispatch_lane_barrier_sync_invoke_and_complete
0x1012d3310 App!0x123310 (0x100123310)
0x1012b9978 App!0x109978 (0x100109978)
0x1eb76ad1c UIKitCore!-[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:]
0x1eb741a74 UIKitCore!-[UIViewAnimationState sendDelegateAnimationDidStop:finished:]
0x1eb742048 UIKitCore!-[UIViewAnimationState animationDidStop:finished:]
0x1c2e573c8 QuartzCore!CA::Layer::run_animation_callbacks(void*)
0x1be3457d4 libdispatch.dylib!_dispatch_client_callout
0x1be2f3008 libdispatch.dylib!_dispatch_main_queue_callback_4CF$VARIANT$mp
0x1be898b20 CoreFoundation!__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
0x1be893a58 CoreFoundation!__CFRunLoopRun
0x1be892fb4 CoreFoundation!CFRunLoopRunSpecific

-[UILabel setText:您的设备不安全]
UILabel setText called from:
0x1eaa7cfe8 UIKitCore!-[_UIAlertControllerView _updateTitleLabelContents]
0x1eaa75544 UIKitCore!-[_UIAlertControllerView _prepareTitleLabel]
0x1eaa750e8 UIKitCore!-[_UIAlertControllerView _prepareViewsAndAddConstraints]
0x1eaa75048 UIKitCore!-[_UIAlertControllerView setAlertController:]
0x1eaa61740 UIKitCore!-[UIAlertController loadView]
0x1ead35ed8 UIKitCore!-[UIViewController loadViewIfRequired]
0x1ead36628 UIKitCore!-[UIViewController view]
0x1ead4ddd4 UIKitCore!-[UIViewController _setPresentationController:]
0x1ead461f4 UIKitCore!-[UIViewController _presentViewController:modalSourceViewController:presentationController:animationController:interactionController:completion:]
0x1ead47ccc UIKitCore!-[UIViewController _presentViewController:withAnimationController:completion:]
0x1ead4a3a8 UIKitCore!__63-[UIViewController _presentViewController:animated:completion:]_block_invoke
0x1ead4a8a4 UIKitCore!-[UIViewController _performCoordinatedPresentOrDismiss:animated:]
0x1ead4a300 UIKitCore!-[UIViewController _presentViewController:animated:completion:]
0x1ead4a560 UIKitCore!-[UIViewController presentViewController:animated:completion:]
0x10128253c App!0xd253c (0x1000d253c)
0x1be344a38 libdispatch.dylib!_dispatch_call_block_and_release

-[UILabel setText:越狱手机存在安全风险]
UILabel setText called from:
0x1eaa7d0f4 UIKitCore!-[_UIAlertControllerView _updateMessageLabelContents]
0x1eaa756f8 UIKitCore!-[_UIAlertControllerView _prepareMesssageLabel]
0x1eaa750f8 UIKitCore!-[_UIAlertControllerView _prepareViewsAndAddConstraints]
0x1eaa75048 UIKitCore!-[_UIAlertControllerView setAlertController:]
0x1eaa61740 UIKitCore!-[UIAlertController loadView]
0x1ead35ed8 UIKitCore!-[UIViewController loadViewIfRequired]
0x1ead36628 UIKitCore!-[UIViewController view]
0x1ead4ddd4 UIKitCore!-[UIViewController _setPresentationController:]
0x1ead461f4 UIKitCore!-[UIViewController _presentViewController:modalSourceViewController:presentationController:animationController:interactionController:completion:]
0x1ead47ccc UIKitCore!-[UIViewController _presentViewController:withAnimationController:completion:]
0x1ead4a3a8 UIKitCore!__63-[UIViewController _presentViewController:animated:completion:]_block_invoke
0x1ead4a8a4 UIKitCore!-[UIViewController _performCoordinatedPresentOrDismiss:animated:]
0x1ead4a300 UIKitCore!-[UIViewController _presentViewController:animated:completion:]
0x1ead4a560 UIKitCore!-[UIViewController presentViewController:animated:completion:]
0x10128253c App!0xd253c (0x1000d253c)
0x1be344a38 libdispatch.dylib!_dispatch_call_block_and_release

在XXX_presentViewController:presentType:presentCompletionHandler:dismissCompleteHandler:方法的调用栈,发现[LaunchingViewController start]方法,使用ida pro查看该函数:

void __cdecl -[LaunchingViewController start](LaunchingViewController *self, SEL a2)
{
  if ( !(~LODWORD(self->_launchAchieveOption) & 0x1FLL) )
  {
    v2 = self;
    self->_launchAchieveOption = 0LL;
    v3 = ((id (__cdecl *)(GuaTabBarController_meta *, SEL))objc_msgSend)(
           (GuaTabBarController_meta *)&OBJC_CLASS___GuaTabBarController,
           "shareTabBarController");
    v4 = objc_retainAutoreleasedReturnValue(v3);
    v23 = _NSConcreteStackBlock;
    v24 = 3254779904LL;
    v25 = sub_100124684;
    v26 = &unk_101A2BC20;
    v5 = (void *)objc_retain(v4);
    v27 = v5;
    v28 = v2;
    +[NaviService naviModelWithNaviRoot:withNaviHead:withSourceFrom:ret:](
      &OBJC_CLASS___NaviService,
      "naviModelWithNaviRoot:withNaviHead:withSourceFrom:ret:",
      _NSConcreteStackBlock,
      3254779904LL,
      sub_100124684,
      &unk_101A2BC20);
    if ( v2->_adDetailDisplayController )
    {
      v6 = objc_msgSend(v5, "viewControllers", v23, v24, v25, v26);
      v7 = (void *)objc_retainAutoreleasedReturnValue(v6);
      v8 = v7;
      v9 = objc_msgSend(v7, "objectAtIndexedSubscript:", 0LL, v23, v24, v25, v26);
      v10 = (void *)objc_retainAutoreleasedReturnValue(v9);
      objc_msgSend(v10, "pushViewController:animated:", v2->_adDetailDisplayController, 1LL, v23, v24, v25, v26);
    }
    v11 = objc_msgSend(&OBJC_CLASS___UIApplication, "sharedApplication", v23, v24, v25, v26);
    v12 = (void *)objc_retainAutoreleasedReturnValue(v11);
    v13 = v12;
    v14 = objc_msgSend(v12, "delegate", v23, v24, v25, v26);
    v15 = (void *)objc_retainAutoreleasedReturnValue(v14);
    v16 = v15;
    v17 = objc_msgSend(v15, "window", v23, v24, v25, v26);
    v18 = (void *)objc_retainAutoreleasedReturnValue(v17);
    objc_msgSend(v18, "setRootViewController:", v5, v23, v24, v25, v26);
    if ( (unsigned int)+[XXXJailBreakJudge deviceIsJailBreak](
                         &OBJC_CLASS___XXXJailBreakJudge,
                         "deviceIsJailBreak",
                         v23,
                         v24,
                         v25,
                         v26) )
    {
      v19 = objc_msgSend(
              &OBJC_CLASS___UIAlertController,
              "alertControllerWithTitle:message:preferredStyle:",
              CFSTR("您的设备不安全"),
              CFSTR("越狱手机存在安全风险"),
              1LL,
              v23,
              v24,
              v25,
              v26);
      v20 = (void *)objc_retainAutoreleasedReturnValue(v19);
      v21 = objc_msgSend(
              &OBJC_CLASS___UIAlertAction,
              "actionWithTitle:style:handler:",
              CFSTR("确定"),
              0LL,
              0LL,
              v23,
              v24,
              v25,
              v26);
      v22 = objc_retainAutoreleasedReturnValue(v21);
      objc_msgSend(v20, "addAction:", v22, v23, v24, v25, v26);
      +[XXXAlertPresentController presentAlertController:](
        &OBJC_CLASS___XXXAlertPresentController,
        "presentAlertController:",
        v20,
        v23,
        v24,
        v25,
        v26);
    }
  }
}

从函数中可发现越狱检测函数为[XXXJailBreakJudge deviceIsJailBreak]:

bool __cdecl +[CFTJailBreakJudge deviceIsJailBreak](CFTJailBreakJudge_meta *self, SEL a2)
{
  CFTJailBreakJudge_meta *v2; // x20
  void *v3; // x0
  __int64 v4; // x0
  void *v5; // x0
  void *v6; // x19
  void *v7; // x0
  void *v8; // x22
  __int64 v9; // x25
  unsigned __int64 v10; // x26
  uint32_t v11; // w0
  uint32_t v12; // w20
  uint32_t v13; // w21
  void *v14; // x25
  const char *v15; // x0
  void *v16; // x25
  char v17; // w26
  _BOOL8 v18; // x20
  bool result; // w0
  __int128 v20; // [xsp+0h] [xbp-140h]
  __int128 v21; // [xsp+10h] [xbp-130h]
  __int128 v22; // [xsp+20h] [xbp-120h]
  __int128 v23; // [xsp+30h] [xbp-110h]
  char v24; // [xsp+40h] [xbp-100h]
  const __CFString *v25; // [xsp+C0h] [xbp-80h]
  const __CFString *v26; // [xsp+C8h] [xbp-78h]
  const __CFString *v27; // [xsp+D0h] [xbp-70h]
  const __CFString *v28; // [xsp+D8h] [xbp-68h]
  const __CFString *v29; // [xsp+E0h] [xbp-60h]
  __int64 v30; // [xsp+E8h] [xbp-58h]

  v2 = self;
  v25 = CFSTR("/Applications/Cydia.app");
  v26 = CFSTR("/Library/MobileSubstrate/MobileSubstrate.dylib");
  v27 = CFSTR("/bin/bash");
  v28 = CFSTR("/usr/sbin/sshd");
  v29 = CFSTR("/etc/apt");
  v3 = objc_msgSend(&OBJC_CLASS___NSArray, "arrayWithObjects:count:", &v25, 5LL);
  v4 = objc_retainAutoreleasedReturnValue(v3);
  v20 = 0u;
  v21 = 0u;
  v22 = 0u;
  v23 = 0u;
  v5 = (void *)objc_retain(v4);
  v6 = v5;
  v7 = objc_msgSend(v5, "countByEnumeratingWithState:objects:count:", &v20, &v24, 16LL, 0LL);
  if ( v7 )
  {
    v8 = v7;
    v9 = *(_QWORD *)v21;
    while ( 2 )
    {
      v10 = 0LL;
      do
      {
        if ( *(_QWORD *)v21 != v9 )
          objc_enumerationMutation(v6);
        if ( (unsigned __int64)objc_msgSend(
                                 v2,
                                 "permissionForFile:",
                                 *(_QWORD *)(*((_QWORD *)&v20 + 1) + 8 * v10),
                                 (_QWORD)v20) & 1 )
        {
          objc_release(v6);
          goto LABEL_16;
        }
        ++v10;
      }
      while ( v10 < (unsigned __int64)v8 );
      v8 = objc_msgSend(v6, "countByEnumeratingWithState:objects:count:", &v20, &v24, 16LL, (_QWORD)v20);
      if ( v8 )
        continue;
      break;
    }
  }
  objc_release(v6);
  if ( !((unsigned __int64)objc_msgSend(v2, "permissionForFile:", CFSTR("/User/Applications/"), (_QWORD)v20) & 1) )
  {
    v11 = _dyld_image_count();
    if ( !v11 )
    {
LABEL_14:
      v18 = getenv("DYLD_INSERT_LIBRARIES") != 0LL;
      goto LABEL_17;
    }
    v12 = v11;
    v13 = 0;
    while ( 1 )
    {
      v14 = (void *)objc_alloc(&OBJC_CLASS___NSString);
      v15 = _dyld_get_image_name(v13);
      v16 = objc_msgSend(v14, "initWithUTF8String:", v15, (_QWORD)v20);
      v17 = (unsigned __int64)objc_msgSend(v16, "containsString:", CFSTR("MobileSubstrate.dylib"), (_QWORD)v20);
      objc_release(v16);
      if ( v17 & 1 )
        break;
      if ( v12 == ++v13 )
        goto LABEL_14;
    }
  }
LABEL_16:
  v18 = 1;
LABEL_17:
  return v18;
}

结果

1、检测以下路径是否存在:

  • /Applications/Cydia.app
  • /Library/MobileSubstrate/MobileSubstrate.dylib
  • /bin/bash
  • /usr/sbin/sshd
  • /etc/apt

2、如果路径/User/Applications/不存在,检测getenv(“DYLD_INSERT_LIBRARIES”)是否存在

3、如果路径/User/Applications/不存在,检测当前现在在运行的动态库是否包含MobileSubstrate.dylib

End

阅读此文档的过程中遇到任何问题,请关注公众号【移动端Android和iOS开发技术分享】或加QQ群【812546729

IMG_4048