All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jadex.bytecode.vmhacks.VmHacks Maven / Gradle / Ivy

There is a newer version: 4.0.267
Show newest version
package jadex.bytecode.vmhacks;


import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.management.ManagementFactory;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.ProtectionDomain;
import java.security.Provider;
import java.security.Security;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

import jadex.commons.SAccess;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;

import jadex.bytecode.IByteCodeClassLoader;
import jadex.bytecode.SASM;
import jadex.bytecode.invocation.IMethodInvoker;
import jadex.bytecode.invocation.SInvocation;
import jadex.nativetools.NativeHelper;
import jadex.commons.SUtil;

/**
 *  Class providing various means of getting around VM restrictions.
 *
 */
public class VmHacks
{
	/** Access to unsafe operations. */
	private static volatile Unsafe UNSAFE;
	
	/** Globally disable all VM Hacks. */
	public static boolean DISABLE = false;
	
	/** Globally disable setAccessible VM Hacks. */
	public static boolean DISABLE_SETACCESSIBLE = false;
	
	/** Globally disable native functionality. */
	public static boolean DISABLE_NATIVE = false;
	
	/** Disable all instrumentation-based functionality. */
	public static boolean DISABLE_INSTRUMENTATION = true;
	
	/** Set to true to see debug infos during startup. */
	public static boolean DEBUG = false;
	
	/**
	 *  Provides access to unsafe operations.
	 *  @return The Unsafe object.
	 */
	public static final Unsafe get()
	{
		if (UNSAFE == null)
		{
			synchronized(VmHacks.class)
			{
				if (UNSAFE == null)
				{
					UNSAFE = new Unsafe();
					
					if (!DISABLE)
					{
						UNSAFE.init();
					}
					
					if (DEBUG)
						System.out.println(UNSAFE.toString());
				}
			}
		}
		return UNSAFE;
	}
	
	/**
     *  Access to unsafe operations.
     */
	public static final class Unsafe
	{
		/** Directory for temporary jar files. */
		protected static final File TEMP_JAR_DIR = new File(System.getProperty("java.io.tmpdir") + File.separator + ".jadex" + File.separator + "tmpjars");
		
		/** Set this to true to switch to fallback mode for invocation */
		private boolean asm = false;
		
		/** The native support if available. */
		private NativeHelper nativehelper = null;
		
		/** sun.misc.Unsafe if available. */
		private Class unsafeclass;
		
		/** sun.misc.Unsafe instance, if available. */
		private Object unsafeinstance = null;
		
		// Start sun.misc.Unsafe methods.
		
		/** The defineClass method. */
		private IMethodInvoker defineclass;
		
		/** The putBoolean method. */
		private IMethodInvoker getboolean;
		
		/** The putBoolean method. */
		private IMethodInvoker putboolean;
		
		/** The putBoolean method. */
		private IMethodInvoker objectFieldOffset;
		
		// End sun.misc.Unsafe methods.
		
		/** setAccessible() override field. */
		private Field setaccessibleoverride;
		
		/** setAccessible() override field offset. */
		private Long setaccessibleoverrideoffset;
		
		/** The instrumentation command queue. */
		private LinkedBlockingQueue instrumentationcommandqueue;
		
		/** The instrumentation access if available. */
//		private Instrumentation instrumentation;
		
		/** Classloader class injection map. */
		private Map>  injectionclassstore;
		
		/** Classloaders that have been enhanced with injections. */
		private Map enhancedloaders = new WeakHashMap();
		
		/** Classloader classes that have been enhanced with injections. */
		private Map, Unsafe> enhancedloaderclasses = new WeakHashMap, Unsafe>();
		
		/**
		 *  Creates the Unsafe.
		 */
		Unsafe()
		{
		}
		
		// --------- Method for checking available capabilities. -----------------
		
		/**
		 *  Tests if ASM is available.
		 *  
		 *  @return True, if ASM is available.
		 */
		public boolean hasAsm()
		{
			return asm;
		}
		
