是否可以从java。*包中检测类

时间:2015-12-01 04:56:05

标签: java bytecode instrumentation java-bytecode-asm

是否可以检测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;
}

1 个答案:

答案 0 :(得分:3)

查看Java Instrumentation API。您可以通过不同方式入侵java.*命名空间:

  1. 使用java.*前缀创建新类,将这些文件捆绑到 jar 中,并将这些类附加到bootstrap类加载器。然后可以从引导类加载器(即全局)获得这些类。
  2. 注册加载类时调用的ClassFileTransformer。对于某些引导类,这允许在加载类之前更改类的字节代码。设置代理程序不需要这些类是很重要的。对于 AWT 类,这应该可以正常工作。
  3. 重新定义java.*命名空间的类。即使在加载了某些类之后,instrumentation API也允许重新转换/重新定义它们。
  4. 我写了一个名为Byte Buddy的库,如果你想避免手动编写Java代理,那么打算更容易使用这样的工具。