📜 ⬆️ ⬇️

Instantiate not instantiated

The idea of ​​this topic was born from this comment.

So we have: a class without constructors, you must create an instance of this class. You can't do this in Java, so you have to directly manipulate the bytecode. The idea is to call NEW, but not call <init>. But there is a difficulty, the JVM specification says that it is impossible to do this,

I took ASM and started the experiments.
')

Attempt to make "in the forehead"



To begin with, we will try to implement the idea directly, generate the code of the method that should instantiate the object without a constructor:
MethodVisitor methodVisitor = classVisitor.visitMethod(ACC_PUBLIC | ACC_STATIC, FACTORY_METHOD_NAME, Type.getMethodDescriptor(Type.getObjectType(classToInstantiateInternalName)), null, null); methodVisitor.visitCode(); methodVisitor.visitTypeInsn(NEW, classToInstantiateInternalName); if (invokeObjectConstructor) { methodVisitor.visitInsn(DUP); methodVisitor.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE), false); } methodVisitor.visitInsn(ARETURN); methodVisitor.visitMaxs(1, 0); methodVisitor.visitEnd(); 

There are two generation options, generally without calling <init> or calling <init> from another class (in this case, Object). As expected, this did not work, in both cases we get VerifyError, in the first case with the message “Expecting to find object / array on stack” (the same type uninitialized ), in the second “Call to wrong initialization method”.

I have already seen it somewhere



If you read the description of how serialization works, it says that the class constructor is not called, only the default constructor of the first superclass that does not implement Serializable is called. This is exactly what we need.

If you delve into the source code ObjectStreamClass, you can find the magic in the form of sun.reflect.ReflectionFactory.newConstructorForSerialization (). Let's try to use it:
 Constructor<Object> objCons = Object.class.getConstructor(); Constructor<?> c = sun.reflect.ReflectionFactory.getReflectionFactory().newConstructorForSerialization(noConstructorClass, objCons); Object instance = c.newInstance(); logger.info("Instance: {}", instance); 

And it worked!

Import substitution



It remains to understand how ReflectionFactory does it and try to reproduce imported technology in the domestic realities. Having rummaged in source codes of OpenJDK it is possible to understand that there the special class which does all dirty work as a result is generated. A little shuffling with the OpenJDK source code we get baytkod and see the "fig", there is the same code as ours, but here it works and we don’t.
Baytkod
 public class sun.reflect.GeneratedSerializationConstructorAccessor1 extends sun.reflect.SerializationConstructorAccessorImpl {                   
   public sun.reflect.GeneratedSerializationConstructorAccessor1 ();                                                                               
     Code:                                                                                                                                        
        0: aload_0                                                                                                                                
        1: invokespecial # 36 // Method sun / reflect / SerializationConstructorAccessorImpl. "<Init>" :() V                              
        4: return                                                                                                                                 
                                                                                                                                                 
   public java.lang.Object newInstance (java.lang.Object []) throws java.lang.reflect.InvocationTargetException;                                    
     Code:                                                                                                                                        
        0: new # 6 // class com / example / test / classes / NoConstructor                                                      
        3: dup                                                                                                                                    
        4: aload_1                                                                                                                                
        5: ifnull 24                                                                                                                       
        8: aload_1                                                                                                                                
        9: arraylength                                                                                                                            
       10: sipush 0                                                                                                                        
       13: if_icmpeq 24                                                                                                                       
       16: new # 22 // class java / lang / IllegalArgumentException                                                          
       19: dup                                                                                                                                    
       20: invokespecial # 29 // Method java / lang / IllegalArgumentException. "<Init>" :() V                                            
       23: athrow                                                                                                                                 
       24: invokespecial # 12 // Method java / lang / Object. "<Init>" :() V                                                              
       27: areturn                                                                                                                                
       28: invokespecial # 42 // Method java / lang / Object.toString :() Ljava / lang / String;                                             
       31: new # 22 // class java / lang / IllegalArgumentException                                                          
       34: dup_x1                                                                                                                                 
       35: swap                                                                                                                                   
       36: invokespecial # 32 // Method java / lang / IllegalArgumentException. "<Init>" :( Ljava / lang / String;) V                          
       39: athrow                                                                                                                                 
       40: new # 24 // class java / lang / reflect / InvocationTargetException                                                 
       43: dup_x1                                                                                                                                 
       44: swap                                                                                                                                   
       45: invokespecial # 35 // Method java / lang / reflect / InvocationTargetException. "<Init>" :( Ljava / lang / Throwable;) V              
       48: athrow                                                                                                                                 
     Exception table:                                                                                                                             
        from to target type                                                                                                                   
            0 24 28 Class java / lang / ClassCastException                                                                                    
            0 24 28 Class java / lang / NullPointerException                                                                                  
           24 27 40 Class java / lang / Throwable                                                                                             
 }

There is a lot of garbage associated with exception handling and argument checking, but the really working strings are 0, 3, 24, 27

In an attempt to figure it out, open sun.reflect.SerializationConstructorAccessorImpl and see there:
Java serialization (in java.io) is a concept of the first non-Serializable superclass. This is not a valid operation according to the VM specification; one can not (for A and B, where B is a subclass of A) write “new B; invokespecial A () »without getting verification error.

In all other respects, it can be reused for this purpose. It was not necessary for the bug fix for 4486457. Future debugging easier.

Here it is, a source of strong ReflectionFactory.

Now it's up to the small, inherit from this class (by the way, bug 4486457, recommends inheriting from MagicAccessorImpl) and trying to roll the same. And everything works, and both options work with the <init> call from Object and without it at all. So it seems that type uninitialized is the notion of a bytecode validator, and not a real entity.

All code is available on Bitbucket . There is a test in the project that shows all described attempts to instantiate an object.

Source: https://habr.com/ru/post/252621/


All Articles