1.目标
此篇文本为入门文章,大家莫抱过多期望。此文章的目的是教大家如何从UI入手,去定位自己想要的东西。
2.操作环境
mac系统
frida-ios-dump:砸壳
已越狱iOS设备:脱壳及frida调试
IDA Pro:静态分析
3.流程
寻找切入点
启动App后,界面如下图:
IMG_67EF545277E4-1
分析过程
从界面可以看出,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_invoke0x1ead4a8a4 UIKitCore!-[UIViewController _performCoordinatedPresentOrDismiss:animated:]0x1ead4a300 UIKitCore!-[UIViewController _presentViewController:animated:completion:]0x1ead4a560 UIKitCore!-[UIViewController presentViewController:animated:completion:]0x10128253c GeneralApp!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:{ 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的cft_presentViewController:presentType:presentCompletionHandler:dismissCompleteHandler:方法,获取到日志如下:
-[UIViewController cft_presentViewController:0x104928e00 presentType:0x1 presentCompletionHandler:0x16ec4e5a8 dismissCompleteHandler:0x16ec4e580]UIViewController cft_presentViewController called from:0x1012b9fdc GeneralApp!+[CFTAlertPresentController presentAlertController:presentCompletionHandler:dismissCompleteHandler:]0x1012d464c GeneralApp!-[LaunchingViewController start]0x1012d3360 GeneralApp!0x123360 (0x100123360)0x1be3457d4 libdispatch.dylib!_dispatch_client_callout0x1be2f3c1c libdispatch.dylib!_dispatch_lane_barrier_sync_invoke_and_complete0x1012d3310 GeneralApp!0x123310 (0x100123310)0x1012b9978 GeneralApp!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_callout0x1be2f3008 libdispatch.dylib!_dispatch_main_queue_callback_4CF$VARIANT$mp0x1be898b20 CoreFoundation!__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__0x1be893a58 CoreFoundation!__CFRunLoopRun0x1be892fb4 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_invoke0x1ead4a8a4 UIKitCore!-[UIViewController _performCoordinatedPresentOrDismiss:animated:]0x1ead4a300 UIKitCore!-[UIViewController _presentViewController:animated:completion:]0x1ead4a560 UIKitCore!-[UIViewController presentViewController:animated:completion:]0x10128253c GeneralApp!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_invoke0x1ead4a8a4 UIKitCore!-[UIViewController _performCoordinatedPresentOrDismiss:animated:]0x1ead4a300 UIKitCore!-[UIViewController _presentViewController:animated:completion:]0x1ead4a560 UIKitCore!-[UIViewController presentViewController:animated:completion:]0x10128253c GeneralApp!0xd253c (0x1000d253c)0x1be344a38 libdispatch.dylib!_dispatch_call_block_and_release
在cft_presentViewController:presentType:presentCompletionHandler:dismissCompleteHandler:方法的调用栈,发现[LaunchingViewController start]方法,使用ida pro查看该函数:
void __cdecl -LaunchingViewController start{ 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:; 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)+CFTJailBreakJudge deviceIsJailBreak ) { 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); +CFTAlertPresentController presentAlertController:; } }}
从函数中可发现越狱检测函数为[CFTJailBreakJudge deviceIsJailBreak]:
bool __cdecl +CFTJailBreakJudge deviceIsJailBreak{ 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】