Skip to content Skip to footer

iOS逆向系列(二) 反调试与绕过案例实战

项目地址:https://github.com/cherubstar/iOSEnvDetection根据项目使用Xcode构建app安装到手机上iPhone:iPhone se2(越狱),iOS 14.4.1

Frida检测端口检测上来先用objection搜索有没有frida相关的类,可以看到其中FridaDetection比较符合,对其hook

1234567891011com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # ios hooking search classes fridaEnvDetection.FridaDetectionViewModel_TtGC7SwiftUIP10$1a5ddeda09BoxVTableVGVS_11StateObjectC12EnvDetection23FridaDetectionViewModel_P10$1a5d8f8983Box__TtGC7SwiftUIP10$1a5ddeda09BoxVTableGVS_P10$1a5db422825ObservedObjectPropertyBoxC12EnvDetection23FridaDetectionViewModel__FridaDetectionFound 4 classescom.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # ios hooking list class_methods FridaDetection- checkListeningPort27042Found 1 methods

只有 checkListeningPort27042 一个函数,hook查看调用栈返回值

12345678910111213141516171819202122com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # ios hooking watch method "*[FridaDetection checkListeningPort27042]" --dump-args --dump-backtrace --dump-return(agent) Found selector at 0x102034000 as -[FridaDetection checkListeningPort27042](agent) Registering job 246950. Type: watch-method for: *[FridaDetection checkListeningPort27042]com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # (agent) [246950] Called: *[FridaDetection checkListeningPort27042] 0 arguments(Kind: instance) (Super: NSObject)(agent) [246950] *[FridaDetection checkListeningPort27042] Backtrace: 0x102051240 EnvDetection!FridaDetectionViewModel.checkListeningPort27042() 0x1020865d4 EnvDetection!closure #2 in closure #1 in closure #1 in FridaDetectionView.body.getter 0x1a56f0e30 SwiftUI!partial apply for implicit closure #2 in implicit closure #1 in WrappedButtonStyle.Body.body.getter 0x1a5a07f28 SwiftUI!closure #1 in PressableGestureCallbacks.dispatch(phase:state:) 0x1a5771b98 SwiftUI!thunk for @escaping @callee_guaranteed () -> () 0x1a5771bc0 SwiftUI!thunk for @escaping @callee_guaranteed () -> (@out ()) 0x1a5771b98 SwiftUI!thunk for @escaping @callee_guaranteed () -> () 0x1a5760764 SwiftUI!static Update.end() 0x1a57b2e6c SwiftUI!EventBindingManager.send(_:) 0x1a5c3fe50 SwiftUI!specialized EventBindingBridge.send(_:source:) 0x1a5c3df28 SwiftUI!UIKitGestureRecognizer.send(touches:event:phase:) 0x1a5c3ebe0 SwiftUI!@objc UIKitGestureRecognizer.touchesBegan(_:with:) 0x1a11cad2c UIKitCore!-[UIGestureRecognizer _componentsEnded:withEvent:] 0x1a171325c UIKitCore!-[UITouchesEvent _sendEventToGestureRecognizer:] 0x1a11c0bd4 UIKitCore!__47-[UIGestureEnvironment _updateForEvent:window:]_block_invoke 0x1a11c08b4 UIKitCore!-[UIGestureEnvironment _updateForEvent:window:](agent) [246950] Return Value: 0x1

返回了0x1,那是不是把函数返回值改为0x0,就不会被检测到

1com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # ios hooking set return_value "*[FridaDetection checkListeningPort27042]" 0x0

再点击检测则过掉了frida代码

12345678910function main(){ Interceptor.attach(ObjC.classes["FridaDetection"]["- checkListeningPort27042"].implementation, { onEnter: function(args){ }, onLeave: function(ret){ console.log("ret =>", ret); ret.replace(new NativePointer(0x0)) } })}setImmediate(main)

文件检测既然是文件检测,那肯定涉及到文件的读取,查资料把文件操作相关的类都用objection查找一遍,最终得到是 NSFileManager 类,进行hook

1com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # ios hooking watch class NSFileManager

手机Frida文件检测刷新一下

