【问题标题】:Get java.lang.IllegalAccessError when accessing the private field of a outside class via ASM Java Bytecode通过 ASM Java Bytecode 访问外部类的私有字段时获取 java.lang.IllegalAccessError
【发布时间】:2025-12-28 21:55:12
【问题描述】:

在反射中,可以通过 getDeclaredField() 和 setAccessible(true) 访问私有字段。如何通过 Objectweb ASM 字节码 API 访问外部类的私有字段? 我设置通过

之类的方式获取私有字段
Field current = sourceObject.getDeclaredField(privateFieldName);
Field.setAccessible(true);
Type sourceType = Type.getType(sourceObject.getClass());
mv.visitFieldInsn(Opcodes.GETFIELD,
                  sourceType.getInternalName(),
                  privateFieldname,
                  Type.getDescriptor(current.getType()));

执行字节码获取私有字段时,总是报错“java.lang.IllegalAccessError”

有什么线索吗?谢谢你,

【问题讨论】:

    标签: java reflection assembly bytecode java-bytecode-asm


    【解决方案1】:

    你不能那样做。 setAccessible(true) 只会影响当前程序执行中的当前字段引用(也就是说,它不会影响结果修改程序的执行)。

    要在运行修改后的程序时访问私有字段,您基本上必须将相应的反射步骤嵌入到程序中。

    要访问存储在局部变量 varId 中的某个对象的私有字段 YourClass.thePrivatefield,您可以执行类似的操作

    // Get hold of the field-reference
    mv.visitLdcInsn(Type.getType("LYourClass;"));
    mv.visitLdcInsn("thePrivateField");
    mv.visitMethodInsn(INVOKEVIRTUAL,
                       "java/lang/Class",
                       "getDeclaredField",
                       "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
    
    // Duplicate the reference
    mv.visitInsn(DUP);
    
    // Call setAccessible(true) using the first reference.
    mv.visitInsn(ICONST_1);
    mv.visitMethodInsn(INVOKEVIRTUAL,
                       "java/lang/reflect/Field",
                       "setAccessible",
                       "(Z)V");
    
    // Call get(yourObject) using the second reference to the field.
    mv.visitInsn(ALOAD, varId);
    mv.visitMethodInsn(INVOKEVIRTUAL,
                       "java/lang/reflect/Field",
                       "get",
                       "(Ljava/lang/Object;)Ljava/lang/Object;");
    

    如果您尝试使其可访问的字段是您正在重写的 cobe 基础的一部分,您显然也可以使用 ACC_PUBLIC 而不是 ACC_PRIVATE 来公开该字段。

    【讨论】:

    • 太棒了。我会试试。顺便问一下,这是 Java ASM 还是 BCEL?谢谢
    【解决方案2】:

    实际的问题是您不能合法地访问这些变量。这是因为 JVM 在 Java 有内部类之前定义了它的访问规则,所以 javac 为在 JVM 中不能合法访问但在 Java 中可以访问的字段创建合成访问器。例如,

    class Sample {
        private int i = 0;
        class Inner {
            int foo = i;
        }
    }
    

    然后我们可以使用javap来反编译生成的类。

    fowles@morbo:/tmp$ javap -private Sample
    Compiled from "Sample.java"
    class Sample extends java.lang.Object{
        private int i;
        Sample();
        static int access$000(Sample);
    }
    
    fowles@morbo:/tmp$ javap -c Sample.Inner
    Compiled from "Sample.java"
    class Sample$Inner extends java.lang.Object{
    int foo;
    
    final Sample this$0;
    
    Sample$Inner(Sample);
      Code:
       0:   aload_0
       1:   aload_1
       2:   putfield    #1; //Field this$0:LSample;
       5:   aload_0
       6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
       9:   aload_0
       10:  aload_0
       11:  getfield    #1; //Field this$0:LSample;
       14:  invokestatic    #3; //Method Sample.access$000:(LSample;)I
       17:  putfield    #4; //Field foo:I
       20:  return
    
    }
    

    注意access$000(Sample) 方法在Sample 中生成并从Sample.Inner 使用。可悲的是,您的选择是要么

    1. 使字段可访问
    2. 使用反射
    3. 生成合成访问器

    【讨论】: