有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

字节码是否可以从java插入类。*包裹

是否可以为java.*包的类插入指令

我想替换java.awt.print.PrinterJob.printDialog(PrintRequestAttributeSet)的方法体,使其始终返回true

当用java.*定义一个新类时,我得到了SecurityException: Prohibited package name: java.awt.print。显然,这是因为我试图用包名中的java.来定义类

那么,有没有办法绕过这种安全检查,或者以不同的方式解决这个问题呢

另外,我还试图重新定义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) 个答案

  1. # 1 楼答案

    看看Java ^{} API。您可以通过不同的方式侵入java.*名称空间:

    1. 创建带有java.*前缀的新类,将这些文件捆绑到一个jar中,并将这些类附加到引导类加载程序中。这些类随后可从引导类加载器(即全局)获得
    2. 注册一个ClassFileTransformer,在加载类时调用它。对于某些引导类,这允许在加载类之前更改类的字节码。设置代理时不需要这些类,这一点很重要。对于AWT类,这应该可以正常工作
    3. 重新定义java.*命名空间的类。即使在加载某些类之后,instrumentation API也允许对它们进行重新转换/重新定义

    我编写了一个名为Byte Buddy的库,如果您不想手动编写Java代理,那么可以更容易地使用这种工具