是否可以检测java.*
包的类?
我想替换java.awt.print.PrinterJob.printDialog(PrintRequestAttributeSet)
的方法正文,使其始终返回true
。
在使用java.*
定义新课程时,我得到了SecurityException: Prohibited package name: java.awt.print
。显然它发生是因为我试图在包名中定义带java.
的类。
那么有没有办法绕过这种安全检查或以不同方式解决这个问题?
P.S我还尝试重新定义java.awt.printerjob
系统属性,以便使用我自己的PrinterJob
实现。但是我失败了,因为我的班级没找到。我没有设法找到实际的PrinterJob
的类加载器来加载我的类。
@Test public void testInstrumentationOfJavaClasses() throws Exception
{
// replace printDialog(attrs) so that it always returns true
String typeName = "java.awt.print.PrinterJob";
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
MethodReplacer methodReplacer = new MethodReplacer(cw, "printDialog", "(Ljavax/print/attribute/PrintRequestAttributeSet;)Z");
ClassReader classReader = createClassReader(typeName);
classReader.accept(methodReplacer, ClassReader.EXPAND_FRAMES);
loadClass(cw.toByteArray(), typeName);
// the actual call of PrinterJob which as I expect to always return "true"
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(PRINTABLE);
PrintRequestAttributeSet printerSettings = new HashPrintRequestAttributeSet();
if (printJob.printDialog(printerSettings))
printJob.print(printerSettings);
}
public static class MethodReplacer extends ClassVisitor implements Opcodes
{
private final String methodName;
private final String methodDefenition;
public MethodReplacer(ClassVisitor classVisitor, String methodName, String methodDescr)
{
super(ASM5, classVisitor);
this.methodName = methodName;
this.methodDefenition = methodDescr;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
{
if (methodName.equals(name) && methodDefenition.equals(desc))
{
System.out.println("visitMethod(" + name + ")");
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, name, desc, null, new String[]{"java/awt/HeadlessException"});
mv.visitCode();
mv.visitInsn(ICONST_1);
mv.visitInsn(IRETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
return mv;
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
private static Class loadClass(byte[] bytecode, String typeName) throws Exception
{
ClassLoader baseClassLoader = TestPrint.class.getClassLoader();
Class<?>[] types = new Class<?>[]{String.class, byte[].class, int.class, int.class};
Method m = ClassLoader.class.getDeclaredMethod("defineClass", types);
m.setAccessible(true);
Object[] args = new Object[]{null, bytecode, 0, bytecode.length};
Class definedClass = (Class<?>) m.invoke(baseClassLoader, args);
return definedClass;
}
答案 0 :(得分:3)
查看Java Instrumentation
API。您可以通过不同方式入侵java.*
命名空间:
java.*
前缀创建新类,将这些文件捆绑到 jar 中,并将这些类附加到bootstrap类加载器。然后可以从引导类加载器(即全局)获得这些类。ClassFileTransformer
。对于某些引导类,这允许在加载类之前更改类的字节代码。设置代理程序不需要这些类是很重要的。对于 AWT 类,这应该可以正常工作。java.*
命名空间的类。即使在加载了某些类之后,instrumentation API也允许重新转换/重新定义它们。我写了一个名为Byte Buddy的库,如果你想避免手动编写Java代理,那么打算更容易使用这样的工具。