Browse documentation
Documentation / Protection passes / Anti-debug detection

Anti-debug detection

ConditionalDetects a JDWP debug agent attached at JVM startup, scoped to avoid false positives against production monitoring.

What it does

Every protected class's static initialiser also calls a runtime check that inspects the JVM's own reported startup arguments for the standard flags a debugger attaches through (-agentlib:jdwp, -Xrunjdwp), cached once per JVM process, and throws a SecurityException if found.

Why it's scoped to JDWP specifically

This check does not treat a general -javaagent as a signal. Production JVMs commonly run legitimate application-performance-monitoring or observability agents, and treating that as "under attack" would be a damaging false positive against your own production deployment. JDWP carries no equivalent risk: nobody ships a production JVM with remote debugging enabled by accident, so this line is drawn to keep the false-positive rate near zero, even though it detects less.

Threat model

Detects the standard way a debugger attaches to a JVM at startup. It does not resist an attacker who attaches through some other channel this check doesn't inspect, and it does not resist an attacker who reads this check's own logic, undisguised in what remains of the bytecode, or easy to find once dev.rjni.runtime classes are identified, and patches it out. It is also a known false-positive risk against a legitimate user who launches your protected jar with remote debugging enabled for their own troubleshooting: it fails with a clear error in that case.

Verified. Every class in a protected Apache Commons Exec build loads and runs on the project's own non-JDWP development JVM, with zero false positives across a load sweep of every output class.