		/**
		 *  Tests if native access is available.
		 *  
		 *  @return True, if native access is available.
		 */
		public boolean hasNative()
		{
			return nativehelper != null;
		}
		
		/**
		 *  Returns functionality unlocked through native interface.
		 *  @return The native helper, may return null if unavailable.
		 */
		public NativeHelper getNativeHelper()
		{
			return nativehelper;
		}
		
		/**
		 *  Checks if instrumentation is available.
		 *  
		 *  @return True, if instrumentation is available.
		 */
		public boolean hasInstrumentation()
		{
			return instrumentationcommandqueue != null;
		}
		
		/**
		 *  Checks if redefineClassIndirect() is available.
		 *  
		 *  @return True, if indirect redefinition is available. 
		 */
		public boolean hasIndirectRedefinition()
		{
			return asm && instrumentationcommandqueue != null;
		}
		
		// --------- Methods providing functionality. -----------------
		
		/**
		 *  Attempts to change the user of the process to the given name.
		 *  If set to null, a list of default user accounts is tried.
		 *  
		 *  @param username The target user name, set to null for a list of default user account.
		 *  @return True, if successful, false if the attempt probably failed.
		 */
		public boolean tryChangeUser(String username)
		{
			boolean ret = false;
			if (hasNative())
			{
				if (username == null)
				{
					String[] defaccounts = new String[] { "jadex", "nobody", "www", "daemon" };
					for (int i = 0; i < defaccounts.length && !ret; ++i)
						ret = nativehelper.tryChangeUser(defaccounts[i]);
				}
				else
				{
					ret = nativehelper.tryChangeUser(username);
				}
			}
			
			return ret;
		}
		
		/**
	     *  Access to sun.misc.Unsafe or equivalent.
	     */
		public Class defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain pd)
	    {
			if(hasNative())
			{
				return nativehelper.defineClass(name, b, loader);
			}
			else if(defineclass != null)
			{
				return (Class)defineclass.invoke(unsafeinstance, name, b, off, len, loader, pd == null ? loader.getClass().getProtectionDomain() : pd);
			}
			else
			{
				Class ret = null;
				try
				{
					Method dc = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ProtectionDomain.class);
					SAccess.setAccessible(dc, true);
					ret = (Class) dc.invoke(loader, name, b, off, len, pd == null ? loader.getClass().getProtectionDomain() : pd);
				}
				catch (Exception e)
				{
				}
				return ret;
			}
	    }
		
		/**
		 *  Redefine class byte code. Check HAS_INSTRUMENTATION before use.
		 *  Uses indirect route via classloader enhancement, more likely to work.
		 * 
		 *  @param clazz Class to be redefined.
		 *  @param bytecode The new byte code.
		 *  @return Redefined class.
		 */
		public Class redefineClassIndirect(final Class clazz, final byte[] bytecode)
		{
			ClassLoader cl = clazz.getClassLoader();
			Class ret = clazz;
			try
			{
				enhanceClassLoader(cl);
				IByteCodeClassLoader bcl = enhancedloaders.get(cl);
//				IByteCodeClassLoader bcl = SASM.createByteCodeClassLoader(cl);
				ret = bcl.doDefineClass(bytecode);
				injectClassIntoStore(injectionclassstore, cl, clazz.getName(), ret);
				if (DEBUG)
					System.out.println("Class " + clazz + " redefined to " + ret + " indirectly via " + bcl);
			}
			catch (Exception e)
			{
				if (DEBUG)
					e.printStackTrace();
			}
			return ret;
		}
		
		/**
		 *  Redefine class byte code. Check HAS_INSTRUMENTATION before use.
		 * 
		 *  @param clazz Class to be redefined.
		 *  @param bytecode The new byte code.
		 */
		public void redefineClass(final Class clazz, final byte[] bytecode)
		{
			ClassDefinition def = new ClassDefinition(clazz, bytecode);
			runInstrumentationCommand(new InstrumentationCommand()
			{
				public void run(Instrumentation instrumentation)
				{
					try
					{
						instrumentation.redefineClasses(def);
					}
					catch (Exception e)
					{
						SUtil.throwUnchecked(e);
					}
				}
			});
		}
		
		/**
		 *  Appends a new class to the bootstrap classloader.
		 *  
		 *  @param classname The class name.
		 *  @param classcontent The bytecode.
		 */
		public void appendToBootstrapClassLoaderSearch(String classname, byte[] classcontent)
		{
			appendToBootstrapClassLoaderSearch(classname, new ByteArrayInputStream(classcontent));
		}
		
		/**
		 *  Appends a new class to the bootstrap classloader.
		 *  
		 *  @param classname The class name.
		 *  @param classcontent The bytecode.
		 */
		public void appendToBootstrapClassLoaderSearch(String classname, InputStream classcontent)
		{
			try
			{
				File file = createTempJar(classname, classcontent, null);
				JarFile jarfile = new JarFile(file);
				runInstrumentationCommand(new InstrumentationCommand()
				{
					public void run(Instrumentation instrumentation)
					{
						try
						{
							instrumentation.appendToBootstrapClassLoaderSearch(jarfile);
						}
						catch (Exception e)
						{
							SUtil.throwUnchecked(e);
						}
					}
				});
			}
			catch (Exception e)
			{
				SUtil.throwUnchecked(e);
			}
		}
		
		/**
		 *  Debug message.
		 */
		public String toString()
		{
			String ret = getClass().getName(); 
			ret += " asm=" + asm;
			ret += " native=" + hasNative();
			ret += " javaunsafe=" + unsafeinstance;
			ret += " instrumentation=" + instrumentationcommandqueue;
			return ret;
		}
		
