![]() ![]() That Instrumentation class we're given here provides us with methods like `addTransformer` and `redefineClasses` which we can use to read and overwrite the raw bytecode of any class in the VM. You can either attach it at startup, like so: ![]() They're widely used by JVM tooling, for everything from application monitoring with New Relic to mutation testing with PiTest.ĭespite the name, they're not Java-only they work for anything that runs on the JVM. What's a Java Agent?Ī Java agent is a special type of JAR file, which can attach to other JVM processes, and is given extra powers by the JVM to transform and instrument bytecode. It's aspect-orientated programming on steroids, and it's surprisingly easy to do. This is really cool! From outside a JVM process, we can use this to reliably rewrite arbitrary bytecode to change how all HTTP in a codebase works, and take control of the entire thing ourselves. This lets us change defaults, ignore custom settings, recreate existing connections, and reconfigure all HTTP(S) to be intercepted by our HTTPS-intercepting proxy. When our agent is attached to the JVM (either at startup before everything loads, or later on) we match against specific classes used within built-in packages and a long list of popular external libraries, looking for everything from TLS configuration state to connection pool logic, and we inject a few small changes throughout. Java agents allow us to hook into a JVM process from the outside, to run our own code, and rewrite existing bytecode. Instead of setting config values at startup that nobody uses, we can capture HTTP by force, using a Java agent. This is often convenient and sensible in general, but very inconvenient later when you want to start debugging and manually testing your HTTP interactions. Even when the library doesn't, many applications define their own connection & TLS configuration explicitly. Most modern libraries ignore these settings by default, opting to provide their own defaults and configuration interfaces. Unfortunately for you, that doesn't work. In some ways, intercepting all HTTP(S) should be easy: the JVM has standard HTTP proxy and SSL context configuration settings (e.g. `-Dhttp.proxy` and ``) so you could try to configure this externally by setting those options at startup. If you want to know how on earth this is possible, and how you can write code that does the same, read on: What's Going on Here? If you just want to try this out right now, go download HTTP Toolkit. ![]() In this article, I want to walk you through the details of how this is possible, so you can understand some of the secret powers of the JVM, learn how to transform raw bytecode for yourself, and build on the examples and source code behind this to build your own debugging & instrumentation tools. This means you can pick any JVM process - your own locally running service, Gradle, Intellij, anything you like - and inspect, breakpoint, and mock all of its HTTP(S) requests in 2 seconds flat. Zero code changes or manual configuration required. It can seize control of all HTTP & HTTPS requests in any JVM, either at startup or attaching later, to redirect them to a proxy and trust that proxy to decrypt all HTTPS, allowing MitM of all JVM traffic. Over the last couple of weeks, I've built a Java agent which can do this, completely automatically. It's hard to examine all outgoing requests, simulate unusual responses & errors in a running system, or mock dependencies during manual testing & prototyping. HTTP requests and responses are the core of interactions between these services, and with their external APIs, but they're also often invisible and inaccessible. Java and the JVM more generally are widely used for services everywhere, but often challenging to debug and manually test, particularly in complicated microservice architectures. ![]()
0 Comments
Leave a Reply. |