123456com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # (agent) [703964] Called: [NSFileManager defaultManager] (Kind: class) (Super: NSObject)(agent) [703964] Called: [NSFileManager _registerForUbiquityAccountChangeNotifications] (Kind: instance) (Super: NSObject)(agent) [703964] Called: [NSFileManager defaultManager] (Kind: class) (Super: NSObject)(agent) [703964] Called: [NSFileManager _registerForUbiquityAccountChangeNotifications] (Kind: instance) (Super: NSObject)(agent) [703964] Called: [NSFileManager defaultManager] (Kind: class) (Super: NSObject)(agent) [703964] Called: [NSFileManager _registerForUbiquityAccountChangeNotifications] (Kind: instance) (Super: NSObject)

对这两个函数进行hook查看调用栈返回值

1234567891011121314151617181920212223242526272829303132333435363738394041424344com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # ios hooking watch method "*[NSFileManager _registerForUbiquityAccountChangeNotifications]" --dump-args --dump-backtrace --dump-return(agent) Found selector at 0x19feee6b0 as -[NSFileManager _registerForUbiquityAccountChangeNotifications](agent) Registering job 536962. Type: watch-method for: *[NSFileManager _registerForUbiquityAccountChangeNotifications]com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # ios hooking watch method "*[NSFileManager defaultManager]" --dump-args --dump-backtrace --dump-return(agent) Found selector at 0x19feec534 as +[NSFileManager defaultManager](agent) Registering job 371228. Type: watch-method for: *[NSFileManager defaultManager]com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # (agent) [371228] Called: *[NSFileManager defaultManager] 0 arguments(Kind: class) (Super: NSObject)(agent) [371228] *[NSFileManager defaultManager] Backtrace: 0x1a000f3d4 Foundation!standardDefaultCenter 0x1a199dc5c UIKitCore!-[UITextEffectsWindow updateForOrientation:forceResetTransform:] 0x1a199e2f0 UIKitCore!+[UITextEffectsWindow _sharedTextEffectsWindowforWindowScene:allowHosted:matchesStatusBarOrientationOnAccess:shouldCreateIfNecessary:] 0x1a199e3fc UIKitCore!+[UITextEffectsWindow sharedTextEffectsWindowForWindowScene:] 0x1a10c640c UIKitCore!-[UIInputResponderController containerWindow] 0x1a10c651c UIKitCore!-[UIInputResponderController containerRootController] 0x1a149a1a8 UIKitCore!-[UIPeripheralHost(UIKitInternal) _isTransitioning] 0x1a0fa219c UIKitCore!-[_UINavigationInteractiveTransition _gestureRecognizer:shouldReceiveEvent:] 0x1a11cfd54 UIKitCore!-[UIGestureRecognizer _delegateShouldReceiveEvent:] 0x1a11cf3f4 UIKitCore!-[UIGestureRecognizer _shouldReceiveTouch:forEvent:recognizerView:] 0x1a171234c UIKitCore!__72-[UITouchesEvent _addGestureRecognizersForView:toTouch:forContinuation:]_block_invoke 0x1a1711cf8 UIKitCore!__62-[UITouchesEvent _collectGestureRecognizersForView:withBlock:]_block_invoke 0x1a1711784 UIKitCore!-[UITouchesEvent _collectGestureRecognizersForView:withBlock:] 0x1a17121b8 UIKitCore!-[UITouchesEvent _addGestureRecognizersForView:toTouch:forContinuation:] 0x1a1712074 UIKitCore!-[UITouchesEvent _addGestureRecognizersForView:toTouch:] 0x1a1712690 UIKitCore!-[UITouchesEvent _addTouch:forDelayedDelivery:](agent) [371228] Return Value: 0x283a77540(agent) [536962] Called: *[NSFileManager _registerForUbiquityAccountChangeNotifications] 0 arguments(Kind: instance) (Super: NSObject)(agent) [536962] *[NSFileManager _registerForUbiquityAccountChangeNotifications] Backtrace: 0x1a000f3e0 Foundation!standardDefaultCenter 0x1a199dc5c UIKitCore!-[UITextEffectsWindow updateForOrientation:forceResetTransform:] 0x1a199e2f0 UIKitCore!+[UITextEffectsWindow _sharedTextEffectsWindowforWindowScene:allowHosted:matchesStatusBarOrientationOnAccess:shouldCreateIfNecessary:] 0x1a199e3fc UIKitCore!+[UITextEffectsWindow sharedTextEffectsWindowForWindowScene:] 0x1a10c640c UIKitCore!-[UIInputResponderController containerWindow] 0x1a10c651c UIKitCore!-[UIInputResponderController containerRootController] 0x1a149a1a8 UIKitCore!-[UIPeripheralHost(UIKitInternal) _isTransitioning] 0x1a0fa219c UIKitCore!-[_UINavigationInteractiveTransition _gestureRecognizer:shouldReceiveEvent:] 0x1a11cfd54 UIKitCore!-[UIGestureRecognizer _delegateShouldReceiveEvent:] 0x1a11cf3f4 UIKitCore!-[UIGestureRecognizer _shouldReceiveTouch:forEvent:recognizerView:] 0x1a171234c UIKitCore!__72-[UITouchesEvent _addGestureRecognizersForView:toTouch:forContinuation:]_block_invoke 0x1a1711cf8 UIKitCore!__62-[UITouchesEvent _collectGestureRecognizersForView:withBlock:]_block_invoke 0x1a1711784 UIKitCore!-[UITouchesEvent _collectGestureRecognizersForView:withBlock:] 0x1a17121b8 UIKitCore!-[UITouchesEvent _addGestureRecognizersForView:toTouch:forContinuation:] 0x1a1712074 UIKitCore!-[UITouchesEvent _addGestureRecognizersForView:toTouch:] 0x1a1712690 UIKitCore!-[UITouchesEvent _addTouch:forDelayedDelivery:]

