From 8cb5517f4711e2cc583c5407349e1dc2d991ed71 Mon Sep 17 00:00:00 2001 From: HackTricks News Bot Date: Thu, 27 Nov 2025 12:47:30 +0000 Subject: [PATCH] Add content from: Bypassing iOS Frida Detection with LLDB and Frida --- .../frida-configuration-in-ios.md | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/src/mobile-pentesting/ios-pentesting/frida-configuration-in-ios.md b/src/mobile-pentesting/ios-pentesting/frida-configuration-in-ios.md index b809ccaea20..a43e59e70d9 100644 --- a/src/mobile-pentesting/ios-pentesting/frida-configuration-in-ios.md +++ b/src/mobile-pentesting/ios-pentesting/frida-configuration-in-ios.md @@ -358,6 +358,79 @@ if (ObjC.available) { } ``` +## LLDB-Assisted Frida Detection Bypass & Swift Hooking + +### Remote debugging pipeline + +Penetration tests against production-like builds often require keeping jailbreak protections enabled while still attaching Frida. A reliable workflow is to pair Apple’s `debugserver` with LLDB over USB multiplexing: + +1. Forward SSH so the jailbroken phone is reachable even without Wi-Fi: `iproxy 2222 22 &` followed by `ssh root@localhost -p 2222`. +2. On the device, spawn the debugger stub and make it wait for the target process: `debugserver *:5678 --waitfor ` and then launch the app from the SpringBoard. +3. Forward the debugging port and attach LLDB from macOS: + + ```bash + iproxy 1234 5678 & + lldb + (lldb) process connect connect://localhost:1234 + ``` + +4. Use `finish` a few times so constructors return and LLDB can resolve every Swift/ObjC image before you start patching symbols. + +Keeping `frida-server` running in parallel now becomes viable even if the app performs anti-instrumentation checks during startup. + +### Patching Swift jailbreak / Frida checks + +Swift apps frequently centralize jailbreak detection into a boolean helper such as `systemSanityCheck() -> Bool`. With LLDB already attached you can resolve the function name and force it to return `false` without touching the binary: + +```bash +(lldb) image lookup -rn 'frida' +(lldb) image lookup -rn 'Check' FridaInTheMiddle.debug.dylib +(lldb) breakpoint set --name 'FridaInTheMiddle.systemSanityCheck' +(lldb) c +(lldb) finish +(lldb) register write x0 0 +(lldb) c +``` + +On arm64 the Swift return value lives in `x0`, so zeroing that register after `finish` makes every caller believe the environment is clean, which keeps the UI alive while `frida-server` remains listening. + +### Discovering Swift targets for Frida + +Once the detection code is neutralized you can dynamically discover the mangled name of the function that handles sensitive data (e.g. the action behind a “Get Flag” button) instead of guessing: + +```bash +frida-trace -U -i "*dummy*" +``` + +Trigger the UI action and `frida-trace` will log the exact symbol such as `$s16FridaInTheMiddle11ContentViewV13dummyFunction4flagySS_tF`. That string can be fed into `Module.load(.debug.dylib).findExportByName()` inside a Frida script for precise hooking. + +### Hooking Swift `String` arguments + +Understanding the Swift ABI is essential to rebuild high-level arguments from registers when you intercept pure Swift functions: + +- **Small strings (≤15 bytes)** are stored inline and the low byte of `x0` carries the length. The characters themselves are packed in the remainder of `x0`/`x1`. +- **Large strings (>15 bytes)** are heap-backed objects. `x1` holds the pointer to the object header and the UTF‑8 buffer starts at `x1 + 32`. + +A single hook can extract both cases without reverse engineering the app’s source: + +```javascript +const mod = Module.load('FridaInTheMiddle.debug.dylib') +const fn = mod.findExportByName('$s16FridaInTheMiddle11ContentViewV13dummyFunction4flagySS_tF') +Interceptor.attach(fn, { + onEnter() { + const inlineLen = this.context.x0.and(0xff) + if (inlineLen.toInt32() > 0 && inlineLen.toInt32() <= 15) { + console.log('flag:', this.context.x0.readUtf8String(inlineLen.toInt32())) + return + } + const heapPtr = ptr(this.context.x1).add(32) + console.log('flag:', heapPtr.readUtf8String()) + } +}) +``` + +Instrumenting the function at this level means any secret `String` arguments—flags, session tokens, or dynamically generated credentials—can be dumped even when the UI never displays them. Combine this hook with the LLDB patch above to keep the app running under observation despite jailbreak or Frida detections. + ## Frida Fuzzing ### Frida Stalker @@ -1576,7 +1649,8 @@ console.log(" - changeProtection(address, size, 'rwx')") ## References - [Great Reversing Training](https://reversing.training/ ) -- [https://www.briskinfosec.com/blogs/blogsdetail/Getting-Started-with-Frida](https://www.briskinfosec.com/blogs/blogsdetail/Getting-Started-with-Frida) +- [Getting Started with Frida](https://www.briskinfosec.com/blogs/blogsdetail/Getting-Started-with-Frida) +- [Bypassing iOS Frida detection with LLDB and Frida](https://tonygo.tech/blog/2025/8ksec-ios-ctf-writeup) {{#include ../../banners/hacktricks-training.md}}