字节码是否可以从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 楼答案
看看Java ^{} API 。您可以通过不同的方式侵入
java.*
名称空间:java.*
前缀的新类,将这些文件捆绑到一个jar中,并将这些类附加到引导类加载程序中。这些类随后可从引导类加载器(即全局)获得李>ClassFileTransformer
,在加载类时调用它。对于某些引导类,这允许在加载类之前更改类的字节码。设置代理时不需要这些类,这一点很重要。对于AWT类,这应该可以正常工作李>java.*
命名空间的类。即使在加载某些类之后,instrumentation API也允许对它们进行重新转换/重新定义李>我编写了一个名为Byte Buddy的库,如果您不想手动编写Java代理,那么可以更容易地使用这种工具