Knowledge Center / Mobile runtime attacks / mobile runtime attacks · 2026·02
Debugger attachment and runtime introspection
On Linux-derived platforms — Android included — the kernel exposes whether a debugger is attached to a process via /proc/[pid]/status's TracerPid. On iOS, sysctl with KERN_PROC reveals the P_TRACED flag in kp_proc.p_flag. Both are reliable, well-documented, and outside the reach of most user-space cloaking. The substrate reads them and signs the result.
/proc/[pid]/status's TracerPid field reveals an attached debugger. On iOS, the P_TRACED flag in kp_proc.p_flag is the equivalent.
1. Mechanism
Debugger attachment on Linux-derived systems is mediated by
ptrace(2) [1]. A debugger calls ptrace(PTRACE_ATTACH, pid)
or, on a child it spawned, PTRACE_TRACEME. Once attached, the
debugger can read and write the target’s registers, memory, and
control its execution via signals.
The same primitive underlies:
gdbandlldb(interactive debuggers).gdbserver(remote debugger backend).frida-serverand Frida’s spawn-and-attach flow (uses ptrace for in-process injection, then drops the trace).strace-class tracing tools.
On iOS and macOS, ptrace(2) exists in a restricted form. The
canonical anti-debug primitive used by hardened apps is
ptrace(PT_DENY_ATTACH, 0, 0, 0), which sets the P_LNOATTACH
bit and causes any subsequent debugger attach to fail. On
modern iOS, the kinfo_proc struct is treated as private API
and BSD-layer process introspection is sandbox-restricted to
self-introspection; production anti-debug uses PT_DENY_ATTACH
plus a csops-style code-signing-flags check and Mach
exception-port observation through the task port subsystem,
with the P_TRACED flag recording the attached condition.
2. Where in the runtime it operates
Two platform-exposed surfaces:
- Android —
/proc/<pid>/status. The kernel exposes a per-process status file [2] with aTracerPid:line. The value is0when no debugger is attached and the PID of the tracer when one is. The file is readable by the process itself for/proc/self/status. - iOS —
PT_DENY_ATTACHandcsops. Hardened iOS apps callptrace(PT_DENY_ATTACH, 0, 0, 0)early to refuse subsequent attaches; complementarycsopschecks read code-signing flags includingCS_DEBUGGEDto confirm the process state. Direct enumeration of other processes’ trace state viasysctl(KERN_PROC, ...)is sandbox-restricted on modern iOS; thekinfo_proc.kp_proc.p_flagP_TRACEDbit is observable for self-introspection only.
Both signals are platform-truth: the kernel knows the trace state because the kernel mediates trace operations. On Android, user-space cloaking via libc hooks does not change what the kernel reports — though hooks installed at the syscall layer (requiring root or Zygisk) can rewrite the read.
3. Which checkpoints it bypasses
- Play Integrity. A device with a debugger attached to an
app on a non-debug build is unusual but not necessarily
detected by Play Integrity verdicts.
ptracefrom another app to a release-build app is blocked on stock Android byro.debuggable=0, by Yama’skernel.yama.ptrace_scope(typically set to1on shipping Android, restricting ptrace to descendant processes), and by SELinux denial ofptraceforuntrusted_app; cross-app attach therefore typically requires root, which itself failsMEETS_STRONG_INTEGRITYvia the RootOfTrust block. - App Attest. Attestation is unaffected by trace state.
The attack here is not on the checkpoints. It is on the app’s runtime privacy — the debugger reads the app’s memory, including credentials, session tokens, in-flight payload values.
4. Which signals make it observable
runtime.environment. The Trusted Runtime Primitive reads
/proc/self/status (Android) or executes the iOS self-
introspection probe (PT_DENY_ATTACH armed at startup; csops
flags read; kinfo_proc.kp_proc.p_flag for the calling process)
and signs the result. On Android, the substrate also cross-checks
TracerPid via direct syscall to bypass any libc-level rewrite.
Execution Evidence Infrastructure (EEI) — the device-identity
infrastructure layer for banking and payments — signs the pair
so the operator’s verifier can compare the libc-routed and
syscall-direct readings; divergence is itself a signal.
5. Evidence Token shape when observed
The following example is illustrative; field names, type values, and schema are defined in YEI-001 §4 (available through the spec-access process).
{
"ev": [{
"ts": "2026-06-15T10:23:14Z",
"class": "runtime.environment",
"type": "debugger.attached",
"data": {
"platform": "android",
"tracer_pid_libc": 9183,
"tracer_pid_syscall": 9183,
"consistent": true
}
}]
}
consistent: true means the libc-level read and the direct
syscall agree. A false here would indicate a hook is
rewriting the read — itself a strong signal.
6. Cross-references
- Sibling articles:
library-injection-frida-xposed,hook-detection-bypass,runtime-memory-manipulation - Architecture:
/architecture/threat-model
7. External references
[1] Linux man-pages. ptrace(2). man7.org/linux/man-pages/man2/ptrace.2.html. Cited 2026-02-04.
[2] Linux man-pages. proc(5) — /proc/[pid]/status. man7.org/linux/man-pages/man5/proc.5.html. Cited 2026-02-04.
[3] Apple Developer. ptrace(2) — PT_DENY_ATTACH and the csops code-signing flags. developer.apple.com (Mac OS X / iOS man pages: ptrace.2, csops). Cited 2026-02-04.