Avoiding Injection when loading instrumented classes - java

Let's say I want to create a custom class at runtime that another class can make use of.
package redefineconcept;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import java.lang.reflect.InvocationTargetException;
public class LoadingTest {
public static final String HelloWorldTag = "$HelloWorld";
public static void main(String[] args){
new LoadingTest().run();
}
private void run(){
InstanceUser u = new InstanceUser();
u.start();
Class <?> createdClass = createAndLoadFor(InstanceUser.class);
System.out.println(String.format("created new class %s", createdClass.getName()));
InstanceUser.canAccess = true;
try {
u.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private Class<?> createAndLoadFor(Class<?> clazz){
ByteBuddy byteBuddy = new ByteBuddy();
String newClassName = clazz.getName() + LoadingTest.HelloWorldTag;
DynamicType.Builder builder = byteBuddy
.subclass(Object.class)
.name(newClassName)
;
DynamicType.Unloaded<?> newType = builder.make();
return newType
.load(clazz.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
}
}
class InstanceUser extends Thread{
public static volatile boolean canAccess = false;
Object instance;
#Override
public void run() {
while(!canAccess){}
String cn = this.getClass().getName() + LoadingTest.HelloWorldTag;
Class clazz;
try{
clazz = Class.forName(cn);
}catch(ClassNotFoundException e){
e.printStackTrace();
throw new RuntimeException();
}
try{
instance = clazz.getConstructor().newInstance();
}catch(NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e){
e.printStackTrace();
throw new RuntimeException();
}
}
}
This works.
However, the ByteBuddy tutorial, suggests
You might consider the chance of encountering circular dependencies to be of minor relevance since you are creating one dynamic type at a time. However, the dynamic creation of a type might trigger the creation of so-called auxiliary types.
These types are created by Byte Buddy automatically to provide access to the dynamic type you are creating.
because of this, we recommend you to load dynamically created classes by creating a specific ClassLoader instead of injecting them into an existing one, whenever possible.
I don't know terribly much about classloaders -- or ByteBuddy, for that matter -- but the tutorial seems to suggest that classloaders are hierachically ordered.
If so, it should be possbible to chain the new class loader to clazz.getClassLoader(), right?
Well, I've had no such luck with neither ClassLoadingStrategy.Default.WRAPPER nor ClassLoadingStrategy.Default.CHILD_FIRST.
Both result in
created new class redefineconcept.InstanceUser$HelloWorld
java.lang.ClassNotFoundException: redefineconcept.InstanceUser$HelloWorld
which led me to believe that
Normally, Java class loaders query their parent ClassLoader before attempting to directly load a type of a given name.
means they only query the parent ClassLoaders but not the children.
Is that so?
And is it at all possible to avoid using ClassLoadingStrategy.Default.INJECTION, here?

Class loaders are (normally) hierarchical. If you are using the INJECTION strategy, Byte Buddy manually defines type by type by explicitly defining the classes. Depending on the JVM and class loader, this might trigger a class loading.
Consider a situation where A references B and B references A. If Byte Buddy injects A before B, the injection of A might cause a loading of B which is not yet injected at that point. At this point, the class loader that is the target of the injection will prematurly and unsuccessfully try to look up B and fail with a NoClassDefFoundError.
When using the WRAPPER strategy, Byte Buddy creates a new class loader that is aware of both types and can look up B when A is loaded as no injection is required.
The problem you encounter is caused by your use of Class.forName(name). This method is caller sensitive meaning that the calling class's class loader is used. From your thread, this will most likely be the system class loader which is the same class loader you injected before.
That said, typically a JVM is loading types lazily and injection should not render a big problem for 99% of all use cases.

Related

ByteBuddy - Read class annotations in a java agent

I am trying to access annotations of a class before applying some transformation in a java agent implemented with ByteBuddy.
To access the annotations, I am trying to load the Class object, but it seems that this creates a duplicate class definition.
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.matcher.ElementMatchers;
import java.lang.instrument.Instrumentation;
public class SimpleTestAgent {
public static void premain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.type(ElementMatchers.isAnnotatedWith(SomeAnnotationType.class))
.transform((builder, type, classLoader, javaModule) -> {
try {
Class loadedClass = Class.forName(type.getName(), true, classLoader);
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return builder;
})
.installOn(inst);
}
}
A simple class that just creates an instance of the class TestClass that is annotated with the expected annotation, throws the following exception:
Exception in thread "main" java.lang.LinkageError: loader (instance of
sun/misc/Launcher$AppClassLoader): attempted duplicate class
definition for name: "TestClass"
public class AgentTest {
public static void main (String[] args) {
TestClass pet = new TestClass();
}
}
I am trying to implement the agent like in the example described in: Easily-Create-Java-Agents-with-ByteBuddy
Is there a way to load the Class object without causing this issue or a way to access the annotations using the arguments passed to the transformation() method?
The transformation should be able to implement new interfaces and not only override methods this is why I think I cannot use "ForAdvice".
UPDATE
The following loop finds the TestClass only after the Class.forName() is executed. This means that the class has not been loaded and so probably there is no hope to use Class.forName to get the annotations.
for (Class<?> t : inst.getAllLoadedClasses()) {
System.out.println("Class name: " + t.getName());
}
It is possible to get the annotations and the full information about a class using the net.bytebuddy.description.type.TypeDescription instance passed to the transform() method.
The problem is that, for example, I need the Method objects that can be called using reflection. It would be easier if I could somehow access the Class object for the class that is being transformed.
Byte Buddy exposes all information on annotations of a class via the TypeDescription API already, you should not load classes as a class that is loaded during a transformation loads this class before the transformation is applied and aborts class loading with the error you observe. Instead, implement your own matcher:
.type(type -> check(type.getDeclaredAnnotations().ofType(SomeAnnotation.class).load())
Byte Buddy will represent the annotation for you without loading the carrier class itself but rather represent it using its own class file processor.
You should make sure the annotation class is loaded before installing your agent builder to avoid a circularity for this exact annotation.

How to pass Spring context as a method argument?

I need some help with Spring (SpringBoot 1.3.2.RELEASE, Spring 4.3.11.RELEASE).
Use case is special and need explanations.
I've got a Spring app managing requests send by customers. In order to process these requests, we need to use services declared with #Autowired annotation. Very classic.
Recently, we decided to process new types of requests from other countries.
The point is that to face the different cases and types of requests, we decided to implement a Strategy Pattern.
-> Depending on the type of request, we execute a strategy which is selected at run time. Each strategy is contained in a concrete class and all strategies share a same Interface.
So, I had :
a main class in which requests processing was completely done. To do its job, this class used to call somes services declared with #Autowired annotation.
Now I have :
a main class in which requests processing is only initialized. To do
its job, this class will instantiate at runtime a single strategy depending on some criterias.
a IStrategy (interface) with 2 methods thats will be implemented
by my 2 concrete classes
2 concrete classes (and more to come) that will do the job. The very important point is that these concrete classes will be instantiated at runtime, AFTER the Spring context is loaded.
The problem is that after the Spring context is loaded, it's not possible anymore to use the #Autowired annotation.
All services I wanted to use in my concrete strategy classes can not be called by #Autowired any more and remain NULL.
I found a workaround by passing services I need as argument to my concrete strategy classes, but the number of services I have to pass as argument vary from one strategy to another.
I think I should pass the whole Spring context instead but I don't know how to do that. And I also don't know how I could access to all annotated services from the context.
PS : I do not show lines of code because I think there's no need actually. If you consider it would be more explicit with code, I will send some.
Thanx by advance.
Instead of declaring the services as bean, you should declare a factory to map the dependencies, it will inspect the request before returning the concrete instance of the service to the injector.
take a look here:
https://grokonez.com/spring-framework/spring-core/use-spring-factory-method-create-spring-bean
You can use below class to get Application Context and Beans statically
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Hello to all of you who answered me very quickly.
First of all, I have to apologize for answering so lately to all your comments. Last sprint was a big load and the new one is not better ^^
My need was to create on object after the Spring context has finished to create and load all parts of the app.
As part of a Strategy pattern, I have to instanciate a class at runtime depending on some values found in the request file I have to process. This class needs many services declared with the #Autowired annotation, but all the autowired objects remained 'null' because called after the context was loaded.
Here is the code I first wanted to use.
It was OK without Spring.
Function<Document, IStrategy> func = doc -> {
String strategyToApply = "";
IStrategy strategy = null;
switch(doc.getPlace()) {
case "Paris":
strategyToApply = "strategies_classes.ProcessParis";
break;
case "New York":
strategyToApply = "strategies_classes.ProcessNewYork";
break;
}
case "Roma":
...
try {
**Class<?> classToUse = Class.forName(strategyToApply);
strategy = (IStrategy) classToUse.newInstance();**
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return strategy;
};
Consumer<Document> consumerStrategy = doc -> {
IStrategy strategy = func.apply(doc);
strategy.bookRequest(doc);
};
documents.stream()
.forEach(consumerStrategy);
I finally found THE magic object. It is a good workaround when Spring object's lifecycle is not compliant with our own conception.
To use it, you just have to declare it with #Autowired :
#Autowired
private AutowireCapableBeanFactory autowireBeanFactory;
Note that the AutowireCapableBeanFactory is a Spring object you don't need to declare anywhere else !!
Then, to use it, simple (I designed a brand new service very different from what you saw above, but it's doing the same) :
public <T> T getStrategyToUse(Entity bookingCtr, Funder funder, StrategyEnum strategy) throws FunctionalException {
String strategyToApply = null;
strategyToApply = strategyDao.getClassToApply(bookingCtr, funder, strategy);
Class<?> classToUse;
try {
classToUse = Class.forName(strategyToApply);
T strat = (T) **autowireBeanFactory.getBean**(classToUse);
return (T) strat;
} catch (ClassNotFoundException e) {
LOGGER.error("The indicated Strategy class was not found", e);
}
return null;
}
When loaded at runtime, the chosen class will be instanciated without any problem and ALL of its autowired objects won't be null anymore.
I hope this will help.

Can I get an instance of a class in Java during runtime if I know its ClassLoader (ClassLoader Namespace)

I am just wondering if I can get an instance of a class (reference to an Object) during JVM runtime if I know its Classloader. Please refer to the code bellow to understand my question.
Class A:
package local.run;
public class A {
public static void main(String[] args) {
B b = new B();
b.setCount(5);
C c = new C();
c.dummyMethod();
b.printCount();
}
}
Class B:
package local.run;
public class B {
public int count = 0;
public void setCount(int aCount) {
count = aCount;
}
public void printCount() {
System.out.println(count);
}
}
Class C:
package local.run;
public class C {
public void dummyMethod() {
// Can I get the instance of class B created in class A here?
// I don't want to pass the instance of class B as a parameter to this method.
System.out.println("ClassLoader Namespace -> "+B.class.getProtectionDomain().getCodeSource().getLocation());
// I know the ClassLoader Namespace of class B
// How to get instance of class B created in class A???
System.out.println(b.count); // I wan't to print 5
}
}
If this is not prossible then I am just wondering how JSF implements #ManagedProperty feature?
No, you cannot get an instance of a give type from a ClassLoader. How would the ClassLoader be able to identify which instance you wanted?
You need some other kind of logic. Like Peter, I don't know how JSF does it exactly, so I'll give an example with Spring.
In Spring, which provides a Inversion of Control container, you declare your beans. These are managed by the container, ie. the container stores them and controls their entire life cycle. If you have a field within one of these beans' class declared as
#Value("${someBean.someField}")
private String copyValueOfThatField;
then the container is responsible for resolving the expression within the #Value annotation and injecting the corresponding value into the copyValueOfThatField field.
In this case, it will use some component that goes through all the beans registered in the container, find the one named someBean, and try to retrieve the value of its someField property.
You'll have tons of reflection going on in the background to get values of object fields.
JSF is a container in its own right and probably does something similar to the above. The use of the term Managed should tell you that.
To get an instance, you have a to have a collection somewhere which retains the instance for lookup. I don't know how JSF work specifically, but usually your framework looks for special annotations it recognises and it holds the instances which are used to set those fields, or it has factories to create the instance.

How do I LoadClass() from outside project build?

I'm trying to create an exploit, I want to load a class from outside the netbeans project, of a subclassed cash register, which should have been made final.
I can load the LegitClass fine from within the original package badclassloader, with:
claz = "badclassloader.LegitClass"
loadClass = this.getClass().getClassLoader().loadClass(claz);
(LegitClass)loadClass.newInstance();
Now I want to load the MalClass which lives in another project and package 'Package Mal'
How do I get the Mal.MalClass into the path for the LoadClass() method to find?
I tried the following:
private void loadLibrary() {
if (library != null) {
AccessController.doPrivileged(new PrivilegedAction() {
#Override
public Object run() {
System.loadLibrary("Path/to/Project/mal.jar");
return null;
}
});
}
}
but firstly I got Directory separator should not appear in library name So that clearly isn't it, I'm pretty sure I'm missing something considerable here.
Create a new, custom class loader...
URLClassLoader cl = new URLClassLoader(
new URL[]{new File("Path/to/Project/mal.jar").toURI().toURL()}
);
Load the class from the class loader...
Class clazz = cl.loadClass("Mal.MalClass");
Create a new instance of the class...
Object obj = clazz.newInstance();
Now. You have a problem. Unless the Class you've just loaded implements a interface which is common to both projects, you won't be able to resolve the instance of the Class to an actual type, as the type would be unknown at compile time. Instead, you would be forced to use reflection, which is never pretty...

Java ClassLoader: load same class twice

I have a ClassLoader which loads a class compiled by JavaCompiler from a source file.
But when I change the source file, save it and recompile it, the ClassLoader still loads the first version of the class.
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class<?> compiledClass = cl.loadClass(stringClass);
What am I missing? like a newInstance or something?
A classloader can't replace a class that already has been loaded. loadClass will return the reference of the existing Class instance.
You'll have to instantiate a new classloader and use it to load the new class. And then, if you want to "replace" the class, you'll have to throw this classloader away and create another new one.
In response to your comment(s): do something like
ClassLoader cl = new UrlClassLoader(new URL[]{pathToClassAsUrl});
Class<?> compiledClass = cl.loadClass(stringClass);
This classloader will use the "default delegation parent ClassLoader" and you have to take care, the class (identified by it fully qualified classname) has not been loaded and can't be loaded by that parent classloader. So the "pathToClassAsUrl" shouldn't be on the classpath!
You have to load a new ClassLoader each time, or you have to give the class a different name each time and access it via an interface.
e.g.
interface MyWorker {
public void work();
}
class Worker1 implement MyWorker {
public void work() { /* code */ }
}
class Worker2 implement MyWorker {
public void work() { /* different code */ }
}
As it was stated before,
Each class loader remembers (caches) the classes that is has loaded before and won't reload it again - essentially each class loader defines a namespace.
Child class loader delegates class loading to the parent class loader, i.e.
Java 8 and before
Custom Class Loader(s) -> App Class Loader -> Extension Class Loader -> Bootstrap Class Loader
Java 9+
Custom Class Loader(s) -> App Class Loader -> Platform Class Loader -> Bootstrap Class Loader.
From the above we can conclude that each Class object is identified by its fully qualified class name and the loader than defined it (also known as defined loader)
From Javadocs :
Every Class object contains a reference to the ClassLoader that
defined it.
The method defineClass converts an array of bytes into an instance of
class Class. Instances of this newly defined class can be created
using Class.newInstance.
The simple solution to reload class is to either define new (for example UrlClassLoader) or your own custom class loader.
For more complex scenario where you need to substitute class dynamic proxy mechanism can be utilized.
Please see below simple solution I used for a similar problem to reload same class by defining custom class loader.
The essence - override findClass method of the parent class loader and then load the class from bytes read from the filesystem.
MyClassLoader - overrides findClass and executed defineClass
package com.example.classloader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class MyClassLoader extends ClassLoader {
private String classFileLocation;
public MyClassLoader(String classFileLocation) {
this.classFileLocation = classFileLocation;
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classBytes = loadClassBytesFromDisk(classFileLocation);
return defineClass(name, classBytes, 0, classBytes.length);
}
private byte [] loadClassBytesFromDisk(String classFileLocation) {
try {
return Files.readAllBytes(Paths.get(classFileLocation));
}
catch (IOException e) {
throw new RuntimeException("Unable to read file from disk");
}
}
}
SimpleClass - experiment subject -
** IMPORTANT : Compile with javac and then remove SimpleClass.java from class path (or just rename it)
Otherwise it will be loaded by System Class Loader due to class loading delegation mechanism.**
from src/main/java
javac com/example/classloader/SimpleClass.java
package com.example.classloader;
public class SimpleClassRenamed implements SimpleInterface {
private static long count;
public SimpleClassRenamed() {
count++;
}
#Override
public long getCount() {
return count;
}
}
SimpleInterface - subject interface : separating interface from implementation to compile and execute output from the subject.
package com.example.classloader;
public interface SimpleInterface {
long getCount();
}
Driver - execute to test
package com.example.classloader;
import java.lang.reflect.InvocationTargetException;
public class MyClassLoaderTest {
private static final String path = "src/main/java/com/example/classloader/SimpleClass.class";
private static final String className = "com.example.classloader.SimpleClass";
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException { // Exception hell due to reflection, sorry :)
MyClassLoader classLoaderOne = new MyClassLoader(path);
Class<?> classOne = classLoaderOne.loadClass(className);
// we need to instantiate object using reflection,
// otherwise if we use `new` the Class will be loaded by the System Class Loader
SimpleInterface objectOne =
(SimpleInterface) classOne.getDeclaredConstructor().newInstance();
// trying to re-load the same class using same class loader
classOne = classLoaderOne.loadClass(className);
SimpleInterface objectOneReloaded = (SimpleInterface) classOne.getDeclaredConstructor().newInstance();
// new class loader
MyClassLoader classLoaderTwo = new MyClassLoader(path);
Class<?> classTwo = classLoaderTwo.loadClass(className);
SimpleInterface ObjectTwo = (SimpleInterface) classTwo.getDeclaredConstructor().newInstance();
System.out.println(objectOne.getCount()); // Outputs 2 - as it is the same instance
System.out.println(objectOneReloaded.getCount()); // Outputs 2 - as it is the same instance
System.out.println(ObjectTwo.getCount()); // Outputs 1 - as it is a distinct new instance
}
}
I think the problem might be more basic than what the other answers suggest. It is very possible that the class loader is loading a different file than what you think it is. To test out this theory, delete the .class file (DO NOT recompile your .java source) and run your code. You should get an exception.
If you do not get the exception, then obviously the class loader is loading a different .class file than the one you think it is. So search for the location of another .class file with the same name. Delete that .class file and try again. Keep trying until you find the .class file that is actually being loaded. Once you do that, you can recompile your code and manually put the class file in the correct directory.

Categories

Resources