//		public void markAsVerified(Class clazz)
//		{
//			
//			sun.misc.Unsafe u = null;
//			Object obj = null;
//			try
//			{
//				Class unsafeclass = Class.forName("sun.misc.Unsafe");
//				Field instancefield = unsafeclass.getDeclaredField("theUnsafe");
//				instancefield.setAccessible(true);
//				u = (sun.misc.Unsafe) instancefield.get(null);
//				obj = u.allocateInstance(clazz);
//			}
//			catch (Exception e)
//			{
//				e.printStackTrace();
//			}
//			long klass = (u.getInt(obj, 8L) & 0xFFFFFFFFL) << 3;
//			int miscflags = (int) (u.getByte(klass+354) & 0xFF);
//			System.out.println("initstate " + clazz.getName() + ": " + miscflags);
////			miscflags |= 1 << 2;
////			u.putChar(klass+252, (char) 16384);
////			u.putChar(klass+252, (char)miscflags);
//			System.out.println("initstate2: " + miscflags);
//		}
		
		/**
		 *  Creates an extended class loader with additional privileges if available.
		 *  
		 *  @param parents ClassLoader parents.
		 *  @return The ClassLoader.
		 */
//		public IByteCodeClassLoader getExtendedByteCodeClassLoader(ClassLoader... parents)
//		{
//			IByteCodeClassLoader ret = null;
//			if (EXTENDED_BYTECODE_CLASSLOADER != null)
//			{
//				try
//				{
//					@SuppressWarnings("unchecked")
//					Constructor c = (Constructor) EXTENDED_BYTECODE_CLASSLOADER.getConstructor(ClassLoader[].class);
//					ret = c.newInstance(new Object[] { parents });
//				}
//				catch (Exception e)
//				{
//					e.printStackTrace();
//				}
//			}
//			ret = null;
//			return ret;
//		}
		
		/**
		 *  Initialization step after constructor to allow bootstrapping.
		 */
		protected void init()
		{
			asm = true;//!SReflect.isAndroid();
			
			SecurityProviderStore.inject();
			
			try
			{
				if (!DISABLE_NATIVE)
					nativehelper = new NativeHelper();
			}
			catch (Throwable t)
			{
			}
			
			try
			{
				unsafeclass = Class.forName("sun.misc.Unsafe");
			}
			catch (Exception e)
			{
			}
			
			if (unsafeclass != null)
				unsafeinstance = getSunUnsafe(unsafeclass);
			
			try
			{
				// setAccessible override flag
				try
				{
					setaccessibleoverride = AccessibleObject.class.getDeclaredField("override");
				}
				catch (Exception e)
				{
					try
					{
						// Sometimes called flag?
						setaccessibleoverride = AccessibleObject.class.getDeclaredField("flag");
					}
					catch (Exception e1)
					{
					}
				}
			}
			catch (Exception e)
			{
				SUtil.throwUnchecked(e);
			}
			
			if (unsafeinstance != null)
			{
				defineclass = getSunUnsafeMethod("defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class);
				getboolean = getSunUnsafeMethod("getBoolean", Object.class, long.class);
				putboolean = getSunUnsafeMethod("putBoolean", Object.class, long.class, boolean.class);
				objectFieldOffset = getSunUnsafeMethod("objectFieldOffset", Field.class);
			}
			
			if (setaccessibleoverride != null && objectFieldOffset != null)
			{
				setaccessibleoverrideoffset = (Long) objectFieldOffset.invoke(unsafeinstance, setaccessibleoverride);
			}
			else if (unsafeinstance != null && setaccessibleoverrideoffset == null)
			{
				try
				{
					Method testmethod = Unsafe.class.getDeclaredMethod("init");
					
					// Scan for the override offset
					boolean[] before = new boolean[200];
					
					for (int i = 0; i < before.length; ++i)
						before[i] = (Boolean) getboolean.invoke(unsafeinstance, testmethod, (long) i);
					
					testmethod.setAccessible(true);
					
					for (int i = 0; i < before.length; ++i)
					{
						boolean current = (Boolean) getboolean.invoke(unsafeinstance, testmethod, (long) i);
						if (current && !before[i])
						{
							setaccessibleoverrideoffset = (long) i;
							break;
						}
					}
				}
				catch (Exception e)
				{
				}
			}
			
			startInstrumentationAgent();
		}
		
		/**
		 *  Creates instrumentation, if available.
		 */
		@SuppressWarnings("unchecked")
		private void startInstrumentationAgent()
		{
			if (!asm || DISABLE_INSTRUMENTATION)
				return;
			
			File jar = null;
			try
			{
				Manifest man = new Manifest();
		        Attributes attrs = man.getMainAttributes();
		        attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
		        attrs.put(new Attributes.Name("Premain-Class"), VmHacksAgent.class.getName());
		        attrs.put(new Attributes.Name("Agent-Class"), VmHacksAgent.class.getName());
		        attrs.put(new Attributes.Name("Can-Redefine-Classes"), "true");
		        attrs.put(new Attributes.Name("Can-Retransform-Classes"), "true");
		        
		        InputStream is = VmHacksAgent.class.getResourceAsStream(VmHacksAgent.class.getSimpleName() + ".class");
		        jar = createTempJar(VmHacksAgent.class.getName(), is, man);
		        SUtil.close(is);
		        
		        boolean hasagent = false;
		        if (hasNative())
		        {
			        try
					{
						Class.forName("sun.instrument.InstrumentationImpl");
						hasagent = nativehelper.startInstrumentationAgent(jar.getAbsolutePath());
						if (DEBUG && hasagent)
							System.out.println("Instrumentation agent loaded via internal API call.");
					}
					catch (Exception e1)
					{
					}
		        }
		        
		        if (!hasagent)
		        {
		        	try
		        	{
				        String javahome = System.getProperty("java.home");
						File toolsjar = new File(javahome + File.separator + "lib" + File.separator + "tools.jar");
						if (!toolsjar.exists())
						{
							toolsjar = new File(javahome + File.separator + ".." + File.separator + "lib" + File.separator + "tools.jar");
						}
						
						ClassLoader toolsloader = VmHacks.class.getClassLoader();
						if (toolsjar.exists())
						{
							toolsloader = new URLClassLoader(new URL[] { toolsjar.toURI().toURL() });
						}
				        Class vmclass = toolsloader.loadClass("com.sun.tools.attach.VirtualMachine");
				        
				        String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
				        
				        Method attach = vmclass.getDeclaredMethod("attach", String.class);
				        Object vm = attach.invoke(null, pid);
				        Method loadagent = vmclass.getDeclaredMethod("loadAgent", String.class);
				        loadagent.invoke(vm, jar.getAbsolutePath());
				        hasagent = true;
				        if (DEBUG)
							System.out.println("Instrumentation agent loaded via tools.jar.");
		        	}
		        	catch (Exception e1)
		        	{
		        	}
		        }
		        
		        if (hasagent)
		        	instrumentationcommandqueue = (LinkedBlockingQueue) SecurityProviderStore.getStore().get(0);
		        
		        if (hasInstrumentation())
		        	injectionclassstore = (Map>) SecurityProviderStore.getStore().get(1);
		        else if (DEBUG)
		        	System.out.println("Instrumentation is unavailable.");
		        
		        jar.delete();
			}
			catch (Exception e)
			{
			}
		}
		
		/**
		 *  Enhance a classloader to allow injections.
		 *  @param cl The classloader.
		 */
		private void enhanceClassLoader(ClassLoader cl)
		{
			synchronized(enhancedloaderclasses)
			{
				if (!enhancedloaders.containsKey(cl))
					enhancedloaders.put(cl, SASM.createByteCodeClassLoader(cl));
				
				if (enhancedloaderclasses.containsKey(cl.getClass()))
					return;
				
				Class clclazz = cl.getClass();
				Method m = null;
				while (m == null)
				{
					try
					{
						m = clclazz.getDeclaredMethod("loadClass", String.class, boolean.class);
					}
					catch (Exception e)
					{
						clclazz = clclazz.getSuperclass();
						if (Object.class.equals(clclazz))
							SUtil.throwUnchecked(e);
					}
				}
				
				if (enhancedloaderclasses.containsKey(clclazz))
				{
					enhancedloaderclasses.put(clclazz, Unsafe.this);
					return;
				}
				
				InputStream is = cl.getResourceAsStream(clclazz.getName().replace('.', '/') + ".class");
				ClassReader cr = null;
				
				try
				{
					cr = new ClassReader(is);
				}
				catch (Exception e)
				{
				}
				
				final Method loadclass = m; 
				
				ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
				ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, cw)
				{
					public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
					{
						
						MethodVisitor ret = cv.visitMethod(access, name, desc, signature, exceptions);
						if (loadclass.getName().equals(name) && "(Ljava/lang/String;Z)Ljava/lang/Class<*>;".equals(signature))
						{
							ret.visitCode();
							ret = new MethodVisitor(Opcodes.ASM5, ret)
							{
								public void visitCode()
								{
								};
								
								public void visitMaxs(int maxStack, int maxLocals)
								{
									mv.visitMaxs(0, 0);
								};
							};
							
							try
							{
								InsnList nl = new InsnList();
								SASM.pushImmediate(nl, SecurityProviderStore.ID);
								nl.accept(ret);
								
								Method valueof = String.class.getMethod("valueOf", int.class);
								ret.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), valueof.getName(), Type.getMethodDescriptor(valueof), false);
								
//								Method getlogger = Logger.class.getMethod("getLogger", String.class);
//								ret.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Logger.class), getlogger.getName(), Type.getMethodDescriptor(getlogger), false);
//								
//								Method getfilter = Logger.class.getMethod("getFilter");
//								ret.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Logger.class), getfilter.getName(), Type.getMethodDescriptor(getfilter), false);
								
								Method getprovider = Security.class.getMethod("getProvider", String.class);
								ret.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Security.class), getprovider.getName(), Type.getMethodDescriptor(getprovider), false);
//								
								Method values = Provider.class.getMethod("values");
								ret.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Provider.class), values.getName(), Type.getMethodDescriptor(values), false);
								
								ret.visitTypeInsn(Opcodes.CHECKCAST, Type.getDescriptor(ArrayList.class));
								
								ret.visitInsn(Opcodes.ICONST_1);
								Method arrget = ArrayList.class.getMethod("get", int.class);
								ret.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(ArrayList.class), arrget.getName(), Type.getMethodDescriptor(arrget), false);
								
								ret.visitTypeInsn(Opcodes.CHECKCAST, Type.getDescriptor(Map.class));
								
								ret.visitInsn(Opcodes.ICONST_2);
								
								ret.visitTypeInsn(Opcodes.ANEWARRAY, Type.getInternalName(Object.class));
								ret.visitInsn(Opcodes.DUP);
								ret.visitInsn(Opcodes.ICONST_0);
								ret.visitVarInsn(Opcodes.ALOAD, 0);
								ret.visitInsn(Opcodes.AASTORE);
								ret.visitInsn(Opcodes.DUP);
								ret.visitInsn(Opcodes.ICONST_1);
								ret.visitVarInsn(Opcodes.ALOAD, 1);
								ret.visitInsn(Opcodes.AASTORE);
								
								Method get = Map.class.getMethod("get", Object.class);
								ret.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(Map.class), get.getName(), Type.getMethodDescriptor(get), true);
								
								Label cont = new Label();
								ret.visitInsn(Opcodes.DUP);
								ret.visitJumpInsn(Opcodes.IFNULL, cont);
								
								ret.visitInsn(Opcodes.ARETURN);
								ret.visitLabel(cont);
							}
							catch (Exception e)
							{
							}
						}
						return ret;
					}
				};
				
				try
				{
					cr.accept(cv, 0);
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
				byte[] newcl = cw.toByteArray();
				
				redefineClass(clclazz, newcl);
				enhancedloaderclasses.put(clclazz, Unsafe.this);
			}
		}
		
		/**
		 *  Gets the unsafe instance from the class.
		 *  
		 *  @param unsafeclazz sun.misc.Unsafe if available.
		 *  @return Instance of the class.
		 */
		private Object getSunUnsafe(Class unsafeclazz)
		{
			Object ret = null;
			try
			{
				Field instancefield = null;
				try
				{
					// Field name in regular Java, normally...
					instancefield = unsafeclazz.getDeclaredField("theUnsafe");
				}
				catch (Exception e)
				{
				}
				
				if (instancefield == null)
				{
					try
					{
						// Field name in Android, normally...
						instancefield = unsafeclazz.getDeclaredField("THE_ONE");
					}
					catch (Exception e)
					{
					}
				}
				
				
				if (instancefield != null)
				{
					// attempt to acquire the singleton instance.
					try
					{
						instancefield.setAccessible(true);
						ret = instancefield.get(null);
					}
					catch (Exception e)
					{
					}
				}
				
				if (ret == null)
				{
					// Okay, last chance, just instantiate a new instance...
					Constructor c = unsafeclazz.getConstructor();
					c.setAccessible(true);
					ret = c.newInstance();
				}
			}
			catch (Exception e)
			{
			}
			
			return ret;
		}
		
		private IMethodInvoker getSunUnsafeMethod(String name, Class... params)
		{
			IMethodInvoker ret = null;
			try
			{
				Method method = null;
				method = unsafeclass.getDeclaredMethod(name, params);
				IByteCodeClassLoader bcl = SASM.createByteCodeClassLoader(method.getDeclaringClass().getClassLoader(), SASM.class.getClassLoader());
				ret = SInvocation.newInvoker(method, bcl);
			}
			catch (Exception e)
			{
			}
			return ret;
		}
		
		/** Run an instrumentation command */
		protected void runInstrumentationCommand(InstrumentationCommand command)
		{
			try
			{
				instrumentationcommandqueue.put(command);
			}
			catch (Exception e)
			{
				SUtil.throwUnchecked(e);
			}
			try
			{
				command.await(5000);
			}
			catch (TimeoutException e)
			{
				instrumentationcommandqueue = null;
				SUtil.throwUnchecked(e);
			}
		}
		
		/**
		 *  Creates an extended class loader class with additional privileges if available.
		 *  
		 *  @return The ClassLoader class.
		 */