看了调用栈都是跟系统相关的,底层会去调用libc下的open函数,frida hook open函数

1234567891011function hook_libc_open(){ var openAddr = Module.findExportByName(null, "open"); Interceptor.attach(openAddr, { onEnter: function(args) { console.log("path =>", args[0].readCString()); console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n\t")) }, onLeave: function(retval){ console.log(retval) } })}

123456789101112131415161718[iOS Device::EnvDetection ]-> path => /usr/lib/frida/frida-server0x100486284 EnvDetection!-[FileAndFolderPathDetection checkPathByOpen:] 0x1004d8978 EnvDetection!CommonUtils.checkPath(path:) 0x1004af724 EnvDetection!closure #1 in closure #1 in closure #1 in FridaFileDetectionView.body.getter 0x1a5c0a280 SwiftUI!HStack.init(alignment:spacing:content:) 0x1004af280 EnvDetection!closure #1 in closure #1 in FridaFileDetectionView.body.getter 0x1a5b0ee74 SwiftUI!ForEachChild.updateValue() 0x1a562d7fc SwiftUI!partial apply for implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init(_:) 0x1c8168a50 AttributeGraph!AG::Graph::UpdateStack::update() 0x1c8168e84 AttributeGraph!AG::Graph::update_attribute(AG::data::ptr, bool) 0x1c8172088 AttributeGraph!AG::Subgraph::update(unsigned int) 0x1a5c80cdc SwiftUI!GraphHost.runTransaction() 0x1a5c83e1c SwiftUI!GraphHost.runTransaction(_:) 0x1a5c827a8 SwiftUI!GraphHost.flushTransactions() 0x1a5c83db4 SwiftUI!closure #1 in closure #1 in GraphHost.asyncTransaction(_:mutation:style:) 0x1a5752168 SwiftUI!partial apply for closure #1 in ViewGraphDelegate.updateGraph(body:) 0x1a5bd5e9c SwiftUI!closure #1 in ViewRendererHost.updateViewGraph(body:)0xffffffffffffffff

这里调用了open的只有 /usr/lib/frida/frida-server 未发现的文件,看它的调用栈上一级[FileAndFolderPathDetection checkPathByOpen:] 很像是检测路径的,objection hook一下

12345678910111213141516171819202122232425com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # ios hooking watch class FileAndFolderPathDetection(agent) Watching method: - checkPathByNSFileManager:(agent) Watching method: - checkPathByAccess:(agent) Watching method: - checkPathByStat:(agent) Watching method: - checkPathByLstat:(agent) Watching method: - checkPathByStatfs:(agent) Watching method: - checkPathByOpen:(agent) Watching method: - checkPathByFopen:(agent) Registering job 393195. Type: watch-class-methods for: FileAndFolderPathDetectioncom.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # (agent) [393195] Called: [FileAndFolderPathDetection checkPathByNSFileManager:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByAccess:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByNSFileManager:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByAccess:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByStat:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByLstat:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByStatfs:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByOpen:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByFopen:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByNSFileManager:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByNSFileManager:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByNSFileManager:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByNSFileManager:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByNSFileManager:] (Kind: instance) (Super: NSObject)(agent) [393195] Called: [FileAndFolderPathDetection checkPathByNSFileManager:] (Kind: instance) (Super: NSObject)

这里使用frida对FileAndFolderPathDetection的所有函数进行hook,打印一下参数返回值

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748function print_arguments(args) { var n = 100; var last_arg = ''; for (var i = 2; i < n; ++i) { var arg = (new ObjC.Object(args[i])).toString(); if (arg == 'nil' || arg == last_arg) { break; } last_arg = arg; return ' args' + (i-2) + ': ' + (new ObjC.Object(args[i])).toString() }}function IosTraceClass(targetClass) { console.log("Entering ios hooking => " + targetClass) if (ObjC.classes.hasOwnProperty(targetClass)) { var methods = ObjC.classes[targetClass].$ownMethods; methods.forEach(function (method) { console.log("hooking " + method); try { Interceptor.attach(ObjC.classes[targetClass][method].implementation, { onEnter: function (args) { this.output = "" this.output = this.output.concat("[*] Detected call to: " + targetClass + " -> " + method) this.output = this.output.concat("\r\n") this.output = this.output.concat(print_arguments(args)) }, onLeave: function (ret) { this.output = this.output.concat("\r\nios return value => ", ret, "\r\n") console.log(this.output) // ret.replace(new NativePointer(0x0)); // 修改返回值 } }) } catch (error) { console.log("ios hooking failed error is => ", error) } }) }}function traceClass(targetClass) { if (ObjC.available) { IosTraceClass(targetClass) }}function main() { traceClass("FileAndFolderPathDetection")}

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859[iOS Device::EnvDetection ]-> [*] Detected call to: FileAndFolderPathDetection -> - checkPathByNSFileManager: args0: /usr/sbin/frida-serverios return value => 0x0[*] Detected call to: FileAndFolderPathDetection -> - checkPathByAccess: args0: /usr/sbin/frida-serverios return value => 0x1 [*] Detected call to: FileAndFolderPathDetection -> - checkPathByNSFileManager: args0: /usr/lib/frida/frida-serverios return value => 0x0[*] Detected call to: FileAndFolderPathDetection -> - checkPathByAccess: args0: /usr/lib/frida/frida-serverios return value => 0x0[*] Detected call to: FileAndFolderPathDetection -> - checkPathByStat: args0: /usr/lib/frida/frida-serverios return value => 0x0[*] Detected call to: FileAndFolderPathDetection -> - checkPathByLstat: args0: /usr/lib/frida/frida-serverios return value => 0x0[*] Detected call to: FileAndFolderPathDetection -> - checkPathByStatfs: args0: /usr/lib/frida/frida-serverios return value => 0x0[*] Detected call to: FileAndFolderPathDetection -> - checkPathByOpen: args0: /usr/lib/frida/frida-serverios return value => 0x0[*] Detected call to: FileAndFolderPathDetection -> - checkPathByFopen: args0: /usr/lib/frida/frida-serverios return value => 0x0[*] Detected call to: FileAndFolderPathDetection -> - checkPathByNSFileManager: args0: /usr/lib/frida/frida-agent.dylibios return value => 0x1[*] Detected call to: FileAndFolderPathDetection -> - checkPathByNSFileManager: args0: /Library/LaunchDaemons/re.frida.server.plistios return value => 0x1[*] Detected call to: FileAndFolderPathDetection -> - checkPathByNSFileManager: args0: /Library/dpkg/info/re.frida.server.extrainst_ios return value => 0x1[*] Detected call to: FileAndFolderPathDetection -> - checkPathByNSFileManager: args0: /Library/dpkg/info/re.frida.server.listios return value => 0x1[*] Detected call to: FileAndFolderPathDetection -> - checkPathByNSFileManager: args0: /Library/dpkg/info/re.frida.server.prermios return value => 0x1[*] Detected call to: FileAndFolderPathDetection -> - checkPathByNSFileManager: args0: /Library/dpkg/info/re.frida.server.md5sumsios return value => 0x1

跟上面的图对照可以发现返回值是0x1的就是检测到的,所以修改所有函数的返回值为0x0,上面代码30行去掉注释,再次运行就全过掉了。

越狱检测其中文件/目录检测,插件APP检测也都是同上面frida文件检测,也是同样过掉了。

代理检测代理APP检测我设备下载还打开了Shadowrocket,不知道为什么代理APP检测测出来。也是objection查找类试了好几个关键词 proxy,network出来很多个类,既然它APP检测有Shadowrocket,直接ida打开搜索字符串下载app压缩包:https://github.com/cherubstar/iOSEnvDetection/releases/download/1.3/EnvDetection.app.zip解压后用ida打开EnvDetection菜单View->Open subviews->Strings 查找Shadowrocket会找到 Shadowrocket.app,按x查找引用,跳到引用的地方

12345678910111213141516__text:0000000100005618 A2 03 00 B0 42 20 0E 91 ADRL X2, cfstr_HttpcatcherApp ; "HttpCatcher.app"__text:0000000100005620 E8 03 00 91 MOV X8, SP__text:0000000100005624 E8 57 00 F9 STR X8, [SP,#0x1E0+var_138]__text:0000000100005628 A9 03 00 B0 29 A1 0E 91 ADRL X9, cfstr_KitsunebiApp ; "Kitsunebi.app"__text:0000000100005630 09 01 00 F9 STR X9, [X8,#0x1E0+var_1E0]__text:0000000100005634 A9 03 00 B0 29 21 0F 91 ADRL X9, cfstr_PotatsoApp ; "Potatso.app"__text:000000010000563C 09 05 00 F9 STR X9, [X8,#0x1E0+var_1D8]__text:0000000100005640 A9 03 00 B0 29 A1 0F 91 ADRL X9, cfstr_QuantumultApp ; "Quantumult.app"__text:0000000100005648 09 09 00 F9 STR X9, [X8,#0x1E0+var_1D0]__text:000000010000564C A9 03 00 B0 29 21 10 91 ADRL X9, cfstr_QuantumultXApp ; "Quantumult X.app"__text:0000000100005654 09 0D 00 F9 STR X9, [X8,#0x1E0+var_1C8]__text:0000000100005658 A9 03 00 B0 29 A1 10 91 ADRL X9, cfstr_ShadowrocketAp ; "Shadowrocket.app"__text:0000000100005660 09 11 00 F9 STR X9, [X8,#0x1E0+var_1C0]__text:0000000100005664 A9 03 00 B0 29 21 11 91 ADRL X9, cfstr_SurgeIosApp ; "Surge-iOS.app"__text:000000010000566C 09 15 00 F9 STR X9, [X8,#0x1E0+var_1B8]__text:0000000100005670 A9 03 00 B0 29 A1 11 91 ADRL X9, cfstr_ThorApp ; "Thor.app"

按tab跳到代码页面,发现函数名是 -[AgentDetection checkAgentAppIsInstalled](AgentDetection *self, SEL a2),使用objection hook

12345678910111213141516171819202122com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # ios hooking watch method "-[AgentDetection checkAgentAppIsInstalled]" --dump-args --dump-backtrace --dump-return(agent) Found selector at 0x1026b8f34 as -[AgentDetection checkAgentAppIsInstalled](agent) Registering job 473143. Type: watch-method for: -[AgentDetection checkAgentAppIsInstalled]com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # (agent) [473143] Called: -[AgentDetection checkAgentAppIsInstalled] 0 arguments(Kind: instance) (Super: NSObject)(agent) [473143] -[AgentDetection checkAgentAppIsInstalled] Backtrace: 0x1026da35c EnvDetection!AgentDetectionViewModel.isInstalled() 0x10270d8ac EnvDetection!closure #1 in AgentApplicationDetectionView.body.getter 0x197e23a60 SwiftUI!List<>.init(content:) 0x10270d3c0 EnvDetection!AgentApplicationDetectionView.body.getter 0x10270ef98 EnvDetection!protocol witness for View.body.getter in conformance AgentApplicationDetectionView 0x197b35b8c SwiftUI!partial apply for closure #1 in ViewBodyAccessor.updateBody(of:changed:) 0x197e8e248 SwiftUI!closure #1 in BodyAccessor.setBody(_:) 0x197b2f328 SwiftUI!ViewBodyAccessor.updateBody(of:changed:) 0x197e8e6d4 SwiftUI!DynamicBody.updateValue() 0x197b557fc SwiftUI!partial apply for implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init(_:) 0x1ba690a50 AttributeGraph!AG::Graph::UpdateStack::update() 0x1ba690e84 AttributeGraph!AG::Graph::update_attribute(AG::data::ptr, bool) 0x1ba696588 AttributeGraph!AG::Graph::input_value_ref_slow(AG::data::ptr, AG::AttributeID, unsigned int, AGSwiftMetadata const*, bool*, long) 0x1ba6a85bc AttributeGraph!AGGraphGetValue 0x197fa11c4 SwiftUI!EnvironmentReadingChild.updateValue() 0x197b557fc SwiftUI!partial apply for implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init(_:)(agent) [473143] Return Value: 0x281954120

没啥有用的信息,在ida的伪代码找到了调用 AppInfo的listInstalledApps

12v26 = (id)objc_alloc_init(&OBJC_CLASS___AppInfo);v5 = objc_msgSend(v26, "listInstalledApps");

直接函数列表过滤查看listInstalledApps的实现,查看代码找到,对其hook

1v2 = -[AppInfo fetchApps](self, "fetchApps");

1234567891011function hook_fetchApps(){ Interceptor.attach(ObjC.classes["AppInfo"]["- fetchApps"].implementation, { onEnter: function(args){ console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n\t")) }, onLeave: function(ret){ console.log("ret value is => ",ret, "=>", ObjC.Object(ret), "=>", ObjC.Object(ret).$className); // var emptyNSArray = Objc.classes.NSArray['new'](); // ret.replace(emptyNSArray); } })}

调用栈里找到了 -[AgentDetection checkAgentAppIsInstalled],这个返回值是NSArray,返回一份不包含任何关键app的NSArray给它即可。为了通用,直接创建一个空的NSArray返回给fetchApps,Objc.classes.NSArray‘new’,去掉7,8行注释即可

常规代理检测用objection看一下AgentDetection函数

12345com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # ios hooking list class_methods AgentDetection- checkAgentAppIsInstalled- checkProxyStatusByCFNetworkCopySystemProxySettings- getCFNetworkCopySystemProxySettingsDetails- setConnectionProxyDictionary

看输出的函数名后面3个检测代理的,其中常规代理检测会调用checkProxyStatusByCFNetworkCopySystemProxySettings,直接hook

12345com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # (agent) [237829] Called: *[AgentDetection checkProxyStatusByCFNetworkCopySystemProxySettings] 0 arguments(Kind: instance) (Super: NSObject)(agent) [237829] Return Value: 0x0// 开启代理后com.zsk.EnvDetection on (iPhone: 14.4.1) [usb] # (agent) [237829] Called: *[AgentDetection checkProxyStatusByCFNetworkCopySystemProxySettings] 0 arguments(Kind: instance) (Super: NSObject)(agent) [237829] Return Value: 0x1

则直接修改其返回值为0x0即可

12345678function hook_checkSystemProxy(){ Interceptor.attach(ObjC.classes["AgentDetection"]["- checkProxyStatusByCFNetworkCopySystemProxySettings"].implementation, { onEnter: function(args){ }, onLeave: function(ret){ ret.replace(new NativePointer(0x0)) } })}

设置代理检测变量如果使用WiFi 代理抓包,会发现请求发不出去,charles也抓不到任何包如果使用vpn代理的话,以Shadowrocket为例,Shadowrocket 的作用是代理转发,vpn代理是处于网络层的,可以抓到更多的数据包,也可以过掉代理检测。一般抓包也推荐使用vpn代理

调试检测常规反调试函数

ptrace用的是ptrace来检测,一个进程同时只能被另外一个进程调试,所以这里开了ptrace后,其它调试工具xcode,lldb就没法对其进行调试没打开ptrace是可以调试上的打开后调试则报错了ptrace 是系统函数,那直接hook

1int ptrace(int _request, pid _pid, caddr_t _addr, int _data);

123456function hook_ptrace(){ var ptraceAddr = Module.findExportByName(null, 'ptrace'); Interceptor.replace(ptraceAddr, new NativeCallback(function(i1, i2, i3, i4){ console.log("ptrace flag is => ", i1); }, 'void', ['int', 'int', 'int', 'int']))}

不让ptrace函数执行就行了,直接置空不做任何操作再次打开ptrace用xcode附加调试则没有报错现象了

ptrace+svc在 iOS 反动态调试中,常用 svc #0x80 汇编指令来实现对 ptrace 和 syscall 系统调用的直接调用。通过这种方式,可以规避标准库提供的 ptrace() 函数,直接触发系统调用,增加反调试的难度,使得 lldb 无法正常附加到 app 进程。svc #0x80 指令等同于 syscall() 函数在 C 语言中的使用,都是直接触发系统调用,而不会走标准库函数的包装。当开启ptrace+svc后上面的hook函数没触发,查看它的源码也可以看到

1234567891011121314- (void)ori_ptrace { ptrace(PT_DENY_ATTACH, 0, 0, 0);}// 等价于- (void)svc_ptrace {#ifdef __arm64__ __asm__("mov X0, #31\n" "mov X1, #0\n" "mov X2, #0\n" "mov X3, #0\n" "mov w16, #26\n" "svc #0x80");#endif}

也可以用直接ida打开搜索 SVC搜索结果跟进查看

1234567891011121314151617__text:0000000100006028 ; void __cdecl -[DebugDetection svc_ptrace](DebugDetection *self, SEL)__text:0000000100006028 __DebugDetection_svc_ptrace_ ; DATA XREF: __objc_methlist:0000000100067FEC↓o__text:0000000100006028__text:0000000100006028 var_10= -0x10__text:0000000100006028 var_8= -8__text:0000000100006028__text:0000000100006028 FF 43 00 D1 SUB SP, SP, #0x10__text:000000010000602C E0 07 00 F9 STR X0, [SP,#0x10+var_8]__text:0000000100006030 E1 03 00 F9 STR X1, [SP,#0x10+var_10]__text:0000000100006034 E0 03 80 D2 MOV X0, #0x1F__text:0000000100006038 01 00 80 D2 MOV X1, #0__text:000000010000603C 02 00 80 D2 MOV X2, #0__text:0000000100006040 03 00 80 D2 MOV X3, #0__text:0000000100006044 50 03 80 52 MOV W16, #0x1A__text:0000000100006048 01 10 00 D4 SVC 0x80__text:000000010000604C FF 43 00 91 ADD SP, SP, #0x10__text:0000000100006050 C0 03 5F D6 RET

汇编指令跟源码差不多方式一可以使用patch的方式,修改 svc #0x80,在IDA View-A界面中选中svc那一行,点击工具栏Edit–>Patch program–>Change Bytes,弹出修改框,将01 10 00 D4修改为nop的byte码 1F 20 03 D5(如何知道nop的byte码,寻找一个nop,change byte就可以了)将patch修改保存到程序里去,点击工具栏Edit–>Patch program–>Apply patches to input file然后重签名打包安装就可以了方式二直接用frida对 -[DebugDetection svc_ptrace]这个函数置空不执行,这是ObjC的函数,写法跟上面的不一样

123456789function hook_svc_ptrace(){ const DebugDetection = ObjC.classes.DebugDetection; const svc_ptrace = DebugDetection['- svc_ptrace'] const oldImpl = svc_ptrace.implementation; svc_ptrace.implementation = ObjC.implement(svc_ptrace, (handle, selector) => { console.log(handle, selector); // oldImpl(handle, selector); // 旧函数运行 })}

只要不执行原函数就行了到这里其实都是要找函数位置去hook返回值或者置空达到的反调试效果,那当代码混淆以及代码膨,使得想要快速定位svc 0x80调用并将其patch掉就变得难以实现。可以查看大佬的文章:https://bbs.kanxue.com/thread-273796.htm

ptrace+xor通过ida查找到对应函数

1234567891011121314151617181920212223242526void __cdecl -[DebugDetection xor_ptrace](DebugDetection *self, SEL a2){ char v2; // w8 void *__handle; // [xsp+10h] [xbp-30h] void *v4; // [xsp+18h] [xbp-28h] _BYTE *i; // [xsp+20h] [xbp-20h] _DWORD __symbol[2]; // [xsp+28h] [xbp-18h] BYREF SEL v7; // [xsp+30h] [xbp-10h] DebugDetection *v8; // [xsp+38h] [xbp-8h] v8 = self; v7 = a2; __symbol[0] = 268633345; *(_DWORD *)((char *)__symbol + 3) = 1897140752; for ( i = __symbol; ; ++i ) { v2 = *i ^ 0x71; *i = v2; if ( !v2 ) break; } __handle = dlopen(0LL, 10); v4 = dlsym(__handle, (const char *)__symbol); ((void (__fastcall *)(__int64, _QWORD, _QWORD))v4)(31LL, 0LL, 0LL); dlclose(__handle);}

这里使用了 dlsym() 函数从动态链接库中查找 ptrace 函数的符号地址,进行调用,用上面的hook就可以了查看了它的源码

1234567891011121314151617181920- (void)xor_ptrace { //A 异或 B 等到 C,C 再异或 A 得到 B,隐藏 ptrace unsigned char str[] = { ('q' ^ 'p'), ('q' ^ 't'), ('q' ^ 'r'), ('q' ^ 'a'), ('q' ^ 'c'), ('q' ^ 'e'), ('q' ^ '\0') }; unsigned char *p = str; while (((*p) ^= 'q') != '\0') p++; int (*ptrace_ptr)(int _request, pid_t _pid, caddr_t _addr, int _data); void *handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW); // 获得句柄 ptrace_ptr = dlsym(handle, (const char *)str); // 动态查找 ptrace 符号 ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0); dlclose(handle);}

定义一个 str 数组, 拼接成ptrace字符串,最终会对 ptrace 函数进行调用

syscallida查看代码后去调用了系统函数syscall

1234void __cdecl -[DebugDetection ori_syscall](DebugDetection *self, SEL a2){ syscall(26, 31LL, 0LL, 0LL, 0LL, a2, self);}

syscall的定义如下

1long syscall(long number, ...);

123456function hook_syscall(){ var syscallAddr = Module.findExportByName(null, 'syscall'); Interceptor.replace(syscallAddr, new NativeCallback(function(i1){ console.log("syscall flag is => ", i1); }, 'void', ['int']))}

syscall+svcptrace+svc 的hook改一下就可以

syscall+xor同syscall

sysctl同样找到代码位置

123456789101112131415161718int __cdecl -[DebugDetection ori_sysctl](DebugDetection *self, SEL a2){ size_t v3[3]; // [xsp+18h] [xbp-2C8h] BYREF char v4[32]; // [xsp+30h] [xbp-2B0h] BYREF int v5; // [xsp+50h] [xbp-290h] int v6[4]; // [xsp+2B8h] [xbp-28h] BYREF v3[2] = (size_t)self; v3[1] = (size_t)a2; v6[0] = 1; v6[1] = 14; v6[2] = 1; v6[3] = getpid(); v3[0] = 648LL; v5 = 0; sysctl(v6, 4u, v4, v3, 0LL, 0LL); return (v5 & 0x800) != 0;}

1int sysctl(int *name, int namelen, void *oldval, size_t *oldlenp, void *newval, size_t newlen);

hook代码同上面的差不多sysctl+svc和sysctl+xor都跟上面的也差不多

Copyright © 2088 上届世界杯冠军_u20世界杯八强 - longxinwl.com All Rights Reserved.
友情链接