Java Virtual Machines (JVMs) provide a number of mechanisms to inspect and modify the Java applications and the runtime they stand on. These include Java agents, JARs that are capable of modifying Java class files at runtime; and JVMTI agents, native libraries that can perform deep hooking into the innards of the JVM itself. In fact, the former are built on top of the latter, which also powers the JDWP (Java Debug Wire Protocol) server implementation that nearly all Java “debuggers” are clients of. While the Java (JAR) agents are often configured in arguments of the Java command used to start an application, both Java and JVMTI (JVM Tools Interface) agents are often connected at runtime to analyze performance issues or other facets of applications running on the JVM.
These capabilities are also useful when attempting to reverse engineer or modify running Java processes. Due to this, software developers will often introduce anti-debugging into their applications that attempt to detect the use of or disable such features. While many are trivially disabled through the use of the capabilities they try to detect, some can be troublesome when it is not feasible to modify the initial command line arguments of the process. In such situations, options that disable the underlying agent attach API can be annoying to get around as this prevents such agents from running in the first place. While these options are legitimately used as a means to rein in the insecure default model that enables any process run by the same user to inject arbitrary code into a running JVM process, attempts to use them for anti-debugging are misguided at best.
To get around such futile practices, I wrote shouganaiyo-loader, a Frida-based Java/JVMTI agent loading tool that forces JVMs to load agents. This tool enables simple injection of Java agents into JVM processes, regardless of their having disabled dynamic agent loading, making it easy to hot instrument a Java app that can’t go down, say for example… to stop active exploitation of a vulnerability in a major logging framework.
shouganaiyo-loader is a cross-platform Frida-based command-line tool that forces Java processes to load a Java/JVMTI agent regardless of whether or not the JVM has disabled the agent attach API. It currently supports the HotSpot and OpenJ9 JVMs on Linux, macosx, and Windows and uses JVM- and platform-specific techniques to re-enable runtime agent attachment.
In Sun/Oracle HotSpot, the attach API is implemented entirely in platform-specific native code; shouganaiyo-loader performs the necessary memory patching, function hooking, and code injection to load agents. An interesting facet of HotSpot discovered while writing this tool is that, on Windows, the
-XX:+DisableAttachMechanism option is unenforced. HotSpot’s Windows attachment protocol is a direct
OpenProcess call combined with
CreateRemoteThread calls that inject and run a C function into the JVM process that invokes a Windows-specific
JVM_EnqueueOperation dynamically sourced from the PE export table via
GetProcAddress. The only reason the normal clients fail to work on Windows is that this option also disables creation of the metadata files that the clients use to identify attachable JVM processes.
In IBM/Eclipse OpenJ9, nearly the entire agent API implementation is written in cross-platform Java code. While invoking this code via Frida does not require the level of native struct spoofing as used to invoke the internal HotSpot APIs, it does present its own challenges. However, it is simple enough, if tedious, to use Frida to wrap the JNI APIs that enable C/C++ code to perform reflection and invoke the necessary Java methods. This code was written before Frida released support for the JVM, which appears to be JVMTI-based, at least in part, and it seems safer to rely on just the JNI APIs to avoid any potential adverse conflicts with the Java or JVMTI agents we are attempting to load.
shouganaiyo-loader is a Node.js command-line tool. It can be used to trivially inject a Java agent JAR (or JVMTI library) into a target process as follows:
# shouganaiyo-loader -p <pid> -t <hotspot|openj9> -a /path/to/myagent.jar 'java agent arguments' # shouganaiyo-loader -p <pid> -t <hotspot|openj9> -j /path/to/myagent.so 'jvmti agent arguments'
It can be installed from NPM with
npm install -g shouganaiyo-loader.