//		private static final Class createExtendedClassLoaderClass()
//		{
//			Class ret = null;
//			
//			if (get().hasAsm() && get().hasInstrumentation())
//			{
//				try
//				{
//					// Future improvement, disable for now....
//					if (!HAS_EXTENDED_BYTECODE_CLASSLOADER)
//						return null;
//					
//					// Get out prybar into the base classloader...
//					ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
//					Class superclass = ByteCodeClassLoader.class.getClassLoader().loadClass("sun.reflect.DelegatingClassLoader");
//					System.out.println(superclass.getDeclaredConstructors().length);
//					String superclassname = superclass.getPackage().getName() + ".PublicDelegatingClassLoader";
//					String internalname = superclassname.replace('.', '/');
//					cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, internalname, null, Type.getType(superclass).getInternalName(), new String[] { });
//					MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", Type.getMethodDescriptor(Type.getType(void.class), Type.getType(ClassLoader.class)), null, null);
//					mv.visitCode();
//					mv.visitVarInsn(Opcodes.ALOAD, 0);
//					mv.visitVarInsn(Opcodes.ALOAD, 1);
//					mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(superclass), "", Type.getConstructorDescriptor(superclass.getDeclaredConstructor(ClassLoader.class)), false);
//					mv.visitInsn(Opcodes.RETURN);
//					mv.visitMaxs(0, 0);
//					mv.visitEnd();
//					cw.visitEnd();
//					byte[] code = cw.toByteArray();
//					get().appendToBootstrapClassLoaderSearch(superclassname, new ByteArrayInputStream(code));
//					superclass = ClassLoader.getSystemClassLoader().loadClass(superclassname);
//					final Constructor supercon = superclass.getDeclaredConstructor(ClassLoader.class);
//					
//					// Now make something nice based on our prybar
//					internalname = (VmHacks.class.getPackage().getName() + "." + ByteCodeClassLoader.class.getSimpleName() + "Extended").replace('.', '/');
//					InputStream is = ByteCodeClassLoader.class.getClassLoader().getResourceAsStream(ByteCodeClassLoader.class.getCanonicalName().replace('.', '/') + ".class");
//					ClassReader cr = new ClassReader(is);
//					final SimpleRemapper remapper = new SimpleRemapper(Type.getInternalName(ByteCodeClassLoader.class), internalname);
//					cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
//					ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, new ClassRemapper(cw, remapper))
//					{
//						private boolean noheader = true;
//						
//						public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
//						{
//							if (noheader)
//							{
//								cv.visit(version, access, name, signature, superName, interfaces);
//								noheader = false;
//							}
//						}
//						
//						public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
//						{
//							MethodVisitor ret = cv.visitMethod(access, name, desc, signature, exceptions);
//							if ("".equals(name))
//							{
////								MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "", Type.getMethodDescriptor(Type.getType(void.class), Type.getType(ClassLoader.class)), null, null);
////								mv.visitCode();
////								mv.visitVarInsn(Opcodes.ALOAD, 0);
////								mv.visitVarInsn(Opcodes.ALOAD, 1);
////								Class superclass = supercon.getDeclaringClass();
////								mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(superclass), "", Type.getConstructorDescriptor(supercon), false);
////								mv.visitInsn(Opcodes.RETURN);
////								mv.visitMaxs(0, 0);
////								mv.visitEnd();
//								ret = new MethodVisitor(Opcodes.ASM5, ret)
//								{
//									public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf)
//									{
//										if (opcode == Opcodes.INVOKESPECIAL)
//										{
//											mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(supercon.getDeclaringClass()), "", Type.getConstructorDescriptor(supercon), false);
//										}
//										else
//										{
//											mv.visitMethodInsn(opcode, owner, name, desc, itf);
//										}
//									};
//								};
//							}
//							ret = new MethodRemapper(ret, remapper);
//							return ret;
//						}
//					};
//					
//					cv.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, internalname, null, Type.getType(superclass).getInternalName(), new String[] { Type.getType(IByteCodeClassLoader.class).getInternalName() });
//					cr.accept(cv, 0);
//					cv.visitEnd();
//					ClassLoader basecl = IByteCodeClassLoader.class.getClassLoader();
//					basecl = basecl == null ? ClassLoader.getSystemClassLoader() : basecl;
//					ByteCodeClassLoader bcl = new ByteCodeClassLoader(basecl);
//					code = cw.toByteArray();
//					ret = bcl.doDefineClassInParent(null, code, 0, code.length, superclass.getProtectionDomain());
//					System.out.println("superrr:  " + ret.getSuperclass());
//				}
//				catch (Exception e)
//				{
//					e.printStackTrace();
//				}
//			}
//			return ret;
//		}
		
		/**
		 *  Tests if the extended classloader is available and functional.
		 *  @return True, if available.
		 */
