8000 Draft: fruity: Add `dump_backtraces()` for jailed iOS by mrmacete · Pull Request #1296 · frida/frida-core · GitHub
[go: up one dir, main page]

Skip to content

Draft: fruity: Add dump_backtraces() for jailed iOS - #1296

Draft
mrmacete wants to merge 1 commit into
mainfrom
dump-backtraces
Draft

mrmacete wants to merge 1 commit into
mainfrom
dump-backtraces

Conversation

@mrmacete
Copy link
Copy Markdown
Member

Capture the backtraces of all threads through the debugger, so it works even when all of Frida's threads in the target are blocked. Frames are expressed as module path plus offset so they can be symbolicated offline.

The result is a dictionary with a threads array, one entry per thread:

{
  "threads": [
    {
      "id": "0x4dee1",
      "name": "gum-js-loop",
      "registers": { "x0": "0x...", ..., "cpsr": "0x..." },
      "frames": [
        { "address": "0x1a2b3c4d0", "module": "/a/b", "offset": 46 },
        { "address": "0x110a28210" }
      ]
    }
  ]
}

Addresses, thread IDs and register values are hex strings, so the result survives JSON serialization. Frames are innermost first: the PC, the LR, then the frame-pointer chain. A frame has module and offset only if it falls inside a known module. Frames inside Gadget are attributed to the local Gadget file that was injected, or to FridaGadget.dylib if it was already loaded. The registers are the general-purpose ones reported by debugserver. If a thread can't be fully walked, it also has an error and keeps the frames collected so far.

Gadget only routes EXC_BREAKPOINT to the debugger, so debugserver's interrupt never arrives once it's loaded. When spawned with the new interruptible option, Gadget therefore starts a thread that executes a breakpoint whenever someone connects to it, passing along its own address range so its frames can be attributed. The injector finds the thread through its name. It makes its socket calls as raw syscalls and guards its state with atomics, so hooks on libc functions can't drag it into a blocked Frida thread.

The breakpoint is disarmed when Gadget detaches, so it can't fire without a debugger to handle it, and the injector re-arms it when reattaching to an existing Gadget.

Capture the backtraces of all threads through the debugger, so it works
even when all of Frida's threads in the target are blocked. Frames are
expressed as module path plus offset so they can be symbolicated
offline.

The result is a dictionary with a `threads` array, one entry per thread:

```json
{
  "threads": [
    {
      "id": "0x4dee1",
      "name": "gum-js-loop",
      "registers": { "x0": "0x...", ..., "cpsr": "0x..." },
      "frames": [
        { "address": "0x1a2b3c4d0", "module": "/a/b", "offset": 46 },
        { "address": "0x110a28210" }
      ]
    }
  ]
}
```

Addresses, thread IDs and register values are hex strings, so the result
survives JSON serialization. Frames are innermost first: the PC, the LR,
then the frame-pointer chain. A frame has `module` and `offset` only if
it falls inside a known module. Frames inside Gadget are attributed to
the local Gadget file that was injected, or to `FridaGadget.dylib` if it
was already loaded. The registers are the general-purpose ones reported
by debugserver. If a thread can't be fully walked, it also has an
`error` and keeps the frames collected so far.

Gadget only routes `EXC_BREAKPOINT` to the debugger, so debugserver's
interrupt never arrives once it's loaded. When spawned with the new
`interruptible` option, Gadget therefore starts a thread that executes
a breakpoint whenever someone connects to it, passing along its own
address range so its frames can be attributed. The injector finds the
thread through its name. It makes its socket calls as raw syscalls and
guards its state with atomics, so hooks on libc functions can't drag it
into a blocked Frida thread.

The breakpoint is disarmed when Gadget detaches, so it can't fire
without a debugger to handle it, and the injector re-arms it when
reattaching to an existing Gadget.
@mrmacete
mrmacete marked this pull request as draft November 12, 2026 12:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

0