Premain
package org.example;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.net.URISyntaxException;
import java.util.jar.JarFile;
public class Main {
public static void premain(String arg, Instrumentation inst) {
System.out.println("FileOutputStream intercepted start!");
try {
inst.appendToBootstrapClassLoaderSearch(new JarFile(AgentJarLocator.getAgentJarFile()));
mode2(inst);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
System.out.println("FileOutputStream intercepted end!");
}
private static void mode2(Instrumentation instrumentation) throws IOException {
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new AgentBuilder.Listener() {
@Override
public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
// System.out.println("Discovered Type: " + typeName);
}
@Override
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) {
System.out.println("Transformed: " + typeDescription);
}
@Override
public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded) {}
@Override
public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) {
throwable.printStackTrace();
}
@Override
public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {}
})
.type(ElementMatchers.named("java.io.FileOutputStream"))
.transform((builder, typeDescription, classLoader, module,v) ->
builder.visit(Advice.to(FileOutputStreamConstructorAdvice.class)
.on(ElementMatchers.isConstructor())))
.installOn(instrumentation);
}
}
AgentJarLocator
package org.example;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.CodeSource;
import java.security.ProtectionDomain;
public class AgentJarLocator {
static File getAgentJarFile() throws URISyntaxException {
ProtectionDomain protectionDomain = Main.class.getProtectionDomain();
CodeSource codeSource = protectionDomain.getCodeSource();
if (codeSource == null) {
throw new IllegalStateException(String.format("Unable to get agent location, protection domain = %s", protectionDomain));
}
URL location = codeSource.getLocation();
if (location == null) {
throw new IllegalStateException(String.format("Unable to get agent location, code source = %s", codeSource));
}
final File agentJar = new File(location.toURI());
if (!agentJar.getName().endsWith(".jar")) {
throw new IllegalStateException("Agent is not a jar file: " + agentJar);
}
return agentJar.getAbsoluteFile();
}
}
Advice
package org.example;
import net.bytebuddy.asm.Advice;
public class FileOutputStreamConstructorAdvice {
@Advice.OnMethodEnter
public static void onEnter(@Advice.AllArguments Object[] args) {
System.out.println("FileOutputStream constructor called with arguments: " + java.util.Arrays.toString(args));
}
}
build.gradle
plugins {
id 'java'
}
archivesBaseName = 'my-agent'
group = 'org.example'
version = '1.0'
repositories {
mavenCentral()
}
dependencies {
testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'
implementation group: 'net.bytebuddy', name: 'byte-buddy', version: '1.14.9'
implementation group: 'net.bytebuddy', name: 'byte-buddy-agent', version: '1.14.9'
}
test {
useJUnitPlatform()
}
task fatJar(type: Jar) {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
manifest {
attributes (
"Class-path": configurations.compileClasspath.collect { it.getName() }.join(' '),
"Premain-Class": "org.example.Main",
"Agent-Class": "org.example.Main",
"Can-Retransform-Classes": "true",
"Can-Redefine-Classes": "true",
"Implementation-Version": rootProject.version,
)
}
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
VMOption
--add-opens java.base/java.io=ALL-UNNAMED -javaagent:"E:DevelopmentProjectsmy-agent-1.0.jar"
Getting below exception
Exception in thread "main" java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:513)
at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:525)
Caused by: java.lang.IllegalAccessError: failed to access class org.example.Main$1 from class org.example.Main (org.example.Main$1 is in unnamed module of loader 'bootstrap'; org.example.Main is in unnamed module of loader 'app')
at org.example.Main.mode2(Main.java:40)
at org.example.Main.premain(Main.java:29)
... 6 more
*** java.lang.instrument ASSERTION FAILED ***: "result" with message agent load/premain call failed at ./src/java.instrument/share/native/libinstrument/JPLISAgent.c line: 422
3