//		private static final boolean hasExtendedClassLoader()
//		{
//			boolean ret = false;
//			if (EXTENDED_BYTECODE_CLASSLOADER != null)
//			{
//				IByteCodeClassLoader testcl = VmHacks.get().getExtendedByteCodeClassLoader(VmHacks.class.getClassLoader());
//				ret = testcl != null;
////				System.out.println("Has extended bytecode classloader!");
//			}
//			else
//			{
//				ret = false;
//			}
//			return ret;
//		}
		
		/**
		 *  Creates a temporary .jar.
		 */
		private static File createTempJar(String classname, InputStream classcontent, Manifest man)
		{
			if(!TEMP_JAR_DIR.exists())
				TEMP_JAR_DIR.mkdirs();
			
			man = man == null ? new Manifest() : man;
			JarOutputStream os = null;
	        
	        File jar = null;
	        try
	        {
	        	jar = File.createTempFile("jadextmp", ".jar");
	        	jar = new File(TEMP_JAR_DIR, SUtil.createPlainRandomId("tmpjar", 32)+".jar");
	            jar.deleteOnExit();
	            os = new JarOutputStream(new FileOutputStream(jar), man);
	            String clname = classname.replace('.', '/') + ".class";
	            JarEntry e = new JarEntry(clname);
	            os.putNextEntry(e);
	            SUtil.copyStream(classcontent, os);
	            os.closeEntry();
	        }
	        catch (Exception e)
	        {
	        }
	        finally
	        {
	            if (os != null)
	            	SUtil.close(os);
	        }
	        return jar;
		}
	}
	
	/**
	 *  Trampoline function for injection into the class redefinition store.
	 *  This allows the stack trace to come from VmHacks instead of VmHacks$Unsafe,
	 *  avoiding potential inner class naming inconsistencies.
	 *  
	 *  @param classstore The class store.
	 *  @param cl The targeted classloader.
	 *  @param classname Name of the class.
	 *  @param clazz The class.
	 */
	protected static final void injectClassIntoStore(Map> classstore, ClassLoader cl, String classname, Class clazz)
	{
		classstore.put(new Object[] { cl, clazz.getName() }, clazz);
	}
	
	/**
	 *  Instrumentation command issued to the instrumentation agent.
	 *
	 */
	protected static abstract class InstrumentationCommand
	{
		/** The semaphore. */
		protected Semaphore sem = new Semaphore(0);
		
		/** Execute the command. */
		public final void execute(Instrumentation instrumentation)
		{
			try
			{
				run(instrumentation);
			}
			catch (Exception e)
			{
			}
			sem.release();
		}
		
		/** Custom command code. */
		public abstract void run(Instrumentation instrumentation);
		
		/** Wait for command to finish. */
		public void await()
		{
			try
			{
				sem.acquire();
				sem.release();
			}
			catch (InterruptedException e)
			{
			}
		}
		
		/** Wait for command to finish. */
		public void await(long timeout) throws TimeoutException
		{
			try
			{
				boolean acquired = sem.tryAcquire(timeout, TimeUnit.MILLISECONDS);
				if (acquired)
					sem.release();
				else
					throw new TimeoutException("Instrumentation command did not finish in time.");
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy